pygame练手项目(带物理引擎pymunk)

2023-04-03  本文已影响0人  圣_狒司机

不断下落和堆积的小球,如果碰撞了,小的就会被吃掉。


一、 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

  1. 初始化物理空间,设置一个重力场,pumunk 6.4.0版本以后坐标系与pygame一致,不需要上下颠倒转换了,如果直接拷贝之前版本的pymunk项目,可以尝试打开pygame_util.positive_y_is_up选项,目前版本默认是False。
space = pymunk.Space() 
space.gravity = 0,998
  1. 杂糅了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)
  1. 事件回调函数
    这个写法类似装饰器,但是不是装饰器,只是显式的把函数当做对象传入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)
上一篇下一篇

猜你喜欢

热点阅读