这是python版的简易合成大西瓜游戏,但是目前没有结束游戏的设置。
需求:合成一个大西瓜后自动结束游戏并显示胜利;当水果到达顶部时显示游戏失败。
还有一个小小的问题,我的泼洒动画显现不出来是为什么呢?
from arcade import Sprite, SpriteList, Window, Sound
from arcade import set_background_color, start_render, draw_text, draw_line, run, load_texture, load_spritesheet
import pymunk
import random
import math
from PIL import Image
#定义窗口大小
SCREEN_WIDTH = 500
SCREEN_HEIGHT = 800
#定义标题
SCREEN_TITLE = "合成大西瓜"
MARGIN = 5
# 设置掉落水果的形状
class CircleSprite(Sprite):
def __init__(self, filename, shape):
super().__init__(filename, center_x=shape.body.position.x, center_y=shape.body.position.y)
self.width, self.height = shape.radius * 2, shape.radius * 2
self.shape = shape
# 随机掉落水果
class Fruit(CircleSprite):
def __init__(self, num, position, space, friction=0.1):
self.mass = num
self.image_url = 'images/fruit/' + str(num) + '.png' #通过self.image_url得到图片文件
self.radius = Image.open(self.image_url).size[0] / 4 #得到半径大小(将图片缩小一半显示)
moment = pymunk.moment_for_circle(self.mass, 0, self.radius * 0.9) #使用pymunk.moment_for_circle函数,得到相应质量和半径的圆形物体的动量
body = pymunk.Body(self.mass, moment) #创建一个具有这一动量和质量的刚体
body.position = position #设置刚体的位置
shape = pymunk.Circle(body, self.radius) #将刚体绑定到一个Circle形状上
shape.friction = friction #设置了形状的摩擦力
shape.collision_type = 1 #知道水果是1这个碰撞类型(collision_type)
shape.sprite = self
space.add(body, shape) #将刚体和它的形状都添加到虚拟的物理世界中
self.space = space
super().__init__(self.image_url, shape)
def __str__(self):
return str(self.shape.body.mass)
# 泼洒动画
class Splash(Sprite):
def __init__(self, texture_list):
super().__init__()
self.current_texture = 0
self.textures = texture_list
def update(self):
self.current_texture += 1
if self.current_texture < len(self.textures):
self.set_texture(self.current_texture)
else:
self.remove_from_sprite_lists()
#添加到游戏中
class MyGame(Window):
def __init__(self, width, height, title):
super().__init__(width, height, title)
set_background_color((250, 190, 100))
self.space = pymunk.Space()
self.space.gravity = (0.0, -900.0)
# 在MyGame的__init__里,调用make_static_line生成容器边框
self.container_lines = []
line = self.make_static_line(self.space, MARGIN, MARGIN, SCREEN_WIDTH - MARGIN, MARGIN)
self.container_lines.append(line)
line = self.make_static_line(self.space, MARGIN, MARGIN, MARGIN, SCREEN_HEIGHT - 100)
self.container_lines.append(line)
line = self.make_static_line(self.space, SCREEN_WIDTH - MARGIN, MARGIN, SCREEN_WIDTH - MARGIN,
SCREEN_HEIGHT - 100)
self.container_lines.append(line)
# 水平线(挡板)
self.hline = self.make_static_line(self.space, MARGIN, SCREEN_HEIGHT - 100, SCREEN_WIDTH - MARGIN,
SCREEN_HEIGHT - 100, radius=10)
# 用两个SpriteList来分别存放在上面可以控制的水果,和掉下去之后,不能再用鼠标控制的水果
self.fruits: SpriteList[CircleSprite] = SpriteList()
self.fruits_raw: SpriteList[CircleSprite] = SpriteList()
# 添加水果
self.new_fruit_x = SCREEN_WIDTH / 2
self.new_fruit(self.new_fruit_x)
# 新建self.count_max和 self.count 两个变量,指产生水果的时间周期,和当前的时间计数
self.count = 0
self.count_max = 120
self.score = 0
self.splash: SpriteList[Sprite] = SpriteList()
self.setup_collision_handler()
self.drop_sound = Sound('sounds/sound01.mp3') #掉落声
self.collision_sound = Sound('sounds/sound02.mp3') #合成声
#给水果设置可活动范围的边框
def make_static_line(self, space, x1, y1, x2, y2, radius=3.0, friction=0.5):
body = pymunk.Body(body_type=pymunk.Body.STATIC)
shape = pymunk.Segment(body, [x1, y1], [x2, y2], radius)
shape.friction = friction
shape.collision_type = 2
space.add(body, shape)
return shape
# 在on_draw()方法里,把每一个需要显示出来的东西都画出来
def on_draw(self):
start_render()
self.fruits.draw()
self.fruits_raw.draw()
self.splash.draw()
for f in self.fruits:
draw_text(str(f.mass), f.center_x, f.center_y, (255, 0, 0), 20)
for line in self.container_lines:
body = line.body
pv1 = body.position + line.a.rotated(body.angle)
pv2 = body.position + line.b.rotated(body.angle)
draw_line(pv1.x, pv1.y, pv2.x, pv2.y, (255, 255, 255), 10)
# 添加鼠标响应
def on_mouse_motion(self, x, y, dx, dy):
self.new_fruit_x = x
for fruit in self.fruits_raw:
fruit.shape.body.position = x if x > 1.5 * MARGIN and x < SCREEN_WIDTH - 1.5 * MARGIN else 1.5 * MARGIN if x < 1.5 * MARGIN else SCREEN_WIDTH - 1.5 * MARGIN if x > SCREEN_WIDTH - 1.5 * MARGIN else x, fruit.shape.body.position.y
# 定义鼠标松开事件
def on_mouse_release(self, x, y, button, modifiers):
if len(self.fruits_raw) > 0:
self.space.remove(self.hline)
sprite = self.fruits_raw.pop()
self.fruits.append(sprite)
# 调用了Fruit类构造新的水果,水果的质量随机选以,位置可以灵活设置,并且重组生成一个就添加到第一个可以控制的SpriteList中
# 新产生水果时,加上水平挡板
def new_fruit(self, x):
if self.hline not in self.space.shapes:
self.space.add(self.hline)
sprite = Fruit(random.choice([2, 4, 8, 16, 32, 64]), (x, SCREEN_HEIGHT), self.space, friction=0.3)
self.fruits_raw.append(sprite)
def on_update(self, delta_time):
self.splash.update()
self.space.step(1 / 60.0) #让space以delta_time的时间频率步进更新自己的数据
# 在上方的水果已经掉落的情况下,按on_update中设定的时间更新一次,
# 当self.count小于self.count_max时,self.count增加1,
# 当它第一次等于时,产生一个新水果,并且把self.count重置回0。
if len(self.fruits_raw) < 1:
if self.count < self.count_max:
self.count += 1
else:
self.new_fruit(self.new_fruit_x)
self.count = 0
#更新已经掉落和和上方未掉落的水果的位置和旋转角度
for fruit in self.fruits:
# 将滑到底部往下的水果,从空间中以及从当前SpriteList中都同时删除
if fruit.shape.body.position.y < 0:
self.space.remove(fruit.shape, fruit.shape.body)
fruit.remove_from_sprite_lists()
fruit.position = fruit.shape.body.position
fruit.angle = math.degrees(fruit.shape.body.angle)
for fruit in self.fruits_raw:
fruit.position = fruit.shape.body.position
fruit.angle = math.degrees(fruit.shape.body.angle)
# 碰撞
def setup_collision_handler(self):
# arbiter表示物理空间中碰撞时双方所有的shape和所有数据,所以可以用a, b = arbiter.shape来赋值
def cco_fruit_fruit(arbiter, space, data):
a, b = arbiter.shapes
# 如果刚才碰撞一方a绑定的body的质量与b绑定的body的质量相等的话,证明它们是同一类水果
if a.body.mass == b.body.mass:
sprite = Fruit(int(2 * a.body.mass), a.body.position, self.space, friction=0.3) #创建一个质量为原来2倍的水果
if a.sprite in self.fruits: #从self.fruits中删除a所对应的真实水果
self.fruits.remove(a.sprite)
if b.sprite in self.fruits: #从self.fruits中删除b所对应的真实水果
self.fruits.remove(b.sprite)
a.space.remove(a.body, a) #从空间中删除a, b形状及其body
b.space.remove(b.body, b)
self.fruits.append(sprite) #添加新的水果sprite
# 播放声音
self.collision_sound.play()
# 显示动画
texture_list = load_spritesheet('images/splash_pink/splash.png', 427, 313, 3, 3)
splash = Splash(texture_list)
splash.position = a.body.position
splash.update()
self.splash.append(splash)
return True
def cco_fruit_wall(arbiter, space, data):
a, b = arbiter.shapes
self.drop_sound.play()
return True
self.space.add_collision_handler(1, 1).begin = cco_fruit_fruit
self.space.add_collision_handler(1, 2).begin = cco_fruit_wall
if __name__ == "__main__":
MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
run()