pythoncollisiongame-physicschipmunkpymunk

Pymunk (chipmunk) - how to turn off physics/collisions temporarily for concrete object


How to turn off collisions for some objects and then again turn it on using pymunk lib in python?

Let me show you the example, based on the code below. I want all red balls to go through first border of lines and stop on the lower border. Blue balls should still collide with upper border.

What needs to be changed in the code?

import pygame
from pygame.locals import *
from pygame.color import *
import pymunk as pm
from pymunk import Vec2d
import math, sys, random

def to_pygame(p):
    """Small hack to convert pymunk to pygame coordinates"""
    return int(p.x), int(-p.y+600)

pygame.init()
screen = pygame.display.set_mode((600, 600))
clock = pygame.time.Clock()
running = True

### Physics stuff
space = pm.Space()
space.gravity = (0.0, -900.0)

## Balls
balls = []

### walls
static_body = pm.Body()
static_lines = [pm.Segment(static_body, (111.0, 280.0), (407.0, 246.0), 0.0),
                pm.Segment(static_body, (407.0, 246.0), (407.0, 343.0), 0.0),
                pm.Segment(static_body, (111.0, 420.0), (407.0, 386.0), 0.0),
                pm.Segment(static_body, (407.0, 386.0), (407.0, 493.0), 0.0)]  
for line in static_lines:
    line.elasticity = 0.95
space.add(static_lines)

ticks_to_next_ball = 10

while running:
    for event in pygame.event.get():
        if event.type == QUIT:
            running = False
        elif event.type == KEYDOWN and event.key == K_ESCAPE:
            running = False

    ticks_to_next_ball -= 1
    if ticks_to_next_ball <= 0:
        ticks_to_next_ball = 100
        mass = 10
        radius = random.randint(10,40)
        inertia = pm.moment_for_circle(mass, 0, radius, (0,0))
        body = pm.Body(mass, inertia)
        x = random.randint(115,350)
        body.position = x, 600
        shape = pm.Circle(body, radius, (0,0))
        shape.elasticity = 0.95
        space.add(body, shape)
        balls.append(shape)

    ### Clear screen
    screen.fill(THECOLORS["white"])

    ### Draw stuff
    balls_to_remove = []
    for ball in balls:
        if ball.body.position.y < 200: balls_to_remove.append(ball)

        p = to_pygame(ball.body.position)
        if ball.radius > 25:
            color = THECOLORS["blue"]
        else:
            color = THECOLORS["red"]
        pygame.draw.circle(screen, color, p, int(ball.radius), 2)

    for ball in balls_to_remove:
        space.remove(ball, ball.body)
        balls.remove(ball)

    for line in static_lines:
        body = line.body
        pv1 = body.position + line.a.rotated(body.angle)
        pv2 = body.position + line.b.rotated(body.angle)
        p1 = to_pygame(pv1)
        p2 = to_pygame(pv2)
        pygame.draw.lines(screen, THECOLORS["lightgray"], False, [p1,p2])

    ### Update physics
    dt = 1.0/60.0
    for x in range(1):
        space.step(dt)

    ### Flip screen
    pygame.display.flip()
    clock.tick(50)
    pygame.display.set_caption("fps: " + str(clock.get_fps()))

Solution

  • Chipmunk has a few options filtering collisions: http://chipmunk-physics.net/release/ChipmunkLatest-Docs/#cpShape-Filtering

    It sounds like you just need to use a layers bitmask though.

    ex:

    # This layer bit is for balls colliding with other balls
    # I'm only guessing that you want this though.
    ball_layer = 1
    # This layer bit is for things that collide with red balls only.
    red_ball_layer = 2
    # This layer bit is for things that collide with blue balls only.
    blue_ball_layer = 4
    
    # Bitwise OR the layer bits together
    red_ball_shape.layers = ball_layer | red_ball_layer
    blue_ball_shape.layers = ball_layer | blue_ball_layer
    
    # Lower border should collide with red only
    upper_border_shape.layers = red_ball_layer
    
    #Upper border with blue balls only
    lower_border_shape.layers = blue_ball_layer
    

    I've never actually used Pymunk personally, but I'm guessing that it exposes the Chipmunk layers property simply as .layers