【问题标题】:Transfer of energy from one object to another in collision (in pymunk/chipmunk)碰撞中从一个物体到另一个物体的能量转移(在 pymunk/花栗鼠中)
【发布时间】:2017-08-21 17:56:52
【问题描述】:

这个问题是关于 pymunk 的,但我知道那里有更多的 Chimpunk 用户;因此,如果您的答案涉及 C/Chipmunk 代码,那没关系。虽然我不知道如何编写 C 代码,但如果我阅读别人的代码,我通常可以弄清楚发生了什么。

设置

我正在模拟一个自上而下的滑动对象游戏(想想冰壶或沙狐球)。 我已经对代码的相关部分做了一个最小的示例(在问题的末尾),但它的核心是:

  • 创建了两个相同的物理对象(按照冰壶术语,我称它们为“石头”)
  • 它们的位置具有相同的 x 值但不同的 y 值(一个在另一个上方的直线上)。
  • 使用 apply_impulse(目前,虽然尝试了其他方法),下方的石头直接“发射”到另一块石头上

我希望实现的目标

当石头碰撞时,下面的石头应该会突然停止(或者可能会反弹一点 - 我还没有为那个细节出汗),而它的全部或大部分能量都转移到上面的石头上,这将开始向上移动。

我得到的是什么

当石头碰撞时,下面的石头不会停止,而是开始向上推动上面的石头。 就好像下部比上部有更多的质量,但它们是用相同的功能创建的,所以它们应该是相同的。

我已经上传了一个 .gif 到 imgur 来说明这一点,如果它有帮助的话: http://imgur.com/a/FF6Xq

它的帧速率低于实际运行脚本时的帧速率,但它仍然说明了正在发生的事情。

我的尝试

通读 pygame 文档,为了尝试识别所有可能相关的 body 和 shape 属性,我尝试以各种组合调整以下所有内容:

  • body.mass
  • 形状.摩擦
  • 形状.弹性
  • “地面摩擦”(在自上而下的场景中模拟地面摩擦的枢轴约束的 max_force)
  • 下石发射的“力量”(传递给 apply_impulse 的参数之一)
  • 使用 apply_force 代替 apply_impulse

调整任何/所有这些都会使石头的行为发生明显和预期的变化,但没有一个可以改变石头碰撞时推动另一块石头的根本问题。

我已经阅读了有关使用 pymunk.CollisionHandler() 的信息,但还没有尝试使用它。从文档中,我感觉到这主要是为了给碰撞添加额外的效果,而不是为了修改碰撞时发生的基本物理特性。但我可能有误解,我愿意接受任何建议。

我看过几个 pymunk 演示。最值得注意的是,一个名为 newtons_cradle.py 的演示展示了我想要的行为。这是对其中一个有五个球连续悬挂的小工具的模拟;当用户将一端的一个球向后拉时,它会撞击该排的其余部分,并且能量会转移到另一侧的球上。 newtons_crade.py 与我的代码只有两个主要区别:

  • 它是“侧视图”(因此,重力大于 0)
  • 而不是使用 apply_impulse 或 apply_force,仅使用重力将球推向其他球(受约束)。

很遗憾,在我的自上而下设置中,使用重力不是一个选项。 所以问题可能是我使用了 apply_impulse/apply_force,但我看不到任何修改这些使用方式的方法(我已经尝试过各种功率和质量组合,以及调整约束的设置)。

即使为我指明了正确的方向 - 即关于我可能阅读的其他内容的一些建议,我可能会尝试修改的其他内容 - 将不胜感激。我不能成为第一个在 pymunk/chipmunk 中尝试这个的人,但我找不到一个例子。至少不在 pymunk 方面;如果我可以学习 C/Chipmunk 中的一个很好的例子,那也很有用。

感谢大家的宝贵时间。

最小示例代码

没有必要研究代码来理解问题,但我将其发布在这里以防万一。尽管被剥离以显示代码的核心,但它是一个完整的脚本并且可以运行。它在 Python 3 中。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import pygame
from pygame.locals import *
import pymunk
import pymunk.pygame_util

def add_and_tether_stone(space,sx,sy):
    """Creates a stone and its corresponding shape, and tethers it with constraints that simulate ground friction and govern spin."""
    #body
    mass = stone_mass
    radius = stone_radius
    moment = pymunk.moment_for_circle(mass, 0, radius)
    body = pymunk.Body(mass, moment)
    body.position = sx,sy
    #shape
    shape = pymunk.Circle(body, radius)
    shape.friction = stone_friction
    shape.elisticity = stone_elisticity
    space.add(body, shape)
    #constraints
    fpiv = pymunk.constraint.PivotJoint(space.static_body,body,(0.0,0.0),(0.0,0.0))
    fpiv.max_force = ground_friction
    fpiv.max_bias = 0.0
    fmot = pymunk.constraint.SimpleMotor(space.static_body,body,0)
    fmot.max_force = 5000000 #arbitry 'very high' value clamps down on the high rotation imparted by apply_impulse or apply_force
    space.add(fpiv)
    space.add(fmot)
    return body,shape,fpiv,fmot

def launch_stone(body,power):
    """Launches a stone in the manner of a player taking a shot."""
    body.apply_impulse_at_world_point((0,power),(0,0)) #force(x,y),offset(x,y)

def main():
    global ground_friction,stone_mass,stone_radius,stone_friction,stone_elisticity
    running = True
    #PyGame setup
    pygame.init()
    screen = pygame.display.set_mode((500,500))
    clock = pygame.time.Clock()
    sheet = pygame.Surface((500,500))
    sheetcolor = (0,0,0)
    sheet.fill(sheetcolor)
    sheet = sheet.convert()
    sheetblit = (0,0)
    screen.blit(sheet,sheetblit)

    #PyMunk setup
    space = pymunk.Space() #space.damping defaults to 1.0, and space.gravity defaults to (0.0, 0.0).
    draw_options = pymunk.pygame_util.DrawOptions(sheet) #used only for the pygame_util debug draw mode

    #Constants to Tweak
    stone_mass = 1.4
    stone_radius = 20
    power = 340 #in a full implementation, this would vary with player input
    ground_friction = 4.5
    stone_friction = 2.0
    stone_elisticity = 1.0

    #Setup for the minimal example: add two stones and launch one at the other.
    stone_a = add_and_tether_stone(space,40,260)
    stone_b = add_and_tether_stone(space,40,21)
    launch_stone(stone_b[0],power)

    while running:
        for event in pygame.event.get(): #listen for controls (all the controls except 'esc' have been removed for the minimal example)
            if event.type == KEYDOWN and event.key == K_ESCAPE:
                running = False
        #Draw, update physics, and advance
        sheet.fill(sheetcolor)
        space.debug_draw(draw_options) #from pymunk.pygame-util (handy!)
        screen.blit(sheet,sheetblit)
        space.step(1/50.0)
        pygame.display.flip()
        clock.tick(50)

if __name__ == '__main__':
    sys.exit(main())

再次感谢大家的宝贵时间。

【问题讨论】:

    标签: python 2d game-physics chipmunk pymunk


    【解决方案1】:

    也许我在你的代码中遗漏了一些东西(我这里只有命令行 python,所以我无法运行你的脚本),但我无法重现你的问题。

    这是我尝试过的一个简短代码,似乎可以按您的意愿工作:

    import pymunk
    
    s = pymunk.Space()
    
    b1 = pymunk.Body(1,10)
    b1.position = 0,0
    
    b2 = pymunk.Body(1,10)
    b2.position = 0,10
    
    c1 = pymunk.Circle(b1, 1)
    c1.elasticity = 1.0
    c2 = pymunk.Circle(b2, 1)
    c2.elasticity = 1.0
    
    j1 = pymunk.constraint.PivotJoint(s.static_body, b1, (0,0),(0,0))
    j1.max_force = 4.5
    j1.max_bias = 0
    
    j2 = pymunk.constraint.PivotJoint(s.static_body, b2, (0,0),(0,0))
    j2.max_force = 4.5
    j2.max_bias = 0
    
    j3 = pymunk.constraint.SimpleMotor(s.static_body,b1,0)
    j3.max_force = 5000000
    j4 = pymunk.constraint.SimpleMotor(s.static_body,b2,0)
    j4.max_force = 5000000
    
    s.add(b1,b2,c1,c2,j1,j2,j3,j4)
    
    b1.apply_impulse_at_world_point((0,30),(0,0))
    
    for x in range(25):
        s.step(0.02)
        print(b1.position, b2.position)
    

    这会在我的屏幕上打印出来(所以 b1 停止,所有运动都转移到 b2):

    Vec2d(0.0, 0.6) Vec2d(0.0, 10.0)
    Vec2d(0.0, 1.1982) Vec2d(0.0, 10.0)
    Vec2d(0.0, 1.7946) Vec2d(0.0, 10.0)
    Vec2d(0.0, 2.3891999999999998) Vec2d(0.0, 10.0)
    Vec2d(0.0, 2.9819999999999998) Vec2d(0.0, 10.0)
    Vec2d(0.0, 3.573) Vec2d(0.0, 10.0)
    Vec2d(0.0, 4.1622) Vec2d(0.0, 10.0)
    Vec2d(0.0, 4.7496) Vec2d(0.0, 10.0)
    Vec2d(0.0, 5.3352) Vec2d(0.0, 10.0)
    Vec2d(0.0, 5.9190000000000005) Vec2d(0.0, 10.0)
    Vec2d(0.0, 6.501) Vec2d(0.0, 10.0)
    Vec2d(0.0, 7.081200000000001) Vec2d(0.0, 10.0)
    Vec2d(0.0, 7.659600000000001) Vec2d(0.0, 10.0)
    Vec2d(0.0, 8.2362) Vec2d(0.0, 10.0)
    Vec2d(0.0, 8.228112001309862) Vec2d(0.0, 10.584682725252637)
    Vec2d(0.0, 8.228112001309862) Vec2d(0.0, 11.159477451815137)
    Vec2d(0.0, 8.228112001309862) Vec2d(0.0, 11.732472178377638)
    Vec2d(0.0, 8.228112001309862) Vec2d(0.0, 12.303666904940137)
    Vec2d(0.0, 8.228112001309862) Vec2d(0.0, 12.873061631502637)
    Vec2d(0.0, 8.228112001309862) Vec2d(0.0, 13.440656358065137)
    

    【讨论】:

    • 谢谢!通过比较您的代码和我的代码,我能够找到问题所在。将 shape.elasticity 设置为 1.0(从默认值 0 开始)是关键的区别——而且,令我尴尬的是,“elasticity”(作为“elisticity”)这个词的错字意味着虽然我认为我在设置弹性,它实际上从未被修改过。呃-通过将我的代码发布在 S.O!好吧,谢谢你的帮助,很抱歉这对 SO 社区来说是一个无聊的问题。
    猜你喜欢
    • 1970-01-01
    • 2012-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多