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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章