在 pymunk 群众之间制造排斥
Creating repulsion between pymunk masses
我正在尝试制作一个动画,其中两个磁铁(相互排斥)落入旋转管中。我有下落的(重力)钻头和向下的旋转,但我在磁力方面遇到了麻烦。我使用的力的方程式是(磁铁 1 的磁场强度 x 磁铁 2 的磁场强度)/(磁铁之间的距离)^2。基本上,力的强度随着磁铁之间距离的平方而减小。目标是让磁铁在同极彼此面对时相互排斥。我相信我没有正确使用“apply_force_at_local_point”命令。我特别不确定在“apply_force_at_local_point”命令
中更新力的 x 和 y 方向
你真的不需要知道太多物理学知识。预先感谢您的帮助
import pymunk
import pymunk.pygame_util
import pygame
import numpy as np
GRAY = (220, 220, 220)
width_mass=48
height_mass=48
charges=[10000,-10000] #magnet strengths
pygame.init()
size = 800,600
screen = pygame.display.set_mode(size,pygame.FULLSCREEN)
draw_options = pymunk.pygame_util.DrawOptions(screen)
space = pymunk.Space()
space.gravity = (0,-900)
pts = [(-27, -238.5), (27,-238.5), (27,238.5), (-27,238.5)] #enpoints of the endlessly rotating rectangle
body_type=pymunk.Body(body_type=pymunk.Body.KINEMATIC)
body_type.position = (400, 263.5)
space.add(body_type)
for i in range(4):
segment = pymunk.Segment(body_type, pts[i], pts[(i+1)%4], 2)
space.add(segment)
body_type.angular_velocity=1 #rotation speed
class Rectangle:
def __init__(self, rect_mass, pos,size=(80, 50)):
self.body = pymunk.Body(mass=rect_mass)
self.body.position = pos
shape = pymunk.Poly.create_box(self.body, size)
shape.density = 0.1
space.add(self.body, shape)
mass_1 = Rectangle(rect_mass=1,pos=(400,473),size=(50,50))
mass_2 = Rectangle(rect_mass=1,pos=(400,420),size=(50,50))
masses=[mass_1,mass_2]
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill(GRAY)
space.debug_draw(draw_options)
pygame.display.update()
space.step(0.01)
temp=[] #collecting the positions of all masses in one place
for mass in masses:
temp.append(mass.body.position)
if len(masses)==2:
rel_dist=np.sqrt((temp[1][1]-temp[0][1])**2+(temp[1][0]-temp[0][0])**2) #euclidean distance between magnets
mag_force=charges[0]*charges[1]/(rel_dist**2 + 0.00001) #force = magnet1*magnet2/dist of the magnets^2
masses[0].body.apply_force_at_local_point(
(mag_force*(temp[1][0]-temp[0][0]),
mag_force*(temp[1][1]-temp[0][1])),
(masses[0].body.position.x,masses[0].body.position.y)) #this needs to be fixed
masses[1].body.apply_force_at_local_point(
-1*(mag_force*(temp[1][0]-temp[0][0]),
mag_force*(temp[1][1]-temp[0][1])),
(masses[1].body.position.x,masses[1].body.position.y)) #this needs to be fixed
pygame.quit()
代码有几个问题。我认为主要问题是您使用 apply_force_at_local_point
而不是 apply_force_at_world_point
。 Apply at local point 从 body 的局部点施加力。也就是说,力和位置都应在 body 本地坐标中给出。因此,例如,如果您将力的位置放在 (0,0),则意味着力将施加在 body 的中心。注意角度也要考虑。
通常更容易在世界坐标中思考并使用 apply_force_at_world_point
代替。从您的代码看来,这也是您的想法,因为您指出要在 body 世界位置施加力。
其他一些问题:
您覆盖了矩形主体上的质量。本身不是问题,但你会感到困惑:)
如果您设置附加到 body 的形状的密度,它们将覆盖您指定的任何质量。所以在你的代码中 rect_mass
实际上没有被使用。相反,body 的质量和力矩是通过使用您在形状上设置的密度来计算的。要查看生成的质量和力矩,您可以在将它们添加到 space 后打印出来:print(body.mass, body.moment)
在第二个应用力中乘以 -1
。但是,你乘的是一个元组,所以最终结果是一个空元组。
-1 * (
mag_force * (temp[1][0] - temp[0][0]),
mag_force * (temp[1][1] - temp[0][1]),
)
相反,您可以将它包装在 pymunk.Vec2d 中,或者将 -1 移到里面:
-1 * Vec2d(
mag_force * (temp[1][0] - temp[0][0]),
mag_force * (temp[1][1] - temp[0][1]),
)
#or
(
-1 * mag_force * (temp[1][0] - temp[0][0]),
-1 * mag_force * (temp[1][1] - temp[0][1]),
)
最后,当我查看您的代码时,我做了两件事来帮助我调试问题。首先是print/draw有问题的部分
您可以为形状设置单独的颜色。这样更容易理解他们的行为。例如像这样 shape.color = (255,0,0,255)
其中元组是 RGBA 元组,使形状变红。
绘制施加的力对我很有用。一种方法是做这样的事情:
f1 = (
mag_force * (temp[1][0] - temp[0][0]),
mag_force * (temp[1][1] - temp[0][1]),
)
masses[0].body.apply_force_at_world_point(
f1, (masses[0].body.position.x, masses[0].body.position.y)
)
p2 = (100, 100) + pymunk.Vec2d(f1) / 10000
pygame.draw.lines(
screen,
(100, 0, 0),
False,
[(100, 100), (round(p2.x), round(p2.y))],
3,
)
(然后在例如 (300,100) 绘制第二张图)
最后我想补充一点,pymunk.Vec2d
具有计算两点之间距离的功能(例如 rel_dist = temp[0].get_distance(temp[1])
)。但这更多是个人选择,而且我很偏颇,因为我是 Pymunk 的作者 :)
我正在尝试制作一个动画,其中两个磁铁(相互排斥)落入旋转管中。我有下落的(重力)钻头和向下的旋转,但我在磁力方面遇到了麻烦。我使用的力的方程式是(磁铁 1 的磁场强度 x 磁铁 2 的磁场强度)/(磁铁之间的距离)^2。基本上,力的强度随着磁铁之间距离的平方而减小。目标是让磁铁在同极彼此面对时相互排斥。我相信我没有正确使用“apply_force_at_local_point”命令。我特别不确定在“apply_force_at_local_point”命令
中更新力的 x 和 y 方向你真的不需要知道太多物理学知识。预先感谢您的帮助
import pymunk
import pymunk.pygame_util
import pygame
import numpy as np
GRAY = (220, 220, 220)
width_mass=48
height_mass=48
charges=[10000,-10000] #magnet strengths
pygame.init()
size = 800,600
screen = pygame.display.set_mode(size,pygame.FULLSCREEN)
draw_options = pymunk.pygame_util.DrawOptions(screen)
space = pymunk.Space()
space.gravity = (0,-900)
pts = [(-27, -238.5), (27,-238.5), (27,238.5), (-27,238.5)] #enpoints of the endlessly rotating rectangle
body_type=pymunk.Body(body_type=pymunk.Body.KINEMATIC)
body_type.position = (400, 263.5)
space.add(body_type)
for i in range(4):
segment = pymunk.Segment(body_type, pts[i], pts[(i+1)%4], 2)
space.add(segment)
body_type.angular_velocity=1 #rotation speed
class Rectangle:
def __init__(self, rect_mass, pos,size=(80, 50)):
self.body = pymunk.Body(mass=rect_mass)
self.body.position = pos
shape = pymunk.Poly.create_box(self.body, size)
shape.density = 0.1
space.add(self.body, shape)
mass_1 = Rectangle(rect_mass=1,pos=(400,473),size=(50,50))
mass_2 = Rectangle(rect_mass=1,pos=(400,420),size=(50,50))
masses=[mass_1,mass_2]
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill(GRAY)
space.debug_draw(draw_options)
pygame.display.update()
space.step(0.01)
temp=[] #collecting the positions of all masses in one place
for mass in masses:
temp.append(mass.body.position)
if len(masses)==2:
rel_dist=np.sqrt((temp[1][1]-temp[0][1])**2+(temp[1][0]-temp[0][0])**2) #euclidean distance between magnets
mag_force=charges[0]*charges[1]/(rel_dist**2 + 0.00001) #force = magnet1*magnet2/dist of the magnets^2
masses[0].body.apply_force_at_local_point(
(mag_force*(temp[1][0]-temp[0][0]),
mag_force*(temp[1][1]-temp[0][1])),
(masses[0].body.position.x,masses[0].body.position.y)) #this needs to be fixed
masses[1].body.apply_force_at_local_point(
-1*(mag_force*(temp[1][0]-temp[0][0]),
mag_force*(temp[1][1]-temp[0][1])),
(masses[1].body.position.x,masses[1].body.position.y)) #this needs to be fixed
pygame.quit()
代码有几个问题。我认为主要问题是您使用 apply_force_at_local_point
而不是 apply_force_at_world_point
。 Apply at local point 从 body 的局部点施加力。也就是说,力和位置都应在 body 本地坐标中给出。因此,例如,如果您将力的位置放在 (0,0),则意味着力将施加在 body 的中心。注意角度也要考虑。
通常更容易在世界坐标中思考并使用 apply_force_at_world_point
代替。从您的代码看来,这也是您的想法,因为您指出要在 body 世界位置施加力。
其他一些问题:
您覆盖了矩形主体上的质量。本身不是问题,但你会感到困惑:) 如果您设置附加到 body 的形状的密度,它们将覆盖您指定的任何质量。所以在你的代码中
rect_mass
实际上没有被使用。相反,body 的质量和力矩是通过使用您在形状上设置的密度来计算的。要查看生成的质量和力矩,您可以在将它们添加到 space 后打印出来:print(body.mass, body.moment)
在第二个应用力中乘以
-1
。但是,你乘的是一个元组,所以最终结果是一个空元组。
-1 * (
mag_force * (temp[1][0] - temp[0][0]),
mag_force * (temp[1][1] - temp[0][1]),
)
相反,您可以将它包装在 pymunk.Vec2d 中,或者将 -1 移到里面:
-1 * Vec2d(
mag_force * (temp[1][0] - temp[0][0]),
mag_force * (temp[1][1] - temp[0][1]),
)
#or
(
-1 * mag_force * (temp[1][0] - temp[0][0]),
-1 * mag_force * (temp[1][1] - temp[0][1]),
)
最后,当我查看您的代码时,我做了两件事来帮助我调试问题。首先是print/draw有问题的部分
您可以为形状设置单独的颜色。这样更容易理解他们的行为。例如像这样
shape.color = (255,0,0,255)
其中元组是 RGBA 元组,使形状变红。绘制施加的力对我很有用。一种方法是做这样的事情:
f1 = (
mag_force * (temp[1][0] - temp[0][0]),
mag_force * (temp[1][1] - temp[0][1]),
)
masses[0].body.apply_force_at_world_point(
f1, (masses[0].body.position.x, masses[0].body.position.y)
)
p2 = (100, 100) + pymunk.Vec2d(f1) / 10000
pygame.draw.lines(
screen,
(100, 0, 0),
False,
[(100, 100), (round(p2.x), round(p2.y))],
3,
)
(然后在例如 (300,100) 绘制第二张图)
最后我想补充一点,pymunk.Vec2d
具有计算两点之间距离的功能(例如 rel_dist = temp[0].get_distance(temp[1])
)。但这更多是个人选择,而且我很偏颇,因为我是 Pymunk 的作者 :)