Как решить проблему столкновения объектов в Python Arcade?Python

Программы на Python
Ответить Пред. темаСлед. тема
Anonymous
 Как решить проблему столкновения объектов в Python Arcade?

Сообщение Anonymous »

Я заметил, что прямоугольные объекты начинают вращаться и сдвигаться после столкновения со стенами (статическими объектами) в Arcade. В зависимости от соотношения высоты и ширины скорость поворота различна. Но оно всегда присутствует. Предполагается, что прямоугольный объект не вращается и движется прямолинейно. После чего прямоугольный объект сталкивается с плоской стеной.
До столкновения;
После столкновения
На картинках выше правый объект работает без настройки, поэтому ведет себя странно. Объект слева от отведения не вращается после столкновения со стеной, поскольку у него есть специальная скорость из hit_handler.
Объект слева, item_1, ведет себя правильно, но требует большого количества дополнительных вычислений. Объект справа, item_2, ведет себя как любой другой прямоугольник в Python Arcade или Pymunk.
Есть ли более простой или менее затратный в вычислительном отношении способ? Есть ли какие-нибудь встроенные функции в Arcade или Pymunk, которые могут помочь?
Возможно, это ошибка в библиотеке Chipmunk2d и простого способа нет. Я очень долго боролся с этой проблемой. Заранее благодарю за любую помощь.
Вот мой код (чтобы начать движение, введите любую клавишу):
from typing import Optional
import arcade
from pyglet.math import Vec2
import numpy as np

# Set how many rows and columns we will have
ROW_COUNT = 10
COLUMN_COUNT = 10

# This sets the WIDTH and HEIGHT of each grid location
WIDTH = 32
HEIGHT = 32

# This sets the margin between each cell
# and on the edges of the screen.
MARGIN = 1

# Do the math to figure out our screen dimensions
SCREEN_WIDTH = ((WIDTH + MARGIN) * COLUMN_COUNT + MARGIN)*2
SCREEN_HEIGHT = ((HEIGHT + MARGIN) * ROW_COUNT + MARGIN)*2
SCREEN_TITLE = "Game example"

DYNAMIC_TYPE = "item"
STATIC_TYPE = "wall"

def cpv(x,y):
return (x,y)
def cpvrotate(t1,t2):
x = t1[0]*t2[0] - t1[1]*t2[1]
y = t1[0]*t2[1] + t1[1]*t2[0]
return cpv(x,y)

def cpvadd(t1,t2):
x = t1[0]+t2[0]
y = t1[1]+t2[1]
return cpv(x,y)

def cpvsub(t1,t2):
x = t1[0]-t2[0]
y = t1[1]-t2[1]
return cpv(x,y)

def cpvdot(t1,t2):
x = t1[0]*t2[0]
y = t1[1]*t2[1]
return x+y

def cpvcross(t1,t2):
x = t1[0]*t2[1]
y = t1[1]*t2[0]
return x-y

def cpvmult(t1,number):
return cpv(t1[0]*number,t1[1]*number)

def cpvperp(t1):
return cpv(-t1[1],t1[0])

def cpvneg(t1):
return cpv(-t1[0],-t1[1])

def relative_velocity(a, b, r1, r2):
v1_sum = cpvadd(a.velocity, cpvmult(cpvperp(r1), a.angular_velocity))
v2_sum = cpvadd(b.velocity, cpvmult(cpvperp(r2), b.angular_velocity))
return cpvsub(v2_sum, v1_sum)

def normal_relative_velocity(a, b, r1, r2, n):
return cpvdot(relative_velocity(a, b, r1, r2), n)

def get_con_bounce(a, b, r1, r2, n, arb):
return normal_relative_velocity(a, b, r1, r2, n)*arb.restitution

def get_vr(a, b, r1, r2, n, surface_vr):
vr = cpvadd(relative_velocity(a, b, r1, r2), surface_vr)
return vr

def apply_impulse(body, j, r):
i_inv = 1.0/body.moment
m_inv = 1.0/body.mass
velocity = cpvmult(j, m_inv)
angular_velocity = i_inv*cpvcross(r, j)
return velocity, angular_velocity

def k_scalar_body(body, r, n):
rcn = cpvcross(r, n)
i_inv = 1.0/body.moment
m_inv = 1.0/body.mass
return m_inv + i_inv*rcn*rcn

def k_scalar(a, b, r1, r2, n):
return k_scalar_body(a, r1, n) + k_scalar_body(b, r2, n)

def cpfclamp(f,min_,max_):
return min(max(f, min_), max_)

def arbApplyImpulse(r1, r2, n, arb, _space, _data, jnAcc=0.0, jtAcc=0.0):
a = arb.shapes[0].body
b = arb.shapes[1].body
nMass = 1.0/k_scalar(a, b, r1, r2, n)
tMass = 1.0/k_scalar(a, b, r1, r2, cpvperp(n))

surface_vr = arb.surface_velocity
bounce = get_con_bounce(a, b, r1, r2, n, arb)

vr = get_vr(a, b, r1, r2, n, surface_vr)

vrn = cpvdot(vr, n)
vrt = cpvdot(vr, cpvperp(n))

jn = -(bounce + vrn)*nMass
jnOld = jnAcc
jnAcc = max(jnOld+jn, 0)

jtMax = arb.friction*jnAcc
jt = -vrt*tMass
jtOld = jtAcc
jtAcc = cpfclamp(jtOld + jt, -jtMax, jtMax)

_j = cpvrotate(n, cpv(jnAcc - jnOld, jtAcc - jtOld))

v,w = apply_impulse(a, cpvneg(_j), r1)

return v, w, bounce

def ArbSetContactPointSet(arb, set_, number, swapped):
p1 = set_[number].point_a
p2 = set_[number].point_b
if swapped:
r1 = cpvsub(p2, arb.shapes[0].body.position)
r2 = cpvsub(p1, arb.shapes[1].body.position)
else:
r1 = cpvsub(p1, arb.shapes[0].body.position)
r2 = cpvsub(p2, arb.shapes[1].body.position)
return r1,r2

def arbApllyImpulses(_arbiter, _space, _data):
swapped = False
v_total = (0,0)
w_total = 0
bounce_total = []
for i in range(len(_arbiter.contact_point_set.points)):
r1,r2 = ArbSetContactPointSet(_arbiter, _arbiter.contact_point_set.points,
i, swapped)
#print("r1 r2 ", r1,r2)
v, w, bounce = arbApplyImpulse(r1, r2, _arbiter.normal, _arbiter,_space, _data)
bounce_total.append(bounce)
v_total = cpvadd(v_total,v)
w_total+=w
_data["a_w"] += w_total

#CUSTOM VELOCITY
_data["a_v"] = list(abs(np.average(bounce_total))*(v_total / np.linalg.norm(v_total)))

class Custom_sprite(arcade.SpriteSolidColor):
def __init__(self, *argv,**kargv):
super(Custom_sprite, self).__init__(*argv,**kargv)

def pymunk_moved(self, physics_engine, dx, dy, d_angle):
""" Handle when the sprite is moved by the physics engine. """
physics_object = physics_engine.sprites[self]
pass

class MyGame(arcade.Window):
def __init__(self, width, height, title):
super().__init__(width, height, title)

# Set the background color of the window
self.background_color = arcade.color.BLACK

self.wall_list = arcade.SpriteList()
self.item_list = arcade.SpriteList()

self.create_map()

self.item_1 = self.create_item(1,1)
self.item_2 = self.create_item(1,3)
self.item_list.append(self.item_1)
self.item_list.append(self.item_2)

self.physics_engine = Optional[arcade.PymunkPhysicsEngine]
damping = 0.9
gravity = (0,0)
self.physics_engine = arcade.PymunkPhysicsEngine(damping=damping,
gravity=gravity)
self.physics_engine.space.collision_slop = 2

self.physics_engine.add_sprite_list(self.wall_list,
friction=0.2,
collision_type="wall",
elasticity = 0.8,
body_type=arcade.PymunkPhysicsEngine.STATIC)

self.physics_engine.add_sprite(self.item_1, friction=0.2,collision_type="item", elasticity = 0.4)
self.physics_engine.add_sprite(self.item_2, friction=0.2, elasticity = 0.4)

def hit_handler_begin(dynamic_sprite, static_sprite, _arbiter, _space, _data):
print()
print("START COLLISION")
print(_arbiter.contact_point_set)
print("ARB angular velocity",_arbiter.shapes[0].body.angular_velocity)
print("ARB angle",_arbiter.shapes[0].body.angle)
print("ARB velocity", _arbiter.shapes[0].body.velocity)
return True

def hit_handler_pre(dynamic_sprite, static_sprite, _arbiter, _space, _data):
if _arbiter.is_first_contact:
print()
print("PRE HIT")
print(_arbiter.contact_point_set)
_data["a_v"] = _arbiter.shapes[0].body.velocity
_data["a_w"] = _arbiter.shapes[0].body.angular_velocity
arbApllyImpulses(_arbiter, _space, _data)
print(_data)
print("ARB angular velocity",_arbiter.shapes[0].body.angular_velocity)
print("ARB angle",_arbiter.shapes[0].body.angle)
print("ARB velocity", _arbiter.shapes[0].body.velocity)
return True

def hit_handler_post(dynamic_sprite, static_sprite, _arbiter, _space, _data):
if _arbiter.is_first_contact:
print()
print("HIT First")
print(_arbiter.contact_point_set)
print("ARB angular velocity",_arbiter.shapes[0].body.angular_velocity)
print("ARB angle",_arbiter.shapes[0].body.angle)
print("ARB velocity", _arbiter.shapes[0].body.velocity)

_arbiter.shapes[0].body.velocity = _data["a_v"]
_arbiter.shapes[0].body.angular_velocity = _data["a_w"]

print("NEW ARB angular velocity",_arbiter.shapes[0].body.angular_velocity)
print("NEW ARB velocity",_arbiter.shapes[0].body.velocity)

def hit_handler_separate(dynamic_sprite, static_sprite, _arbiter, _space, _data):
print()
print("SEP")
print("ARB angular velocity",_arbiter.shapes[0].body.angular_velocity)
print("ARB angle",_arbiter.shapes[0].body.angle)
print("ARB velocity", _arbiter.shapes[0].body.velocity)

self.physics_engine.add_collision_handler(DYNAMIC_TYPE, STATIC_TYPE, begin_handler = hit_handler_begin)
self.physics_engine.add_collision_handler(DYNAMIC_TYPE, STATIC_TYPE, pre_handler = hit_handler_pre)
self.physics_engine.add_collision_handler(DYNAMIC_TYPE, STATIC_TYPE, post_handler = hit_handler_post)
self.physics_engine.add_collision_handler(DYNAMIC_TYPE, STATIC_TYPE, separate_handler = hit_handler_separate)

# Create the cameras. One for the GUI, one for the sprites.

# We scroll the 'sprite world' but not the GUI.

self.camera_sprites = arcade.Camera(SCREEN_WIDTH, SCREEN_HEIGHT)

self.camera_gui = arcade.Camera(SCREEN_WIDTH, SCREEN_HEIGHT)

def scroll_to_item(self, x, y):

position = Vec2(x - self.width / 2, y - self.height / 2)

self.camera_sprites.move_to(position, 0.5)

def create_wall_item(self, row, column, width, height):
x = column * (WIDTH + MARGIN) + (WIDTH / 2 + MARGIN)
y = row * (HEIGHT + MARGIN) + (HEIGHT / 2 + MARGIN)
sprite = arcade.SpriteSolidColor(width, height, arcade.color.GRAY)
sprite.center_x = x
sprite.center_y = y
self.wall_list.append(sprite)

def create_map(self):
# Create a list of solid-color sprites to represent each grid location
for row in range(-1,ROW_COUNT+1):
for column in range(-1,COLUMN_COUNT+1):
if row==-1 or column==-1 or row==ROW_COUNT or column==COLUMN_COUNT:
self.create_wall_item(row,column, WIDTH, HEIGHT)

def get_sprite_coords_by_cell(self, row, column):
x = column * (WIDTH + MARGIN) + (WIDTH / 2 + MARGIN)
y = row * (HEIGHT + MARGIN) + (HEIGHT / 2 + MARGIN)
return x, y

def create_item(self, row = 1, column = 1):
x, y = self.get_sprite_coords_by_cell(row, column)
item_ = Custom_sprite(WIDTH//4, HEIGHT, arcade.color.BLUE)
item_.center_x = x
item_.center_y = y
item_.angle = 0
return item_

def on_draw(self):
"""
Render the screen.
"""
# We should always start by clearing the window pixels
self.clear()

# Select the camera we'll use to draw all our sprites

self.camera_sprites.use()

self.item_list.draw()
self.item_1.draw_hit_box(color = arcade.color.GREEN)
self.item_2.draw_hit_box(color = arcade.color.GREEN)
self.wall_list.draw()

# Select the (unscrolled) camera for our GUI

self.camera_gui.use()

def apply_force(self, physics_engine, sprite, force):
""" Apply force to a Sprite. """
physics_object = physics_engine.sprites[sprite]
physics_object.body.apply_force_at_local_point(force, (0, 0))

def apply_force_world_point(self, physics_engine, sprite, force):
""" Apply force to a Sprite. """
physics_object = physics_engine.sprites[sprite]
physics_object.body.apply_force_at_world_point(force, (sprite.center_x, sprite.center_y))

def on_key_press(self, key, modifiers):
"""Called whenever a key is pressed. """

force = ( 0, 200)
self.physics_engine.apply_impulse(self.item_1, force)
self.physics_engine.apply_impulse(self.item_2, force)

def on_update(self, delta_time):
""" Movement and game logic """

self.physics_engine.step()
x = self.item_1.center_x
y = self.item_1.center_y
self.scroll_to_item(x,y)

def main():
MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
arcade.run()

if __name__ == "__main__":
main()


Подробнее здесь: https://stackoverflow.com/questions/792 ... hon-arcade
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «Python»