【问题标题】:Pymunk (chipmunk) - how to turn off physics/collisions temporarily for concrete objectPymunk(花栗鼠) - 如何暂时关闭具体物体的物理/碰撞
【发布时间】:2012-12-14 10:52:28
【问题描述】:

如何关闭某些对象的碰撞,然后在 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()))

【问题讨论】:

    标签: python collision game-physics chipmunk pymunk


    【解决方案1】:

    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

    【讨论】:

      【解决方案2】:

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

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

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

      【讨论】:

      • 我只需要进行一些试验(使用位 :P)来弄清楚碰撞过滤在 Pymunk 中的工作原理并且找不到其他示例,所以如果其他人有相同的情况,我会发布这个问题。
      • 为什么所有掩码 ^ 都带有异或?
      • 你能检查我的代码吗?我编辑了,如果你能帮助我,我需要一些紧急支持:)
      • 如果您将pymunk.ShapeFilter.ALL_MASKS 作为掩码属性传递,则掩码的所有位都打开,并且形状将与所有类别发生冲突。如果您想排除一个或多个类别,您必须关闭这些位,这可以通过切换位的按位异或来完成(请参阅wikipedia article)。
      • 我想与一个类别发生冲突而忽略所有其他类别。请在单元函数下检查我的代码。我的代码可以编译。我使用了 XOR 和 NOT 操作数。碰撞一个忽略所有其他。然而,这可能不是一件好事。我从上一个问题中知道您的代码。但这是一个新问题,它有点独特@skrx
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多