Python 愤怒的小鸟代码实现:物理引擎pymunk使用
游戏介绍
最近比较忙,周末正好有时间写了python版本的愤怒的小鸟,使用了物理引擎pymunk,图片资源是从github上下载的,实现了一个可玩的简单版本。
功能实现如下:
- 支持小鸟类型:红色小鸟,蓝色小鸟,黄色小鸟。
- 支持障碍物的类型:玻璃,木头,石头。
- 支持障碍物的形状:各种长度的长方形,正方形和圆形。
- 使用json文件保存关卡信息,设置小猪和障碍物的位置。
游戏截图如下:

图2

图3

完整代码
游戏实现代码的github链接 愤怒的小鸟
这边是csdn的下载链接 愤怒的小鸟
Pymunk介绍
pymunk是一个2D的物理引擎, 它实际是封装了 c语言写的2D物理引擎Chipmunk,可以实现碰撞,旋转等物理运动。
安装pymunk,可以直接使用pip工具,安装最新的pymunk 5.5.0:
pip install pymunk
介绍下在pymunk中会使用到的四个基本的类:
- 刚体 (pymunk.Body):一个刚体具有物体的物理属性(质量、坐标、旋转角度、速度等),它自己是没有形状的。
- 碰撞形状 (pymunk.Circle, pymunk.Segment and pymunk.Poly):通过将形状附加到实体,你可以定义一个实体的形状。你可以将多个形状附加到单个实体上来定义一个复杂的形状,如果不需要形状,则可以不附加任何形状。
- 约束/关节 (pymunk.constraint.PinJoint, pymunk.constraint.SimpleMotor):你可以在两个实体之间附加关节以约束它们的行为。比如在两个实体间保持一个固定的距离。
- 空间 (pymunk.Space): 空间是pymunk中基本的模拟单元。你可以添加实体,形状和关节到空间,然后整体更新空间。pymunk会控制空间中所有的实体,形状和关节如何相互作用。
代码实现
将物理引擎相关的代码单独放在了一个文件 (source\component\physics.py)中,减少代码的耦合。
定义了一个Physics类,向外提供所有物理引擎相关的函数。
这篇文章只介绍physics.py 中pymunk相关的代码。
pymunk相关初始化
reset 函数初始化了 空间类(pm.Space), 设置了两个参数
- gravity : 重力
- dt (Time step length) : 表示pymunk中每次更新的时间段值,比如dt值是0.002,表示时间段是0.002秒。
setup_lines函数设置了一条直线,作为地面。
Segment类创建了一条从点a 到 点b的直线。
class pymunk.Segment(body, a, b, radius)
Bases: pymunk.shapes.Shape
A line segment shape between two point. Meant mainly as a static shape.
交流群:632408235
import pymunk as pm class Physics():
def __init__(self):
self.reset() def reset(self, level=None):
self.level = level
# init space: set gravity and dt
self.space = pm.Space()
self.space.gravity = (0.0, -700.0)
self.dt = 0.002
self.birds = []
self.pigs = []
self.blocks = []
self.path_timer = 0
self.check_collide = False
self.setup_lines()
self.setup_collision_handler() def setup_lines(self):
# Static Ground
x, y = to_pymunk(c.SCREEN_WIDTH, c.GROUND_HEIGHT)
static_body = pm.Body(body_type=pm.Body.STATIC)
static_lines = [pm.Segment(static_body, (0.0, y), (x, y), 0.0)] for line in static_lines:
line.elasticity = 0.95
line.friction = 1
line.collision_type = COLLISION_LINE
self.space.add(static_lines)
self.static_lines = static_lines
setup_collision_handler 函数用来设置在两种类型的物体在碰撞发生时,可以由用户使用的回调函数。
add_collision_handler 函数添加两种类型物体a和b碰撞时会调用的handler。比如小猪和小鸟这两种类型物体的注册函数就是:add_collision_handler(COLLISION_PIG, COLLISION_BIRD)
add_collision_handler(collision_type_a, collision_type_b)
Return the CollisionHandler for collisions between objects of type collision_type_a and collision_type_b.
我们这里只用到了 post_solve 回调函数,在两个物体碰撞结束后,获取碰撞冲击力(collision impulse)。
post_solve
Two shapes are touching and their collision response has been processed.
func(arbiter, space, data)
You can retrieve the collision impulse or kinetic energy at this time if you want to use it to calculate sound volumes or damage amounts. See Arbiter for more info.
比如handle_pig_collide函数在小猪和障碍物碰撞后,会根据冲击力的大小来相应减去小猪的生命。
COLLISION_BIRD = 1
COLLISION_PIG = 2
COLLISION_BLOCK = 3
COLLISION_LINE = 4 def setup_collision_handler(self):
def post_solve_bird_line(arbiter, space, data):
if self.check_collide:
bird_shape = arbiter.shapes[0]
my_phy.handle_bird_collide(bird_shape, True)
def post_solve_pig_bird(arbiter, space, data):
if self.check_collide:
pig_shape = arbiter.shapes[0]
my_phy.handle_pig_collide(pig_shape, MAX_IMPULSE)
def post_solve_pig_line(arbiter, space, data):
if self.check_collide:
pig_shape = arbiter.shapes[0]
my_phy.handle_pig_collide(pig_shape, arbiter.total_impulse.length, True)
def post_solve_pig_block(arbiter, space, data):
if self.check_collide:
if arbiter.total_impulse.length > MIN_DAMAGE_IMPULSE:
pig_shape = arbiter.shapes[0]
my_phy.handle_pig_collide(pig_shape, arbiter.total_impulse.length)
def post_solve_block_bird(arbiter, space, data):
if self.check_collide:
block_shape, bird_shape = arbiter.shapes
my_phy.handle_bird_collide(bird_shape)
if arbiter.total_impulse.length > 1100:
my_phy.handle_block_collide(block_shape, arbiter.total_impulse.length) self.space.add_collision_handler(
COLLISION_BIRD, COLLISION_LINE).post_solve = post_solve_bird_line self.space.add_collision_handler(
COLLISION_PIG, COLLISION_BIRD).post_solve = post_solve_pig_bird self.space.add_collision_handler(
COLLISION_PIG, COLLISION_LINE).post_solve = post_solve_pig_line self.space.add_collision_handler(
COLLISION_PIG, COLLISION_BLOCK).post_solve = post_solve_pig_block self.space.add_collision_handler(
COLLISION_BLOCK, COLLISION_BIRD).post_solve = post_solve_block_bird def handle_pig_collide(self, pig_shape, impulse, is_ground=False):
for pig in self.pigs:
if pig_shape == pig.phy.shape:
if is_ground:
pig.phy.body.velocity = pig.phy.body.velocity * 0.8
else:
damage = impulse // MIN_DAMAGE_IMPULSE
pig.set_damage(damage) # must init as a global parameter to use in the post_solve handler
my_phy = Physics()
创建一个pymunk物体
创建物体一般有下面五个步骤
moment_for_circle
函数根根据质量和转动惯量来创建一个刚体(pymunk.Body)。
pymunk.moment_for_circle(mass, inner_radius, outer_radius, offset=(0, 0))
Calculate the moment of inertia for a hollow circle
inner_radius and outer_radius are the inner and outer diameters. (A solid circle has an inner diameter of 0)
据质量(mass), 圆的半径 来计算出刚体的转动惯量(Moment Of Inertia),惯量就像刚体的旋转质量。
class pymunk.Body(mass=0, moment=0, body_type=<class 'CP_BODY_TYPE_DYNAMIC'>)
根据刚体,和形状类型创建一个碰撞形状,比如圆形就是 pymunk.Circle。
class pymunk.Circle(body, radius, offset=(0, 0))Bases: pymunk.shapes.ShapeA circle shape defined by a radius
设置形状的一些属性
摩擦系数(friction)
Friction coefficient.
Pymunk uses the Coulomb friction model, a value of 0.0 is frictionless.
A value over 1.0 is perfectly fine.
弹力 (elasticity)
Elasticity of the shape.
A value of 0.0 gives no bounce, while a value of 1.0 will give a ‘perfect’ bounce.
最后将这个刚体和碰撞形状都添加到空间中。
pymunk.Space.add(*objs)
Add one or many shapes, bodies or joints to the space
class PhyPig():
def __init__(self, x, y, radius, space):
mass = 5
inertia = pm.moment_for_circle(mass, 0, radius, (0, 0))
body = pm.Body(mass, inertia)
body.position = x, y
shape = pm.Circle(body, radius, (0, 0))
shape.elasticity = 0.95
shape.friction = 1
shape.collision_type = COLLISION_PIG
space.add(body, shape)
self.body = body
self.shape = shape
PhyPig 类的初始化函数创建了一个小猪物体,参数有物体的位置(x,y), 可以将小猪作为一个圆形物体,所以参数有圆的半径(radius), 参数space就是我们上面创建的空间类。
pymunk 状态更新
update函数是更新函数,代码只显示了小猪相关的代码。
step 函数的参数dt值就是上面设置的时间段值,表示这次调用 该空间经过了多少时间,pymunk 根据这个时间值更新空间中的所有物体的状态(比如速度,位置等)。按照pymunk 文档的说明,将dt值设小一点,每次调用多次会使得模拟更稳定和精确,所以这里每次调用5次step函数。
pymunk.Space.step(dt)
Update the space for the given time step.
遍历所有的小猪:
- 检查小猪的状态,如果生命小于零或者y轴位置超出了范围,删除这个小猪。
- 更新小猪的位置
pygame 和 pymunk 中对于位置的值是不同的, y轴的坐标需要进行转换,具体看 to_pygame 函数,600是高度。pymunk 中 body.position的值是物体的中间位置,对应pygame 中 rect 的centerx 和 centery,所以需要转成[left, top]位置。
- pygame中,以左上角的位置为(0,0)
- pymunk中,以左下角的位置为(0,0)
def to_pygame(p):
"""Convert position of pymunk to position of pygame"""
return int(p.x), int(-p.y+600) def update(self, game_info, level, mouse_pressed):
pigs_to_remove = [] #From pymunk doc:Performing multiple calls with a smaller dt
# creates a more stable and accurate simulation
#So make five updates per frame for better stability
for x in range(5):
self.space.step(self.dt)
...
for pig in self.pigs:
pig.update(game_info)
if pig.phy.body.position.y < 0 or pig.life <= 0:
pigs_to_remove.append(pig)
poly = pig.phy.shape
p = to_pygame(poly.body.position)
x, y = p
w, h = pig.image.get_size()
# change to [left, top] position of pygame
x -= w * 0.5
y -= h * 0.5
angle_degree = math.degrees(poly.body.angle)
pig.update_position(x, y, angle_degree) for pig in pigs_to_remove:
self.space.remove(pig.phy.shape, pig.phy.shape.body)
self.pigs.remove(pig)
level.update_score(c.PIG_SCORE)
...
完善后的代码已打包成python教程,交流群:632408235 可到当中获取
编译环境
python3.7 + pygame1.9 + pymunk 5.5.0
Python 愤怒的小鸟代码实现:物理引擎pymunk使用的更多相关文章
- python下的Box2d物理引擎的配置
/******************************* I come back! 由于已经大四了,正在找工作 导致了至今以来第二长的时间内没有更新博客.向大家表示道歉 *********** ...
- Unity3D游戏开发初探—3.初步了解U3D物理引擎
一.什么是物理引擎? 四个世纪前,物理学家牛顿发现了万有引力,并延伸出三大牛顿定理,为之后的物理学界的发展奠定了强大的理论基础.牛顿有句话是这么说的:“如果说我看得比较远的话,那是因为我站在巨人的肩膀 ...
- Cocos2d-x3.2 使用物理引擎进行碰撞检测[转]
通常在游戏简单逻辑判断和模拟真实的物理世界时,我们只需要在定时器中判断游戏中各个精灵的条件是否满足判断条件就可以了.例如,在飞机大战中,判断我方子弹和敌机是否发生碰撞一般在定时器中通过敌机所在位置的矩 ...
- UIDynamic仿物理引擎-浮动碰撞效果-b
最近产品提了个需求(电商的APP-两鲜),需要在APP背景加上几个水果图案在那里无规则缓慢游荡...模仿 天天果园 APP的.好吧,那我就在网上找了很多文章,总结一下写个demo.效果如下: Mou ...
- Cocos2d-x3.2总结---使用物理引擎进行碰撞检测
[转自]: http://blog.csdn.net/cbbbc/article/details/38541099 通常在游戏简单逻辑判断和模拟真实的物理世界时,我们只需要在定时器中判断游戏中各个精灵 ...
- Cocos2d-x 使用物理引擎进行碰撞检测
[转自]: http://blog.csdn.net/cbbbc/article/details/38541099 通常在游戏简单逻辑判断和模拟真实的物理世界时,我们只需要在定时器中判断游戏中各个精灵 ...
- Bullet物理引擎的安装与使用
图形赋予游戏一种视觉的吸引力,但是能够让游戏的世界鲜活起来的还应该是内部的物理引擎.物理引擎是游戏引擎中的子模块,是一种软件组件,可仿真物理系统.它根据牛顿力学定律,计算游戏中物体的合理的物理位置,并 ...
- HTML5之2D物理引擎 Box2D for javascript Games 系列 翻外篇--如何结合createJS应用box2d.js
太久没有更新了,新年回来工作,突然有收到网友的邮件提问,居然还有人在关注,惭愧,找了下电脑上还有一点儿存着,顺便先发这一个番外篇吧,好歹可以看到真实的效果,等我考完英语,一定会更新下一章," ...
- HTML5之2D物理引擎 Box2D for javascript Games 系列 第一部分
我要的是能在H5页面上跑的javascript版的Box2D啊!!! 最近想学习Javascript版本的Box2D JS物理引擎,无奈搜了半天也没找到相对比较系统的资料 官方网站也只是简单的介绍,A ...
随机推荐
- uva 11665 Chinese Ink (几何+并查集)
UVA 11665 随便给12的找了一道我没做过的几何基础题.这题挺简单的,不过uva上通过率挺低,通过人数也不多. 题意是要求给出的若干多边形组成多少个联通块.做的时候要注意这题是不能用double ...
- oracle索引的操作
ORACLE对索引有两种访问模式. 索引唯一扫描 ( INDEX UNIQUE SCAN) 大多数情况下, 优化器通过WHERE子句访问INDEX. 例如: 表LODGING有两个索引 : 建立在LO ...
- 如何利用aop的环绕消息处理log, 以及各种坑的记录
如何利用aop的环绕消息处理log, 以及各种坑的记录 本文链接: https://www.cnblogs.com/zizaiwuyou/p/11667423.html 因为项目里有很多地方要打log ...
- CSS画矩形、圆、半圆、弧形、半圆、小三角、疑问框
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- React与Vue的相同与不同点
我们知道JavaScript是世界上最流行的语言之一,React和Vue是JS最流行的两个框架.所以要想前端的开发那么必须掌握好这两个框架. 那么这两个框架有什么不同呢? React 和 Vue 相同 ...
- centos linux ip地址无法连接数据库,ssh登录服务器时必须使用22端口
问题一:连接数据库时直接使用ip地址无法连接,必须使用ssh方式才能连接? 问题二:ssh登录服务器时必须使用22端口,在/etc/ssh/sshd_config中添加了10086端口,防火墙中已开启 ...
- Nuget 通过 dotnet 命令行发布
在开发完成一个好用的轮子就想将这个轮子发布到 nuget 让其他小伙伴可以来使用,但是 nuget.org 的登陆速度太慢,本文介绍一个命令行发布的方法,通过命令行发布的方法可以配合 Jenkins ...
- P1019 聪聪理扑克
题目描述 聪聪的两个小伙伴灵灵和豪豪喜欢打扑克,什么斗地主.德州.牛牛,他们都玩的有模有样. 但是每次玩好扑克他们都不整理一下,所以整理扑克的任务就交到了聪聪的手上. 已知现在桌面上有 n 张扑克牌, ...
- 备战省赛组队训练赛第十八场(UPC)
传送门 题解:by 青岛大学 A:https://blog.csdn.net/birdmanqin/article/details/89789424 B:https://blog.csdn.net/b ...
- 原生js添加鼠标事件的兼容性写法
兼容pc和移动端,还兼容了surface平板. surface平板特别坑,既可以用鼠标也能用触摸屏,也就是说同时有touch事件和mouse事件. function addEvent(_target, ...