简介:
本游戏是基于2D引擎库pymunk的pygame小项目,将github的项目下载下来调通后做了些优化完成的。
图片音乐等资源来源于愤怒小鸟的游戏的资源文件。该游戏可以在论坛https://tieba.baidu.com/p/6492186070下载。
pymunk官网
Pymunk官网简介
(以下源于谷歌翻译的Pymunk简介)
您将在 Pymunk 中使用 4 个基本类。
刚体( pymunk.Body
)
刚体具有物体的物理特性。(质量、位置、旋转、速度等)它本身没有形状。如果您之前使用过粒子物理学,那么刚体的主要区别在于它们能够旋转。刚体通常与游戏中的精灵具有 1:1 的相关性。您应该构建您的游戏,以便使用刚体的位置和旋转来绘制精灵。
碰撞形状( pymunk.Circle
,pymunk.Segment
和pymunk.Poly
)
通过将形状附加到实体,您可以定义实体的形状。您可以将多个形状附加到单个实体以定义复杂的形状,或者如果不需要形状,则不附加任何形状。
约束/接头(pymunk.constraint.PinJoint
,pymunk.constraint.SimpleMotor
以及其他许多)
您可以在两个实体之间附加约束以约束它们的行为,例如保持两个实体之间的固定距离。
空间( pymunk.Space
)
空间是 Pymunk 中的基本模拟单元。您向空间添加实体、形状和约束,然后整体更新空间。它们控制所有刚体、形状和约束如何相互作用。
实际模拟由空间完成。将应该模拟的对象添加到时空后,该pymunk.Space.step()
功能会以小步向前移动 。
您在屏幕上看到的不一定与实际物理对象的形状完全相同。通常用于碰撞检测(和其他物理模拟)的形状是屏幕上绘制内容的简化版本。即使是高端 AAA 游戏也将碰撞形状与屏幕上绘制的形状分开。
有很多原因可以解释为什么将碰撞形状和绘制的内容分开是好的。
您可以在Pymunk 中包含的using_sprites.py示例中看到这样的示例。那里的物理形状是一个三角形,但绘制的是金字塔中的 3 个盒子,顶部有一条蛇。另一个示例是 platformer.py示例,其中玩家被绘制为红灰色女孩。然而,物理形状只是相互叠加的几个圆形。
有时,Pymunk 的用户可能会对所有东西的定义单位感到困惑。例如,物体的质量是克还是千克?Pymunk 是无单位的,不关心您使用哪个单位。如果您将秒传递给期望时间的函数,那么您的时间单位是秒。如果您将像素传递给需要距离的函数,那么您的距离单位就是像素。
那么派生单位只是上述的组合。因此,在秒和像素的情况下,速度单位将是像素/秒。
(这与其他一些物理引擎相反,它们可以具有您应该使用的固定单位)
接下来步入正题,怎么去调用Pymunk去完成这个小project?
首先要对这个游戏进行简单的分析:
然后对游戏中的流程进行简单分析
通过对流程分析后大概可以知道有这么一些模块
没有代码空谈这些是耍流氓的!!!
下面进入代码部分hhh
class Bird: def __init__(self, distance, angle, x, y, space): self.life = 20 # 生命值20 mass = 5 # 质量 5 radius = 12 # 刚力小圆圈的半径 12 inertia = pm.moment_for_circle(mass, 0, radius, (0, 0)) # 转动惯量 body = pm.Body(mass, inertia) # 初始化刚体 body.position = x, y # 鸟的位置 power = distance * 53 # 放大距离,优化参数增强游戏体验 impulse = power * Vec2d(1, 0) # 冲力 angle = -angle body.apply_impulse_at_local_point(impulse.rotated(angle)) # rotated 旋转向量 apply_impulse_at_local_point 在局部点施加脉冲 shape = pm.Circle(body, radius, (0, 0)) # 碰撞类型圆形好计算 shape.elasticity = 0.95 # 弹性 shape.friction = 0.9 # 摩擦力 shape.collision_type = 0 # 碰撞类型 space.add(body, shape) # 加到2维平面 self.body = body self.shape = shape
class Pig: def __init__(self, x, y, space): self.life = 20 mass = 5 radius = 14 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 = 0.9 shape.collision_type = 1 space.add(body, shape) self.body = body self.shape = shape
class Polygon: def __init__(self, pos, length, height, space, mass=5.0): moment = 1000 body = pm.Body(mass, moment) body.position = Vec2d(pos) shape = pm.Poly.create_box(body, (length, height)) shape.color = (0, 0, 255) shape.friction = 0.5 shape.collision_type = 2 space.add(body, shape) self.body = body self.shape = shape wood = pygame.image.load("E:/py/AngryBirds/images/wood.png").convert_alpha() wood2 = pygame.image.load("E:/py/AngryBirds/images/wood2.png").convert_alpha() rect = pygame.Rect(251, 357, 86, 22) self.beam_image = wood.subsurface(rect).copy() rect = pygame.Rect(16, 252, 22, 84) self.column_image = wood2.subsurface(rect).copy() def to_pygame(self, p): """Convert pymunk to pygame coordinates""" # pygame右边x轴,下边y轴 return int(p.x), int(-p.y+600) def draw_poly(self, element, screen): """Draw beams and columns""" # 梁横 柱竖 poly = self.shape ps = poly.get_vertices() ps.append(ps[0]) ps = map(self.to_pygame, ps) ps = list(ps) color = (255, 0, 0) pygame.draw.lines(screen, color, False, ps) if element == 'beams': p = poly.body.position p = Vec2d(self.to_pygame(p)) angle_degrees = math.degrees(poly.body.angle) + 180 rotated_logo_img = pygame.transform.rotate(self.beam_image, angle_degrees) offset = Vec2d(rotated_logo_img.get_size()) / 2. p = p - offset np = p screen.blit(rotated_logo_img, (np.x, np.y)) if element == 'columns': p = poly.body.position p = Vec2d(self.to_pygame(p)) angle_degrees = math.degrees(poly.body.angle) + 180 rotated_logo_img = pygame.transform.rotate(self.column_image, angle_degrees) offset = Vec2d(rotated_logo_img.get_size()) / 2. p = p - offset np = p screen.blit(rotated_logo_img, (np.x, np.y))
Tips:
注意,pygame和pymunk的坐标系不一样!!!
下图为pygame的坐标图(懒的自己画图了,体谅一下)
pymunk的y轴是向上的。
def post_solve_bird_pig(arbiter, space, _): """Collision between bird and pig""" surface = screen a, b = arbiter.shapes bird_body = a.body pig_body = b.body p = to_pygame(bird_body.position) p2 = to_pygame(pig_body.position) r = 30 pygame.draw.circle(surface, BLACK, p, r, 4) pygame.draw.circle(surface, RED, p2, r, 4) pigs_to_remove = [] for pig in pigs: if pig_body == pig.body: pig.life -= 20 pigs_to_remove.append(pig) global score score += 10000 for pig in pigs_to_remove: space.remove(pig.shape, pig.shape.body) pigs.remove(pig)
if (event.type == pygame.MOUSEBUTTONUP and event.button == 1 and mouse_pressed): # Release new bird mouse_pressed = False if level.number_of_birds > 0: song_fly = 'E:/py/AngryBirds/music/bird 01 flying.wav' pygame.mixer.music.load(song_fly) pygame.mixer.music.play(0) level.number_of_birds -= 1 t1 = time.time() * 1000 xo = 154 yo = 156 if mouse_distance > rope_length: mouse_distance = rope_length if x_mouse < sling_x + 5: bird = Bird(mouse_distance, angle, xo, yo, space) birds.append(bird) else: bird = Bird(-mouse_distance, angle, xo, yo, space) birds.append(bird) if level.number_of_birds == 0: t2 = time.time()
其余部分在代码中看(主要还是写的有点乱,粘出来不好看,滑稽)
完整代码下午上传到github上