爆炸效果方案
想增加黑色小鳥的爆炸效果,但是翻遍了pymunk的文檔,沒有找到可以實現爆炸的函數,那就只能自己實現一個。
爆炸實現思路如下:當黑色小鳥爆炸時,以小鳥剛體的中心爲起點,向外發射速度很快的圓形小剛體,這些小剛體會被噴射到附近的剛體上,對附近的剛體施加力,造成爆炸的效果。
爆炸的測試截圖如下,可以看到12個紅色的小剛體以圓形向外擴散。
開始 debug模式:在source\contants.py中,設置 DEBUG = True。
因爲小剛體速度很快,我有將物理引擎的時間段修改爲 self.dt = 0.0002,方便截圖。
圖1
圖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