Python 愤怒的小鸟代码实现(2):爆炸效果实现

python 愤怒的小鸟代码实现(1):爆炸效果实现

爆炸效果方案

想增加黑色小鸟的爆炸效果,但是翻遍了pymunk的文档,没有找到可以实现爆炸的函数,那就只能自己实现一个。

爆炸实现思路如下:当黑色小鸟爆炸时,以小鸟刚体的中心为起点,向外发射速度很快的圆形小刚体,这些小刚体会被喷射到附近的刚体上,对附近的刚体施加力,造成爆炸的效果。

爆炸的测试截图如下,可以看到12个红色的小刚体以圆形向外扩散。
开始 debug模式:在source\contants.py中,设置 DEBUG = True。
因为小刚体速度很快,我有将物理引擎的时间段修改为 self.dt = 0.0002,方便截图。
图1explode
图2在这里插入图片描述
图2是爆炸时和障碍物碰撞的测试截图。

完整代码

游戏实现代码的github链接 愤怒的小鸟
这边是csdn的下载链接 愤怒的小鸟,这个是老的代码,最新代码请看github上的。

代码实现

pymunk的使用介绍在上一篇物理引擎pymunk使用中有介绍,这里就不重复了。
下面的代码都在source\component\physics.py 中

create_explosion函数根据参数

  • pos:黑色小鸟爆炸时的位置(x,y),这个已经是pymunk的坐标,不需要再转换
  • radius:黑色小鸟的圆形半径,可以看到计算小刚体的位置 (x,y) 时,是根据角度得到在小鸟的圆形弧上的位置,这样比从圆心发射更准确些。
  • length:设置爆炸的范围
  • mass:发射小刚体的质量

目前发射的小刚体数目为explode_num (值为12),可以修改。
注意这边的角度angle要以 (math.pi * 2) 计算得出,而不是度数。

add_explode函数,创建PhyExplode类,并添加到self.explodes list中,后面更新检测函数中会用到

    def add_explode(self, pos, angle, length, mass):
        phyexplode = PhyExplode(pos, angle, length, self.space, mass)
        self.explodes.append(phyexplode)

    def create_explosion(self, pos, radius, length, mass):
        ''' parameter pos is the pymunk position'''
        explode_num = 12
        sub_pi = math.pi * 2 / explode_num
        for i in range(explode_num):
            angle = sub_pi * i
            x = pos[0] + radius * math.sin(angle)
            y = pos[1] + radius * math.cos(angle)
            # angle value must calculated by math.pi * 2
            self.add_explode((x,y), angle, length, mass)

看下爆炸小刚体的类实现
初始化函数中

  • impulse: 表示初始对小刚体施加的力量,可以看作是初速度,这个是有方向的,Vec2d(0, 1)表示力量方向是竖直向上。
  • angle:传入的angle参数是顺时钟方向的角度,而rotated函数需要一个逆时针方向的角度,需要转换下。调用 impulse.rotated(angle) 后,力量的方向就偏转了对应的angle角度。
  • apply_impulse_at_local_point函数将上面的力量施加到刚体上。
    apply_impulse_at_local_point(impulse, point=(0, 0))
    Add the local impulse impulse to body as if applied from the body local point.
  • collision_type:定义刚类的碰撞类型为一个新的类型COLLISION_EXPLODE。

is_out_of_length函数,self.length就是初始化时设置的爆炸的范围,如果超出爆炸范围,就要删除这个刚体。

class PhyExplode():
    def __init__(self, pos, angle, length, space, mass=5.0):
        ''' parater angle is clockwise value '''
        radius = 3
        moment = 1000
        body = pm.Body(mass, moment)
        body.position = Vec2d(pos)

        power = mass * 2000
        impulse = power * Vec2d(0, 1)
        # the angle of rotated function is counter-clockwise, need to reverse it
        angle = -angle
        body.apply_impulse_at_local_point(impulse.rotated(angle))

        shape = pm.Circle(body, radius, (0, 0))
        shape.friction = 1
        shape.collision_type = COLLISION_EXPLODE
        space.add(body, shape)
        self.body = body
        self.shape = shape
        self.orig_pos = pos
        self.length = length

    def is_out_of_length(self):
        pos = self.body.position
        distance = tool.distance(*pos, *self.orig_pos)
        if distance >= self.length:
            return True
        return False

爆炸的更新检查函数check_explosion, 计算刚体是否要被删除:

  • 计算刚体的存在时间,如果超过1000ms就删除,
  • 调用 is_out_of_length 函数判断刚体是否超出的爆炸范围,如果超出就删除。
    def check_explosion(self):
        explodes_to_remove = []
        if len(self.explodes) == 0:
            return

        if self.explode_timer == 0:
            self.explode_timer = self.current_time
        elif (self.current_time - self.explode_timer) > 1000:
            for explode in self.explodes:
                self.space.remove(explode.shape, explode.shape.body)
                self.explodes.remove(explode)
                self.explode_timer = 0

        for explode in self.explodes:
            if explode.is_out_of_length():
                explodes_to_remove.append(explode)

        for explode in explodes_to_remove:
            self.space.remove(explode.shape, explode.shape.body)
            self.explodes.remove(explode)

setup_collision_handler函数中添加爆炸小刚体和小猪或障碍物的碰撞回调函数,这样在爆炸小刚体和小猪或障碍物发生碰撞时,会根据冲击力的大小来相应减去小猪或障碍物的生命。

def setup_collision_handler(self):
	    ...

        def post_solve_block_explode(arbiter, space, data):
            if self.check_collide:
                block_shape = arbiter.shapes[0]
                if arbiter.total_impulse.length > MIN_DAMAGE_IMPULSE:
                    my_phy.handle_block_collide(block_shape, arbiter.total_impulse.length)

        def post_solve_pig_explode(arbiter, space, data):
            if self.check_collide:
                pig_shape = arbiter.shapes[0]
                if arbiter.total_impulse.length > MIN_DAMAGE_IMPULSE:
                    my_phy.handle_pig_collide(pig_shape, arbiter.total_impulse.length)

        ...
        
        self.space.add_collision_handler(
            COLLISION_BLOCK, COLLISION_EXPLODE).post_solve = post_solve_block_explode

        self.space.add_collision_handler(
            COLLISION_PIG, COLLISION_EXPLODE).post_solve = post_solve_pig_explode

编译环境

python3.7 + pygame1.9 + pymunk 5.5.0

发布了22 篇原创文章 · 获赞 487 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/marble_xu/article/details/102538247
今日推荐