I have a simple setup in pymunk where a circle collides with a segment. On every collision the radius of the circle should increase by an amount I specify (5% in this case). The circle should bounce correctly from the segment even after the radius increases. Here is my implementation but I am facing an issue where this code works erratically. Sometimes the radius increases on collision and sometimes it does not and I can’t figure out why.
import pygame as pg
import pymunk
import pymunk.pygame_util
import sys
import math
pymunk.pygame_util.positive_y_is_up = False
RES = WIDTH, HEIGHT = 1080 // 2, 1920 // 2
FPS = 120
pg.init()
surface = pg.display.set_mode(RES)
clock = pg.time.Clock()
draw_options = pymunk.pygame_util.DrawOptions(surface)
space = pymunk.Space()
space.gravity = 0, 530
elasticity = 0.999
friction = 0.05
# create the bounding box
def create_segments(start, end):
segment_shape = pymunk.Segment(space.static_body, start, end, 3)
segment_shape.elasticity = elasticity
segment_shape.friction = friction
segment_shape.collision_type = 1 # Assign a collision type to the walls
space.add(segment_shape)
create_segments((0, HEIGHT), (WIDTH, HEIGHT)) # floor
create_segments((0, 0), (0, HEIGHT)) # left wall
create_segments((WIDTH, 0), (WIDTH, HEIGHT)) # right wall
create_segments((0, 0), (WIDTH, 0)) # ceiling
# create the ball
ball_mass = 1
ball_radius = 20 # Separate variable to store the radius
ball_moment = pymunk.moment_for_circle(ball_mass, 0, ball_radius)
ball_body = pymunk.Body(ball_mass, ball_moment)
ball_body.position = WIDTH // 2, 50
ball_body.velocity = (400, 200)
ball_shape = pymunk.Circle(ball_body, ball_radius)
ball_shape.elasticity = elasticity
ball_shape.friction = friction
ball_shape.collision_type = 2 # Assign a different collision type to the ball
space.add(ball_body, ball_shape)
#Collision handler
collided = False # Flag to track if collision happened in the current frame
def collision_handler(arbiter, space, data):
global ball_radius, ball_shape, collided
if collided:
return False
# Calculate new radius
new_radius = ball_radius * 1.05 # 5% increase
# Adjust the ball's position based on the collision direction
normal = arbiter.contact_point_set.normal
if normal.y > 0: # Collision with floor
ball_body.position += (0, new_radius - ball_radius)
elif normal.y < 0: # Collision with ceiling
ball_body.position -= (0, new_radius - ball_radius)
elif normal.x > 0: # Collision with right wall
ball_body.position += (new_radius - ball_radius, 0)
elif normal.x < 0: # Collision with left wall
ball_body.position -= (new_radius - ball_radius, 0)
# Now check against the limits after adjusting the position
if new_radius > ball_body.position[0] or new_radius > ball_body.position[1] or
new_radius > WIDTH - ball_body.position[0] or new_radius > HEIGHT - ball_body.position[1]:
return True # Reject radius increase if it exceeds space limits
# Increase the radius
ball_radius = new_radius
# Remove the old ball shape
space.remove(ball_shape)
# Create the new ball shape
ball_shape = pymunk.Circle(ball_body, ball_radius)
ball_shape.elasticity = elasticity
ball_shape.friction = friction
ball_shape.collision_type = 2 # Assign the same collision type as before
space.add(ball_shape)
collided = True
return True
# Add collision handler for the ball hitting the walls
handler = space.add_collision_handler(1, 2) # Only handle collisions between types 1 and 2
handler.pre_solve = collision_handler # Use pre_solve instead of post_solve
# Main loop
while True:
surface.fill(pg.Color('white'))
for i in pg.event.get():
if i.type == pg.QUIT:
pg.quit()
sys.exit()
collided = False
space.step(1 / FPS)
space.debug_draw(draw_options)
pg.display.flip()
clock.tick(FPS)
I have some other parts of code where I check and display the radius and that’s how I was able to tell that the radius is not increasing on every bounce. For simplicity I’ve only included the relevant parts of the code here. I believe it is something to do with my collision handling logic but I am not able to troubleshoot.