Передача энергии от одного объекта другому при столкновении (в пимунке / бурундуке)
Этот вопрос о пимунке, но я знаю, что там намного больше пользователей Chimpunk; так что если ваш ответ связан с кодом C/Chipmunk, то все в порядке. Хотя я не знаю, как писать код на C, обычно я могу понять, что происходит, если читаю чужой.
Настройка
Я моделирую игру со скользящим объектом сверху вниз (думаю, керлинг или шаффлборд). Я сделал минимальный пример соответствующей части кода (в конце вопроса), но суть этого:
- Создаются два идентичных физических объекта (в соответствии с терминами керлинга я называю их "камнями")
- их местоположения имеют одинаковое значение x, но разные значения y (одно находится над прямой линией над другим).
- используя apply_impulse (пока, хотя были опробованы другие методы), нижний камень "запускается" прямо на другом камне
Чего я надеюсь достичь
Когда камни сталкиваются, нижний камень должен внезапно остановиться (или, возможно, немного отскочить назад - я еще не потею от этой детали), в то время как вся или большая часть его энергии передается верхнему камню, что начать движение вверх по экрану.
Что я получаю вместо
Когда камни сталкиваются, нижний камень не останавливается и начинает толкать верхний камень вверх по экрану. Как будто у нижнего было больше массы, чем у верхнего, но они созданы с той же функцией, поэтому они должны быть одинаковыми.
Я загрузил.gif в imgur, который иллюстрирует это, если это поможет: https://imgur.com/a/FF6Xq
Это более низкая частота кадров, чем при фактическом запуске сценария, но он все еще иллюстрирует происходящее.
Что я пробовал
Читая документацию по pygame, чтобы попытаться определить все свойства тела и формы, которые могут иметь отношение к делу, я попытался настроить все перечисленное ниже в различных сочетаниях:
- масса тела
- shape.friction
- shape.elasticity
- "трение о землю" (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, но я не смог найти пример. По крайней мере, не на стороне пимуна; если бы в 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())
Еще раз всем спасибо за ваше время.
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)