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