Open ptwaixingren opened 6 months ago
Take a look at this example and check what might be different from your setup. If you want you can also set b1.position to something else as I have commented out, its just that then it will keep collide with impulse & ke > 0 for more frames so I left it out to easily see the result.
import math
import pygame
import pymunk
import pymunk.pygame_util
pygame.init()
screen = pygame.display.set_mode((800, 800))
draw_options = pymunk.pygame_util.DrawOptions(screen)
clock = pygame.time.Clock()
space = pymunk.Space()
b1 = pymunk.Body()
s1 = pymunk.Segment(b1, (-25, 0), (25, 0), 5)
s1.mass = 1
#b1.position = 300, 125
b1.position = 300, 100
b1.angle = math.pi / 2
b2 = pymunk.Body()
s2 = pymunk.Segment(b2, (-25, 0), (25, 0), 5)
s2.mass = 1
b2.position = 400, 100
space.static_body.position = 250, 100
s3 = pymunk.Circle(space.static_body, 25)
space.add(s3)
space.add(b1, s1, b2, s2)
b2.apply_impulse_at_local_point((-10, 0))
def post_solve(arbiter: pymunk.Arbiter, space, data):
if arbiter.total_ke == 0:
return
print("post_solve")
print(f"total_impulse: {arbiter.total_impulse}, total_ke {arbiter.total_ke}")
handler = space.add_default_collision_handler()
handler.post_solve = post_solve
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
screen.fill((0, 0, 0))
space.debug_draw(draw_options)
space.step(1 / 50)
pygame.display.update()
clock.tick(50)
Thanks for your help! I changed the parameters of the shape in your code and found that the problem still persists. The new code is shown below.
import math
import pygame
import pymunk
import pymunk.pygame_util
pygame.init()
screen = pygame.display.set_mode((800, 800))
draw_options = pymunk.pygame_util.DrawOptions(screen)
clock = pygame.time.Clock()
space = pymunk.Space()
b1 = pymunk.Body()
s1 = pymunk.Segment(b1, (-8, 0), (8, 0), 8)
s1.mass = 1
#b1.position = 300, 125
b1.position = 300, 100
b1.angle = math.pi / 2
b2 = pymunk.Body()
s2 = pymunk.Segment(b2, (-8, 0), (8, 0), 8)
s2.mass = 1
b2.position = 400, 92
# space.static_body.position = 250, 100
# s3 = pymunk.Circle(space.static_body, 25)
# space.add(s3)
space.add(b1, s1, b2, s2)
b2.apply_impulse_at_local_point((-10, 0))
def post_solve(arbiter: pymunk.Arbiter, space, data):
if arbiter.total_ke == 0:
return
print("post_solve")
print(f"total_impulse: {arbiter.total_impulse}, total_ke {arbiter.total_ke}")
handler = space.add_default_collision_handler()
handler.post_solve = post_solve
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
screen.fill((0, 0, 0))
space.debug_draw(draw_options)
space.step(1 / 50)
pygame.display.update()
clock.tick(50)
I found that the result is the same as my previous question.
By the way, I also tried to use the code you commented on, but the collision does not work.
I also tried changing the angle of body1 by setting b1.angle=0. The collision result is incorrect.
However, if I change b1.position to (300, 115), the collision works.
Initially, I guessed the problem might be that the collision detection does not work when the circular parts of the segments collide. Then, I changed the positions and angles of the two objects. The difference between the new value and the old value is very small.
b1 = pymunk.Body()
s1 = pymunk.Segment(b1, (-25, 0), (25, 0), 5)
s1.mass = 1
#b1.position = 300, 125
b1.position = 300, 100
# b1.angle = math.pi / 2
b2 = pymunk.Body()
s2 = pymunk.Segment(b2, (-25, 0), (25, 0), 5)
s2.mass = 1
#b2.position = 400, 99
b2.position = 400, 101
space.static_body.position = 200, 100
s3 = pymunk.Circle(space.static_body, 25)
space.add(s3)
The collision detection of the two segments works well.
I'm very curious about why this happens. Do you have any pointers for me?
Thank you!
Ah, now I understand the issue. Something is broken with the segment/segment collision I think.
When playing around with it I also noticed that there are two different errors possible:
This arrangement will not create a collision at all until they intersect "without" radius:
b1 = pymunk.Body()
s1 = pymunk.Segment(b1, (-80, 0), (80, 0), 80)
s1.mass = 1
b1.angle = math.pi / 2
b1.position = 200, 200
b2 = pymunk.Body()
s2 = pymunk.Segment(b2, (-80, 0), (80, 0), 80)
s2.mass = 1
b2.position = 500, 280
While this one will create a collision, but no impulse to resolve it etc until the meet at the "no radius" point :
b1 = pymunk.Body()
s1 = pymunk.Segment(b1, (-80, 0), (80, 0), 80)
s1.mass = 1
b1.angle = math.pi / 2
# b1.position = 300, 125
b1.position = 200, 200
b2 = pymunk.Body()
s2 = pymunk.Segment(b2, (-80, 0), (80, 0), 80)
s2.mass = 1
b2.position = 500, 120
(I increased the size of the segments to better see the collision drawing)
The code for the segment collisions is here in case you are interested: https://github.com/viblo/Chipmunk2D/blob/2e8d4104b7e2380d1a73f5363a931b3eb3de8d07/src/cpCollision.c#L573 I suspect I wont be able to solve this quickly..
Anyway, I saw you sent me an email as well, and I think the idea you had of a workaround with two circles and a box all attached to the same body should work. Something like this, but maybe with some more consideration of the mass in case its important since now its not exactly the same.
b1 = pymunk.Body()
s1 = pymunk.Segment(b1, (-80, 0), (80, 0), 80)
s1.mass = 1
b1.angle = math.pi / 2
# b1.position = 300, 125
b1.position = 200, 200
b2 = pymunk.Body()
s2a = pymunk.Circle(b2, 80, (-80, 0))
s2b = pymunk.Circle(b2, 80, (80, 0))
s2c = pymunk.Poly.create_box(b2, (160, 160))
# s2 = pymunk.Segment(b2, (-80, 0), (80, 0), 80)
s2c.mass = 1
b2.position = 500, 120
space.add(b1, s1, b2, s2a, s2b, s2c)
Hi, thank you for your reply. I tested the code you provided, and the collision detection works well. I also have two questions about pymunk. The first one is, why should we consider the mass more in the previous example? In the example, the mass center of body 2 is at the center of the box. Could this cause errors when calculating the energy loss during collision? The second question is about the collision_type in the callback function, which seems to change its order when two bodies collide over many steps. I will describe this second question further. I created two bodies
b1 = pymunk.Body()
s1 = pymunk.Segment(b1, (-80, 0), (80, 0), 80)
s1.mass = 1
b1.angle = math.pi / 2
# b1.position = 300, 125
b1.position = 200, 20
b1.collision_type = 1
b2 = pymunk.Body()
s2 = pymunk.Segment(b2, (-80, 0), (80, 0), 80)
s2.mass = 1
# b1.position = 300, 125
b2.position = 250, 20
b2.collision_type = 2
def post_solve(arb, space, data):
collide_pair_contact_points = []
body1_id = arb.shapes[0].collision_type
body2_id = arb.shapes[1].collision_type
space.add_collision_handler(1, 2).post_solve = post_solve
"When I use body2 to collide with body1 continuously, I check the collision_type in the post_solve function. At first, it will return [1, 2]. Then, after some steps, it will return [2, 1]. Why does this happen?
So, to answer your first question about the mass: The moment of the body (body.moment
) will be different even when the center of gravity is the same.
For the second case I believe you have a small bug, at least in the code you pasted here. The collision type should be set on the shape and not body. If I change it to do that the ordering is consistent all the time for me.
Oh, I see. Thank you for your information.
Hi, I'm trying to simulate the collision between two segments, s1 and s2, which have the same shape. The initial positions of s1 and s2 are p1 = Vec2d(100, 100) and p2 = Vec2d(150, 92), respectively. Their initial angles are a1 = np.pi / 2 for s1 and a2 = -np.pi for s2. The elasticity and gravity for both segments are set to 0. The initial scenario is shown below.![simulation_steps_3](https://github.com/viblo/pymunk/assets/3894312/3b1395e4-84e6-4dcb-83d5-b1a531446d0d)
s2 approaches s1 using two methods. The first method involves setting the velocity of s2 (v2) to Vec2d(-0.5, 0) at the first step (
At this step, the two contact points obtained from the collide_handler are [100.0, 100.0] and [101.5, 100.0]. Subsequently, the segments separate from each other, resulting in the final scenario depicted below.
![simulation_steps_108](https://github.com/viblo/pymunk/assets/3894312/b13ec0be-70df-4cbd-84de-ee51df8adf4a)
agents[1].body.velocity = Vec2d(-0.5, 0.)
). The second method involves applying an impulse to s2 with the codeagents[1].body.apply_impulse_at_world_point(Vec2d(-1.0, 0), agents[1].body.position)
at the first step. After a few steps, the segments collide. Initially, when s1 and s2 collide, I check the total_impulse and total_ke of the system, which are (0, 0) and 0, respectively. These values remain unchanged until the positions of the two segments are as shown in the following figure.I wonder why this happen? Do you have any pointers for me? Thank you!