Pymunk: предотвращение перемещения динамических объектов в статические.

У меня есть динамический объект, для которого я установил разные значения скорости. Однако когда это динамическое тело ударяется о статическое тело, оно частично перекрывается со статической формой, пока это столкновение не разрешится и оно не отодвинется.

Есть ли у пимунка способ заставить динамическое тело останавливаться точно на границах статического тела, даже когда скорость применяется в этом направлении? Если возникают конфликты столкновений, я бы предпочел их разрешить другим способом, чем перекрывать две формы.

Применение сил и импульсов на самом деле не вариант, так как я хочу иметь постоянную скорость.

(Для работы приведенный ниже код необходимо выполнить дважды.)

import pymunk
import pyglet
from PIL import Image
from PIL import ImageDraw

# setup of pyglet

window = pyglet.window.Window()
main_batch = pyglet.graphics.Batch()
keys = pyglet.window.key.KeyStateHandler()
window.push_handlers(keys)

# setup of pymunk

space = pymunk.Space()

"""MOVABLE CIRCLE"""

# creating pyglet sprite

circle_img = Image.new('RGBA', (50,50))
draw = ImageDraw.Draw(circle_img)
draw.ellipse((1, 1, 50-1, 50-1), fill=(255,0,0))
circle_img.save('circle.png')
pyglet_circle_img = pyglet.resource.image('circle.png')
pyglet_circle_img.anchor_x = pyglet_circle_img.width/2
pyglet_circle_img.anchor_y = pyglet_circle_img.height/2
circle_sprite = pyglet.sprite.Sprite(pyglet_circle_img, window.width/2, window.height/2, batch=main_batch)

# creating pymunk body and shape

mass = 2
radius = 25
moment = pymunk.moment_for_circle(mass, 0, radius)
circle_body = pymunk.Body(mass, moment)
circle_body.position = circle_sprite.position
circle_shape = pymunk.Circle(circle_body, 25)
circle_shape.elasticity = 0.0
space.add(circle_body, circle_shape)

"""STATIC SQUARE"""

# creating pyglet sprite

square_img = Image.new('RGBA', (70,70))
draw = ImageDraw.Draw(square_img)
draw.rectangle([(0, 0), (70-1, 70-1)], fill=(0,255,0))
square_img.save('square.png')
pyglet_square_img = pyglet.resource.image('square.png')
pyglet_square_img.anchor_x = pyglet_square_img.width/2
pyglet_square_img.anchor_y = pyglet_square_img.height/2
square_sprite = pyglet.sprite.Sprite(pyglet_square_img, 3*window.width/4, window.height/2, batch=main_batch)

# creating pymunk body and shape

square_body = pymunk.Body(body_type=pymunk.Body.KINEMATIC)
square_body.position = square_sprite.position
square_shape = pymunk.Poly(square_body, [(-35,-35),(-35,35),(35,35),(35,-35)])
square_shape.elasticity = 0.0
space.add(square_body, square_shape)

def update(dt):
    space.step(dt)
    circle_sprite.position = circle_body.position
    print(circle_body.position)
    key_pressed = False
    if keys[pyglet.window.key.LEFT]:
        circle_body.velocity = (-100,0)
        key_pressed = True
    elif keys[pyglet.window.key.RIGHT]:
        circle_body.velocity = (100, 0)
        key_pressed = True
    if keys[pyglet.window.key.UP]:
        circle_body.velocity = (0, 100)
        key_pressed = True
    elif keys[pyglet.window.key.DOWN]:
        circle_body.velocity = (0, -100)
        key_pressed = True
    if not key_pressed:
        circle_body.velocity = (0,0)

@window.event
def on_draw():
    window.clear()
    main_batch.draw()

pyglet.clock.schedule_interval(update, 1/60.)

pyglet.app.run()

person Nico Autia    schedule 02.11.2017    source источник


Ответы (1)


В общем, ответ заключается в том, что вы должны ожидать странных эффектов, когда вы вручную устанавливаете положение или скорость тела, поскольку это обманывает физический движок (точно так же, как вы получите странные эффекты в реальной жизни, если что-то телепортируется вокруг)

Это также тот случай, когда следует ожидать небольшого перекрытия при столкновении объектов, именно так решается столкновение. В пространстве есть два свойства, которые вы можете использовать, чтобы немного управлять этим, collision_slop и collision_bias.

Однако вы можете поэкспериментировать с некоторыми ручными исправлениями, которые могут помочь. Один из способов - отодвинуть объекты в случае столкновения. Вы можете сделать это с помощью обратного вызова столкновения.

Вот краткий пример, который вы можете поместить непосредственно перед функцией обновления в своем примере, чтобы предотвратить перекрытие:

circle_shape.collision_type = 1
h = space.add_wildcard_collision_handler(circle_shape.collision_type)
def f(arbiter, space, data):
    ps = arbiter.contact_point_set
    arbiter.shapes[0].body.position += ps.normal * ps.points[0].distance
h.post_solve = f

(в вашем реальном коде вам нужно добавить несколько аварийных сейфов, а также учесть более одной точки на случай, если у вас более сложные формы)

person viblo    schedule 03.11.2017