美文网首页
pygame练手项目(带物理引擎pymunk)

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

作者: 圣_狒司机 | 来源:发表于2023-04-03 15:24 被阅读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)
    

    相关文章

      网友评论

          本文标题:pygame练手项目(带物理引擎pymunk)

          本文链接:https://www.haomeiwen.com/subject/fiodddtx.html