爆炸效果方案
想增加黑色小鸟的爆炸效果,但是翻遍了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
转载:https://blog.csdn.net/marble_xu/article/details/102538247