python版合成大西瓜如何实现结束游戏?

这是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()