不断下落和堆积的小球,如果碰撞了,小的就会被吃掉。
![](https://img.haomeiwen.com/i4299975/55722a82f9737e45.png)
一、 pygame框架
import pygame,pymunk,sys
import numpy as np
pygame.init()
space = pymunk.Space()
space.gravity = 0,998
screen = pygame.display.set_mode((800,600))
clock = pygame.time.Clock()
FPS = 50
txt_font = pygame.font.SysFont(None,48)
ball_count = 0
while True:
screen.fill(0)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.blit(txt_img,(0,0))
pygame.display.flip()
clock.tick(FPS)
二、 物理引擎pymunk
- 初始化物理空间,设置一个重力场,pumunk 6.4.0版本以后坐标系与pygame一致,不需要上下颠倒转换了,如果直接拷贝之前版本的pymunk项目,可以尝试打开pygame_util.positive_y_is_up选项,目前版本默认是False。
space = pymunk.Space()
space.gravity = 0,998
- 杂糅了pymunk的pygame.spirit.Spirit类:
pymunk实例化物理模型最好是先实例空的Body对象,再实例化Shape对象,如果直接在Body里赋值重量、转动惯量等属性,在实例化Shape时很可能人工赋值被模块自动计算的值覆盖。
Body和Shape对象在space.add操作以后,才会获得真正的空间位置和其他各种空间属性,在此操作之前的很多物理属性都是无用的默认值。
pymun的空间位置是浮点数,很多pygame.draw方法要求空间位置,比如坐标中心点等,都要求整型,所以需要类型转换。
如果实例化精灵类太多,就需要精灵杀死机制以控制总量。
杀死机制顺序是 先space.remove物理类型,然后group.remove精灵类型。
class Sphere(pygame.sprite.Sprite):
def __init__(self,radius,pos):
super().__init__()
self.radius = radius
self.center = np.random.randint(5,50,2).tolist()
self.image = pygame.Surface((self.radius*2,self.radius*2))
self.rect = pygame.draw.circle(self.image,(255,255,255),(self.radius,self.radius),self.radius)
self.body = pymunk.Body(body_type=0)
self.shape = pymunk.Circle(self.body,self.radius)
self.shape.density = 1
self.shape.elasticity = 0.9
self.shape.set_friction = 0.2
self.shape.collision_type = 1
self.body.position = pos
space.add(self.body,self.shape)
self.rect.center = np.array(self.shape.bb.center()).astype(np.int32).tolist()
balls.add(self)
def update(self):
self.rect.center = np.array(self.shape.bb.center()).astype(np.int32).tolist()
if any([self.rect.center[0]<0,self.rect.center[0]>800,self.rect.center[1]<0,self.rect.center[1]>600]):
balls.remove(self)
- 事件回调函数
这个写法类似装饰器,但是不是装饰器,只是显式的把函数当做对象传入handler中。
space.add_collision_handler(0,1)会生成一个pymunk.collision_handler.CollisionHandler对象,(0,1)是shape.collision_type,在精灵类里必须设置好,类型为整型。
CollisionHandler对象包含四个属性:
data = {},begin,post_solve,pre_solve,separate;
data是个字典,用于传参,另外三个是回调函数接口;
给post_solve赋值,赋值为一个回调函数,CollisionHandler对象会自动在自身生成时付给post_solve这个回调函数一些参数:(Arbiter,space,data)
Arbiter 翻译叫做仲裁者,它包含了事件发生时的所有关联对象的即时性质、状况,用于操作事件发生前后如何去处理。space就是物理空间,data是事件发生时人工再添加一些附属性质。永远不要实例化一个Arbiter类,它应该被自动生成。
def add_collision_handler():
def post_collision_line_ball(arbiter, space,data):
pass
def post_collision_ball_ball(arbiter, space,data):
shape = arbiter.shapes[0] if arbiter.shapes[0].radius < arbiter.shapes[1].radius else arbiter.shapes[1]
space.remove(shape)
for spirit in balls.sprites():
if spirit.shape == shape:
balls.remove(spirit)
space.add_collision_handler(0,1).post_solve = post_collision_line_ball
space.add_collision_handler(1,1).post_solve = post_collision_ball_ball
三、 源代码
import pygame
import pymunk
import numpy as np
import sys
pygame.init()
space = pymunk.Space()
space.gravity = 0,998
screen = pygame.display.set_mode((800,600))
clock = pygame.time.Clock()
FPS = 50
txt_font = pygame.font.SysFont(None,48)
ball_count = 0
t = 0
balls = pygame.sprite.Group()
def add_collision_handler():
def post_collision_line_ball(arbiter, space,data):
pass
def post_collision_ball_ball(arbiter, space,data):
shape = arbiter.shapes[0] if arbiter.shapes[0].radius < arbiter.shapes[1].radius else arbiter.shapes[1]
space.remove(shape)
for spirit in balls.sprites():
if spirit.shape == shape:
balls.remove(spirit)
space.add_collision_handler(0,1).post_solve = post_collision_line_ball
space.add_collision_handler(1,1).post_solve = post_collision_ball_ball
class Line():
def __init__(self,a,b):
self.body = pymunk.Body(body_type=2)
self.shape = pymunk.Segment(self.body,a,b,5)
self.rect = pygame.draw.line(screen,(255,255,255),a,b)
self.elasticity = 1
self.friction = 0.1
self.shape.collision_type = 0
space.add(self.body,self.shape)
class Sphere(pygame.sprite.Sprite):
def __init__(self,radius,pos):
super().__init__()
self.radius = radius
self.center = np.random.randint(5,50,2).tolist()
self.image = pygame.Surface((self.radius*2,self.radius*2))
self.rect = pygame.draw.circle(self.image,(255,255,255),(self.radius,self.radius),self.radius)
self.body = pymunk.Body(body_type=0)
self.shape = pymunk.Circle(self.body,self.radius)
self.shape.density = 1
self.shape.elasticity = 0.9
self.shape.set_friction = 0.2
self.shape.collision_type = 1
self.body.position = pos
space.add(self.body,self.shape)
self.rect.center = np.array(self.shape.bb.center()).astype(np.int32).tolist()
balls.add(self)
def update(self):
self.rect.center = np.array(self.shape.bb.center()).astype(np.int32).tolist()
if any([self.rect.center[0]<0,self.rect.center[0]>800,self.rect.center[1]<0,self.rect.center[1]>600]):
balls.remove(self)
[Line(a,b) for a,b in [[(0,0),(0,600)],[(0,600),(400,500)],[(400,500),(800,600)],[(800,600),(800,0)]]]
add_collision_handler()
while True:
screen.fill(0)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
t+=1
if t%20 == 0:
ball_count += 1
Sphere(int(np.random.randint(15,30,1)),np.random.randint(200,600,2).tolist())
pygame.draw.lines(screen,(255,255,255),False,[(0,0),(0,600),(400,500),(800,600),(800,0)],5)
space.step(0.02)
balls.update()
balls.draw(screen)
txt_img = txt_font.render(f'{ball_count=}',True,(160,160,90),(0,0,0))
screen.blit(txt_img,(0,0))
pygame.display.flip()
clock.tick(FPS)
网友评论