1

如何关闭某些对象的碰撞,然后在 python 中使用 pymunk lib 再次打开它?

让我根据下面的代码向您展示示例。我希望所有红球都通过线的第一个边界并停在下边界。蓝色球仍应与上边界碰撞。

代码中需要改变什么?

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()))
4

2 回答 2

4

Chipmunk 有一些过滤冲突的选项: http: //chipmunk-physics.net/release/ChipmunkLatest-Docs/#cpShape-Filtering

听起来您只需要使用图层位掩码即可。

前任:

# 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

我从未真正亲自使用过 Pymunk,但我猜它会将 Chipmunk 图层属性简单地暴露为 .layers

于 2012-12-29T17:35:23.873 回答
1

在 Pymunk 中,您可以使用ShapeFilter类来设置对象可以碰撞的类别(层)。我将上下线放入类别 1 和 2 中,然后设置球的蒙版,以便它们忽略这些层。您需要了解位掩码的工作原理。

这是基于原始问题中的代码的完整示例(按鼠标左键和右键生成球)。

import sys
import pygame as pg
from pygame.color import THECOLORS
import pymunk as pm


def to_pygame(p):
    """Small hack to convert pymunk to pygame coordinates"""
    return int(p[0]), int(-p[1]+600)


pg.init()
screen = pg.display.set_mode((600, 600))
clock = pg.time.Clock()

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

# Walls
static_body = space.static_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 idx, line in enumerate(static_lines):
    line.elasticity = 0.95
    if idx < 2:  # Lower lines.
        # The lower lines are in category 2, in binary 0b10.
        line.filter = pm.ShapeFilter(categories=2)
    else:  # Upper lines.
        # The upper lines are in category 1, in binary 0b1.
        line.filter = pm.ShapeFilter(categories=1)
space.add(static_lines)

balls = []
running = True

while running:
    for event in pg.event.get():
        if event.type == pg.QUIT:
            running = False
        elif event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE:
            running = False
        if event.type == pg.MOUSEBUTTONDOWN:
            radius = 15 if event.button == 1 else 30
            mass = 10
            inertia = pm.moment_for_circle(mass, 0, radius, (0,0))
            body = pm.Body(mass, inertia)
            body.position = to_pygame(event.pos)
            shape = pm.Circle(body, radius, (0,0))
            shape.elasticity = 0.95
            if shape.radius > 25:
                # bin(pm.ShapeFilter.ALL_MASKS ^ 1) is '0b11111111111111111111111111111110'
                # That means all categories are checked for collisions except
                # bit 1 (the upper lines) which are ignored.
                shape.filter = pm.ShapeFilter(mask=pm.ShapeFilter.ALL_MASKS ^ 1)
            else:
                # Ignores category bin(2), '0b11111111111111111111111111111101'
                # All categories are checked for collisions except bit 2 (the lower lines).
                shape.filter = pm.ShapeFilter(mask=pm.ShapeFilter.ALL_MASKS ^ 2)

            space.add(body, shape)
            balls.append(shape)

    screen.fill(THECOLORS["white"])

    balls_to_remove = []
    for ball in balls:
        if ball.body.position.y < 100:
            balls_to_remove.append(ball)

        p = to_pygame(ball.body.position)
        if ball.radius > 25:
            color = THECOLORS["red"]
        else:
            color = THECOLORS["blue"]
        pg.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)
        pg.draw.lines(screen, THECOLORS["gray29"], False, [p1, p2])

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

    pg.display.flip()
    clock.tick(50)


pg.quit()
sys.exit()
于 2017-04-11T13:26:02.903 回答