一个飞机大战游戏

Pyxel Shooter 0.1


概述

一开始将原来官方的pyxel shooter 300多行的代码被硬生生写出了1500多行

发现有好多语句都写的差不多,写到1000多行的时候终于想起还有类继承这么一个方法,明明行为相同的类都可以继承自同一个父类,这样就可以减少重复代码的编写了,那样不仅方便省事而且又便于一起修改。后来也想到对于那些不断不断更新、重置的变量,是不是也可以写个reset()函数什么的来进行批量管理和修改呢?如果我之前就这样做了,估计就不会感到debug和修改代码那么头疼了。

这个游戏最初最开始的一步就是设计功能了(可能还有更先一步就是看懂原来的代码还有官方那一点API文档,大致知道它可以干什么,然后做功能的时候就知道大致要用到些什么东西)

设计的时候有考虑将具有相似行为的事物划分到一类,其实一开始也是根据这个创建文件及其类的,后来又应为要使用全局变量,就将它们都放到了一个文件里面。就是看到那个子弹使用了一个全局变量,bullet_list.append(self),然后自己做的时候发现做子弹这么做是非常省事的,于是就这么做了。但到了后来的后来,发现原来可以创建一个文件(_ 0_global.py),专门用来存放宏定义和全局变量(还以为python没有头文件是个缺陷),也就是最后还是拆分成了多个文件,当然这么做是有好处也有坏处的,好处就是方便找,坏处也是不方便找(一个文件下可以直接使用pycharm中的find来找东西,多个文件就不好找,但一个文件写的太长了也不会好找,虽然可以用structure)

然后设计的时候第设计的就是计时器,一开始以为是个难点,但发现只要使用time.time()就能做到(还以为要用多线程),关键是要保存上一次的时间(第一次调用time.time()的时候将其保存)。

然后主要分为三个大类:

  • 角色(Player)
    • 三个角色,一开始以为3个类,但其实这三个角色也没什么不同,就只是个技能不同而已,故通过一个技能标志来实现,只需要一个类。
  • 补给品(Recruit)
    • 看着好像有多处不同,退而选择了使用三个不同的类来实现之,后来改代码的时候发现一处改处处改很是麻烦,果然还是继承一个抽象一点的父类比较好。开发时从它们的每个简单功能代码开始的,因为有相似之处,感觉只要做好一个其它几个都能做好,故先开发了一个。
  • 敌人(Enemy)
    • 功能到后面有些改了,因为实现有点麻烦,但即使改也应该没降低其趣味性
    • 也没用父类,一个一个的写了类。后来又发现产生的子弹可以是敌人、产生的章鱼和产生子弹同理。感觉一开始的设计优点不到位啊。都说一开始设计结构式非常地重要的。体会到了,后面改起来真痛苦。

就功能设计而言,也是耗费了一些时间的,稍微考虑了一下游戏的平衡性(后来调的时候发现平衡性压根就不好),最重要的是趣味性

以下是一开的功能,但不是最终功能,有些功能给cut了,比如击败不同敌人加的分数不同,最终都是10分,boss1000分

"""
一、任务
  1.创建一个飞机大战游戏
-有个定时器记录游戏时间
-希望子弹有冷却、按住发射可以连射(定时器实现)
-补给品和技能都得配定时器,但说不定都可以通过第一个定时器来实现
-击败敌人获得分数

二、角色设计
-  3 个 角色
- shadow    技能幻影 身边出现两个幻影,幻影和自己的攻击模式相同,且无视碰撞,持续10s      能量条: 15
- laser     技能激光 蓄力1s,发射20发子弹威力的激光                               能量条: 20
- tempo     技能增速 速度提升为1.5倍,子弹发射间隔缩短为0.5倍,持续5s                 能量条: 10

三、补给品
-1. 增加发射子弹      15s(可叠加到3发,之后再捡时限延长)
-2. 暴走            15s(多捡延长时间, 玩家速度提升为2倍)
-3. 散弹            10s(子弹形态改变,由长方形变成圆形,碰撞到敌人和边缘将变成5发随机速度、方向的小发子弹,小子弹伤害与原子弹相同)
----- 每击毙一个敌人,获得1能量条。(后期可以调整)
四、敌人
-1、 小飞机                 只会向前动,随机发射子弹                            10分
-2、 迅速飞机                速度是子弹的1.5倍,向前移动,随机发射子弹              30分
-3、 小章鱼群                  成群出现的章鱼,可以跑到屏幕外,移动速度很快      一个15分
-4、 boss(大章鱼)         可以产生小章鱼群(血量为150发子弹)                 1000分
"""
  • 调整游戏平衡性的时候,发现一开始用了宏定义多好,那样就方便了。

  • 子弹类让我眼前一亮的是它在初始化的时候就将自己添加进了全局变量列表。这样一个好处就是,它本来被创建就是要添加进列表的,这样做省事,原来还有这种和全局变量联动的做法啊。我一开始也不知道子弹类的update和draw函数有什么用,明明学过gui却连其中一丁点的原理都没捞懂,其实这都是跟App里的循环事件挂钩的。App里的update和draw才会不断被调用,那bullet类中写update和draw干嘛呢,那当然是被App里的update和App里的draw调用啊,这样不就能将其加入事件循环了吗。update和draw的区别就是字面上的,update是用来更新要draw的变量的,draw就是普通的draw,可以很方便地通过update来实现动态的画面。然后,我又发现我设计的一个毛病了,一个子弹类就传入x(或者y方向)的速度,然后传入x:y的比值,我一开始认为这样很好,但后来就发现不行了,因为怎么都无法使子弹只往y(或者x)方向走,这就是传比例的坏处了,害果然传的东西还是原子一点比较好吧。

  • 然后也发现pyxel里面的一个帧(frame_count)的东西,这东西也可以做个不精确的定时器,一开始总在frame_count()后面加括号,然后就总报错说’int’ object is not callable,那当然了frame_count是个int类型怎么能当函数呢。

  • 然后发现这些动着的,会被破坏的东西都加了一个alive的标志位,似乎是清除的标志,并且用列表批量处理,真的很方便,特别是python中对列表的操作可是方便至极,原作者甚至还写了操作这些列表的函数update_list(list)、draw_list(list)等等,批量操作,简直爽的一批。后来我都将它们加入到了utils文件了,这是springboot学习时候学到的,果然还是得创一个工具类啊。但是最后还是没能把cleanup_list拿到里面去,应为它有一些更特殊的操作,不能离开主函数。

  • 类中多加一些工具函数也是有好处的,这样其它类调用它实例的时候,也不要一个个去改它内部的变量了,直接调用它的函数进行批量修改。

  • 然后我也发现自己写的逻辑判断实在是太长了,而且多个地方都用到了相似的逻辑判断,我觉得应该写那么个函数来处理这些共有的逻辑判断

  • 之前技能类很难关系上玩家类,然后我又想到了GUI设计的时候子窗口继承自父窗口,这样子窗口技能对父窗口进行操作了。于是我就在技能中的添加一个parent参数指向文件类,这样就可以操作玩家类了。但这样又导致玩家类要设置更多的标志变量,我想这两者中间可不可以在加一层,即再加一个类来关联两者,这样标志变量就不用放在玩家类了,我想后期优化的时候这么做,感觉这样会方便维护。

  • 使用Qt的时候发现他的一些事件枚举体之间使用了二进制或、与操作,这样来合并或者排斥对方,于是自己也想了一下,说不定可以使用十进制也这么做。比如:用个位0、1来标志模式一的开与关,十位0、1来标志模式二的开与关,于是11就表示模式一和模式二都开、10就表示模式二开而模式一关之类的,这东西在我做技能的时候稍微有用到。

  • 我之前发现技能居然可以一直按,原来是没有与能量条关联,要设置一个条件(开关)使其什么时候可以开、什么时候可以关。还可以在类中设置一个操纵开关的函数,这样有时可以有效减少逻辑判断的代码, 而且对解决别的问题可能有帮助。特别使如果只在类中创建了draw、update函数,意味着这两个函数中的代码都会不停被执行,偶尔还是希望它停一停是吧。

  • 其实我还希望能有可传可变参数的函数,这样就不必写的如下面这么麻烦了,有时间在改吧

    • list_process.update_list(bullet_list)
      list_process.update_list(enemy_list)
      list_process.update_list(blast_list)
      list_process.update_list(boss_list)
      list_process.update_list(recruit_list)
      
      cleanup_list(enemy_list)
      cleanup_list(bullet_list)
      cleanup_list(blast_list)
      cleanup_list(boss_list)
      cleanup_list(recruit_list)
      
  • 总是觉得标志开关的开与关很麻烦还经常容易漏,得思考一下该怎么办

  • 正式游戏设计的时候还考虑什么时候用全局变量比较好,一开始很犹豫,不想使用全局变量,后来发现还是全局变量方便,当然感觉也可以写个类来存这些变量,但这样修改和读取都会更加的复杂。所以一开始还是不要犹豫,大胆地去使用全局变量吧。

  • 我又发现说不定一开始不用先实现什么功能,先画上去再说,先实现draw,再来调整update和其它逻辑说不定是个好方法。

代码

需要同目录下文件夹(文件夹名为assets)下的图片:

boss1.png、boss2.png、boss3.png(都是28x32的)

noguchi_tileset_128x128.png(官方自带的图片)

copyplayer.png(幻影的图片,8x8)

skill3_1.png、skill3_2.png、skill3_3.png(2x8)

其实上面除官方的图片都是我自己做的,用的是Pyxel Edit,网上有,随便下个就行吧

_0_global.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-
from random import seed, random, randrange, uniform
import pyxel
import time

seed(time.time())       # 使用随机种子
# 宏定义
# scene
SCENE_TITLE = 0
SCENE_PLAY = 1
SCENE_GAMEOVER = 2
SCENE_WIN = 3
# 屏幕大小
SCREEN_WIDTH = 120
SCREEN_HEIGHT = 160
# star
STAR_COUNT = 100
STAR_COLOR_HIGH = 12
STAR_COLOR_LOW = 5
# player
PLAYER_WIDTH = 8
PLAYER_HEIGHT = 8
PLAYER_SPEED = 2
# boss
BOSS_WIDTH = 28
BOSS_HEIGHT = 32
BOSS_BLOOD = 400
BOSS_ANGER = 150
BOSS_COME_TIME = 80
# 普通子弹
BULLET_WIDTH = 2
BULLET_HEIGHT = 8
BULLET_COLOR = 11
BULLET_SPEED = 4
# 榴弹
SHRAPNEL_COLOR = 7
SHRAPNEL_RADIUS = 3
LITTLE_BULLET_RADIUS = 1
LITTLE_BULLET_RADIUS_ALTER = 2
# 飞碟和章鱼
ENEMY_WIDTH = 8
ENEMY_HEIGHT = 8
ENEMY_SPEED = 1.5
# 爆炸
BLAST_START_RADIUS = 1
BLAST_END_RADIUS = 8
BLAST_COLOR_IN = 7
BLAST_COLOR_OUT = 10
# 激光
LASER_COLOR = 3

# 要画的和要更新的元素列表,作为全局变量使用
enemy_list = []
bullet_list = []
blast_list = []
boss_list = []
little_bullet_list = []
recruit_list = []
# 记录玩家当前坐标,敌人发射的子弹将根据此进行调整
player_pos = [60, 150]
# 可被更改的开始时间
start_time = 0

_1_bullet.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

from _0_global import *

# 普通子弹类
class Bullet:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.w = BULLET_WIDTH
        self.h = BULLET_HEIGHT
        self.alive = True

        bullet_list.append(self)

    def update(self):
        self.y -= BULLET_SPEED

        if self.y + self.h - 1 < 0:
            self.alive = False

    def draw(self):
        pyxel.rect(self.x, self.y, self.w, self.h, BULLET_COLOR)


# littelBullet类
class littleBullet:
    def __init__(self, x, y, direction, speed, radius):  # direction是纵速比横速,speed是横向速度,纵向速度 = speed * direction
        self.x = x
        self.y = y
        self.xspeed = speed
        self.yspeed = self.xspeed * direction
        self.radius = radius
        self.w = self.h = self.radius * 2
        self.alive = True

        bullet_list.append(self)

    def update(self):
        self.y += self.yspeed
        self.x += self.xspeed

        # 超出范围后死亡
        if self.y + 2 * self.radius - 1 < 0:
            self.alive = False
        if self.x + 2 * self.radius - 1 < 0 or self.x - 2 * self.radius + 1 > 120:
            self.alive = False

    def draw(self):
        pyxel.circ(self.x+self.radius, self.y+self.radius, self.radius, randrange(7, 12))

# Sharpnel类
class Shrapnel:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.radius = SHRAPNEL_RADIUS
        self.w = self.h = self.radius * 2
        self.alive = True

        bullet_list.append(self)

    def update(self):
        self.y -= BULLET_SPEED

        # 超出范围后死亡
        if self.y - 1 < 0:
            self.alive = False

        if self.alive is False and self.y -1 < 0:
            for i in range(0, randrange(5, 8)):
                # print(1)
                temp = uniform(-BULLET_SPEED * 1.5, BULLET_SPEED * 1.5)
                littleBullet(self.x, self.y, uniform(0, 2) if temp > 0 else -uniform(0, 2),
                                                        temp, LITTLE_BULLET_RADIUS)
        elif self.alive is False:
            for i in range(0, randrange(5, 8)):
                littleBullet(self.x, self.y, uniform(-0.8, 0.8),
                                                        uniform(-BULLET_SPEED * 1.5, BULLET_SPEED * 1.5), LITTLE_BULLET_RADIUS)

    def draw(self):
        pyxel.circ(self.x+self.radius, self.y+self.radius, self.radius, SHRAPNEL_COLOR)

_2_enemy.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

from _0_global import *

class GenralEnemy:
    def __init__(self):
        pass

class Octopus(GenralEnemy):
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.w = ENEMY_WIDTH
        self.h = ENEMY_HEIGHT
        self.dir = 1
        self.alive = True
        self.offset = int(random() * 60)
        enemy_list.append(self)

    def update(self):
        if (pyxel.frame_count + self.offset) % 60 < 30:
            self.x += ENEMY_SPEED
            self.dir = 1
        else:
            self.x -= ENEMY_SPEED
            self.dir = -1

        self.y += ENEMY_SPEED

        if self.y > pyxel.height - 1:
            self.alive = False
    # 这个画应该是不停的画,传入坐标和框框大小
    def draw(self):
        pyxel.blt(self.x, self.y, 0, 8, 0, self.w * self.dir, self.h, 0)

# 先不考虑圆盘发射子弹
class Enemy(GenralEnemy):
    def __init__(self, x, y, speed):
        self.x = x
        self.y = y
        self.w = ENEMY_WIDTH
        self.h = ENEMY_HEIGHT
        self.speed = speed
        self.alive = True
        self.shoot = False
        self.shoot_time = -1

        enemy_list.append(self)

    def update(self):
        self.y += self.speed
        if self.y > pyxel.height - 1:
            self.alive = False

        if self.shoot is False:
            self.shoot = True if randrange(0, 1000) < 5 else False

        if self.shoot is True and self.shoot_time == -1:
            EnemyCircle(self.x+self.w/2, self.y+self.h, ENEMY_SPEED-1)
            self.shoot_time = pyxel.frame_count % 25

        elif self.shoot_time > 0 and self.shoot_time ==pyxel.frame_count % 25 and self.shoot is True:
            self.shoot_time = -1
            self.shoot = False

    # 这个画应该是不停的画,传入坐标和框框大小
    def draw(self):
        if self.shoot is False:
            pyxel.blt(self.x, self.y, 1, 8, 48, self.w, self.h, 0)
        else:
            pyxel.blt(self.x, self.y, 1, 0, 48, self.w, self.h, 0)


class EnemyCircle(GenralEnemy):
    def __init__(self, x, y, speed):
        self.x = x
        self.y = y
        self.w = ENEMY_WIDTH
        self.h = ENEMY_HEIGHT
        self.speed = speed
        self.dir = player_pos
        self.xspeed = self.speed if player_pos[0]-self.x > 0 else -self.speed
        self.yspeed = -self.xspeed * (player_pos[1] - self.y) / (player_pos[0] - self.x) if abs(player_pos[0] - self.x) > 1.5 else 0
        self.alive = True

        enemy_list.append(self)

    def update(self):
        self.y -= self.yspeed
        self.x += self.xspeed

        if self.y > pyxel.height - 1:
            self.alive = False

    # 这个画应该是不停的画,传入坐标和框框大小
    def draw(self):
        pyxel.blt(self.x, self.y, 1, 40, 32, self.w, self.h, 0)


# 移动先算了
class Boss(GenralEnemy):  # 400发子弹的血量
    def __init__(self):
        self.blood = BOSS_BLOOD
        self.x = 46
        self.y = 0
        self.w = BOSS_WIDTH
        self.h = BOSS_HEIGHT
        self.alive = True
        self.pic_pos = [[0, 128], [28, 128], [56, 128]]
        self.index = 0
        self.anger = BOSS_ANGER
        self.angry = False
        self.duration_start = 0
        self.draw_flag = False

        boss_list.append(self)

    def update(self):
        if self.blood <= 0:
            self.alive = False
        if pyxel.frame_count % 20 == 0:
            if randrange(0, 3) > 0:     # 2/3 几率出怪
                self.index = (self.index + 1) % 3
                for i in range(randrange(4, 7)):
                    Octopus(self.x + self.w / 2+(i-3)*8,self.y+self.h+1)

        # 血量小于 150 愤怒模式
        if 0 < self.blood <= BOSS_ANGER and self.draw_flag is False and self.duration_start == 0:
            self.angry = True
            self.duration_start = time.time()
            self.draw_flag = True

        if self.angry is True:
            if pyxel.frame_count % 30 == 0:
                if randrange(0, 3) > 0:
                    EnemyCircle(self.x + self.w / 2,self.y, BULLET_SPEED)

        if time.time() - self.duration_start >= 10:
            self.draw_flag = False

    def draw(self):
        # 只要想办法改变这其中的参数就能实现图片的切换,也就能实现动画
        # 总之下面的是画出boss来
        pyxel.blt(self.x, self.y, 1, self.pic_pos[self.index][0], self.pic_pos[self.index][1], self.w, self.h, 0)
        if self.draw_flag:
            pyxel.text(self.x + self.w / 2 + randrange(-1, 2), self.y + self.h/4, "Angery", 8)

_3_mytimer.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import pyxel
import time

class myTimer:
    def __init__(self,start_time):
        self.current = 0
        self.start_time = start_time

    def reset(self):
        self.current = 0
        self.start_time = time.time()

# update说不定是在draw之前,也就是说要将draw的变量焕然一新
    def update(self):
        self.current = int(time.time() - self.start_time)
		
    def draw(self):
      	#  :2 是两个空格
        pyxel.text(120 - 45, 10, "Time {:2}".format(self.current), 7)

_4_player.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

from _8_skill import *
class Player:
    def __init__(self, x, y, default_skill, pb):         # defaultSkill标志了到底是哪一种飞机0、1、2
        self.x = x
        self.y = y
        self.w = PLAYER_WIDTH
        self.h = PLAYER_HEIGHT
        self.alive = True
        self.speed = PLAYER_SPEED
        self.bullet_delay = 0.25  # 子弹发射间隔为0.75秒
        self.last_bullet_time = 0  # 记录上次发射子弹的时间
        self.attack_mode = 0
        self.default_skill = default_skill
        self.status = [0, rtskill1(self), rtskill2(self), rtskill3(self)]
        # self.status[3].aliveTime=10
        self.pb = pb
        self.last_shoot_time = 0
        if self.default_skill == 0:
            self.status[0] = skill1(self)
        elif self.default_skill == 1:
            self.status[0] = skill2(self)
        elif self.default_skill == 2:
            self.status[0] = skill3(self)

    def update(self):
        if pyxel.btn(pyxel.KEY_A) or pyxel.btn(pyxel.KEY_LEFT):
            self.x -= self.speed

        if pyxel.btn(pyxel.KEY_D) or pyxel.btn(pyxel.KEY_RIGHT):
            self.x += self.speed

        if pyxel.btn(pyxel.KEY_W)or pyxel.btn(pyxel.KEY_UP):
            self.y -= self.speed

        if pyxel.btn(pyxel.KEY_S) or pyxel.btn(pyxel.KEY_DOWN):
            self.y += self.speed

        if pyxel.btnp(pyxel.KEY_K):       # K 开启技能
            print(self.status[0])
            self.status[0].release()

        # 不能超出矩形框
        self.x = max(self.x, 0)
        self.x = min(self.x, pyxel.width - self.w)
        self.y = max(self.y, 0)
        self.y = min(self.y, pyxel.height - self.h)

        if pyxel.btn(pyxel.KEY_J) and time.time() - self.last_shoot_time >= self.bullet_delay:
            self.last_shoot_time = time.time()
            print(self.attack_mode)
            if self.attack_mode < 10:
                if self.attack_mode == 0:
                    Bullet(
                        self.x + (PLAYER_WIDTH - BULLET_WIDTH) / 2, self.y - BULLET_HEIGHT / 2
                    )
                    # 播放声音
                    pyxel.play(0, 0)

                elif self.attack_mode == 1:
                    Bullet(
                        self.x + 1, self.y - BULLET_HEIGHT / 2
                    )
                    Bullet(
                        self.x + PLAYER_WIDTH - 3, self.y - BULLET_HEIGHT / 2
                    )
                    pyxel.play(0, 0)
            else:
                if self.attack_mode == 10:
                    Shrapnel(
                        self.x + (PLAYER_WIDTH - SHRAPNEL_RADIUS * 2) / 2, self.y - SHRAPNEL_RADIUS
                    )
                    # 播放声音
                    pyxel.play(0, 0)

                elif self.attack_mode == 11:
                    Shrapnel(
                        self.x - 3, self.y - SHRAPNEL_RADIUS
                    )
                    Shrapnel(
                        self.x + PLAYER_WIDTH - 3, self.y - SHRAPNEL_RADIUS
                    )
                    pyxel.play(0, 0)
                elif self.attack_mode == 20:
                    for i in range(0, randrange(8, 12)):
                        temp = uniform(-BULLET_SPEED * 1.5, BULLET_SPEED * 1.5)
                        s = uniform(0, 2)
                        if -0.5 < temp < 0.5:
                            s = 10
                        littleBullet(self.x, self.y, -s if temp > 0 else s,
                                     temp, LITTLE_BULLET_RADIUS_ALTER)
                        pyxel.play(0, 0)
                elif self.attack_mode == 21:
                    for i in range(0, randrange(16, 24)):
                        temp = uniform(-BULLET_SPEED * 1.5, BULLET_SPEED * 1.5)
                        s = uniform(0, 2)
                        if -0.5 < temp < 0.5:
                            s = 10
                        littleBullet(self.x, self.y, -s if temp > 0 else s,
                                     temp, LITTLE_BULLET_RADIUS_ALTER)
                        pyxel.play(0, 0)

            player_pos[0] = self.x + self.w / 2
            player_pos[1] = self.y + self.h / 2

    def draw(self):
        pyxel.blt(self.x, self.y, 0, 0, 0, self.w, self.h, 0)

_5_powerbar.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

powerBar_width = 20
powerBar_height = 5
powerBar_totalColor = 1
powerBar_currentColor = 10

import pyxel
class powerBar:
    def __init__(self, total):
        # if curren 等于 total 才能释放技能
        self.total = total
        self.current = total
        self.w = powerBar_width
        self.h = powerBar_height
        # self.parent = parent
        self.cw = self.w
        self.skillbar = True

    def set(self, t, c):
        self.total = t
        self.current = c

    def update(self):
        self.cw = int(self.w * self.current / self.total)

        # 稍微检错
        if self.current < 0:
            self.current = 0
        if self.current > self.total:
            self.current = self.total

    def draw(self):
        pyxel.rect(3, 3, self.w, self.h, powerBar_totalColor)
        pyxel.rect(3, 3, self.cw, self.h, powerBar_currentColor-1 if self.current == self.total and pyxel.frame_count % 72 > 36 else powerBar_currentColor)

_6_recruit.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

from _0_global import *
from _9_utils import *

class Recruit:
    def __init__(self, x, number, direction, speed): # speed 是 y的speed
        self.x = x
        self.y = 0
        self.alive = True
        self.yspeed = speed
        self.xspeed = speed * direction
        self.collisionsNum = 5              # 碰撞5次消失
        self.w = PLAYER_WIDTH
        self.h = PLAYER_HEIGHT
        self.number = number
        if self.number == 0:
            self.str = "A"
        elif self.number == 1:
            self.str = "B"
        else:
            self.str = "C"
        recruit_list.append(self)

    def update(self):
        self.y += self.yspeed
        self.x += self.xspeed
        if self.collisionsNum != 0:
            if edgeCheck(120, 160, self, 1):
                self.collisionsNum -= 1
            if self.x + 0.5 < 0 or self.x + self.w - 0.5 > 120:
                self.xspeed = - self.xspeed
            if self.y + 0.5 < 0 or self.y - 0.5 + self.h > 160:
                self.yspeed = - self.yspeed
        if self.collisionsNum == 0:
            self.alive = False

    def draw(self):
        pyxel.rect(self.x, self.y, self.w, self.h, 0)
        pyxel.text(self.x + 3, self.y + 3, self.str, 3 if pyxel.frame_count % 72 > 36 else 4)

_7_scoreboard.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import pyxel

class ScoreBoard:
    def __init__(self):
        self.current = 0
        self.parent = 0
        # self.addType = [10, 30, 15, 2000]

    def setParent(self, parent):
        self.parent = parent

    # 要先调用setParent才能调用update
    def update(self):
        self.current = self.parent.score

    def draw(self):
        # :8是指8个空格
        pyxel.text(120 - 45, 4, "SCORE {0}".format(self.current), 7)

_8_skill.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

# 1、释放
# 2、和powerBar拉钩
# 3、和玩家有关(玩家的形态改变、属性改变)

from _0_global import *
from _1_bullet import Bullet, Shrapnel, littleBullet

# 幻影
class skill1:
    def __init__(self, parent):
        self.limitTime = 10
        self.aliveTime = 0  # 技能好了要重置
        self.flag = False                # 技能开关,只有开了才会减
        self.parent = parent
        self.p1 = 0
        self.p2 = 0
        self.start_time = -1

    def release(self):
        if self.parent.pb.current == self.parent.pb.total:
            self.aliveTime = self.limitTime
            self.parent.pb.skillbar = True

    def update(self):
        if self.aliveTime == self.limitTime and self.flag is False: #表示第一次启动
            print(1)
            self.flag = True
            self.p1 = PlayerCopy(-self.parent.w - 2, self.parent)  # 左边的
            self.p2 = PlayerCopy(self.parent.w + 2, self.parent)  # 右边的
            self.start_time = time.time()

        if self.aliveTime > 0 and self.flag is True:
            self.p1.update()
            self.p2.update()
            self.aliveTime = self.limitTime - (time.time() - self.start_time)
            self.parent.pb.current = self.aliveTime

        # 小于是一种粗略的错误检验
        if self.aliveTime <= 0 and self.flag is True:     # 通过main函数让aliveTime减小的
            self.aliveTime = 0
            self.flag = False
            self.parent.pb.skillbar = False
            del self.p1
            del self.p2

    def draw(self):
        if self.aliveTime > 0 and self.flag is True:
            self.p1.draw()
            self.p2.draw()

# 激光类
class Laser:
    def __init__(self, parent):
        self.parent = parent
        self.alive = False
        self.w = self.parent.parent.w
        self.h = self.parent.parent.y - 3
        self.x = self.parent.parent.x
        self.y = 0
        self.flag = False

    def update(self):
        if self.parent.aliveTime > 0 and self.flag is False:
            self.alive = True
            self.flag = True
            bullet_list.append(self)

        if self.parent.aliveTime > 0 and self.flag is True:
            self.x = self.parent.parent.x
            self.y = 0

        if self.parent.aliveTime <= 0:
            self.parent.aliveTime = 0
            self.alive = False
            self.flag = False

    def draw(self):
        if self.parent.aliveTime > 0 and self.alive is True:
            if self.parent.parent.y - 3 > 0:
                pyxel.rect(self.parent.parent.x-randrange(0, 2), 0, self.parent.parent.w-randrange(0, 2), self.parent.parent.y - 3,
                           LASER_COLOR)
# 激光
# 发射激光时捡了暴走无效
class skill2:
    def __init__(self, parent):
        self.limitTime = 1
        self.aliveTime = 0  # 技能好了要重置
        self.flag = False  # 技能开关,只有开了才会减
        self.parent = parent
        # self.temp = 0
        self.Laser = Laser(self)
        self.start_time = -1

    def release(self):
        if self.parent.pb.current == self.parent.pb.total:
            self.aliveTime = self.limitTime
            self.parent.pb.skillbar = True
            self.flag = True
            self.parent.pb.current = self.aliveTime / self.limitTime * 20
            self.start_time = time.time()
            # self.parent.speed, self.temp = self.temp, self.parent.speed

    def update(self):
        if self.aliveTime > 0 and self.flag is True:
            self.aliveTime = self.limitTime - (time.time() - self.start_time)
            self.parent.pb.current = self.aliveTime / self.limitTime * 20
            self.Laser.update()

        # 小于是一种粗略的错误检验
        if self.aliveTime <= 0 and self.flag is True:     # 通过main函数让aliveTime减小的
            self.aliveTime = 0
            self.flag = False
            self.start_time = -1
            # self.parent.speed, self.temp = self.temp, self.parent.speed
            self.parent.pb.skillbar = False

    def draw(self):
        self.Laser.draw()

# 敏捷
class skill3:
    def __init__(self, parent):
        self.limitTime = 3
        self.aliveTime = 0  # 技能好了要重置
        self.flag = False                # 技能开关,只有开了才会减
        self.parent = parent
        self.temp = [0, 0]
        self.start_time = -1
        self.now_pic = 0

    def release(self):
        if self.parent.pb.current == self.parent.pb.total:
            self.aliveTime = self.limitTime
            self.parent.pb.skillbar = True
            self.flag = True
            self.parent.pb.current = self.aliveTime
            self.start_time = time.time()

            self.temp[0] = self.parent.speed
            self.temp[1] = self.parent.bullet_delay
            self.parent.speed = self.temp[0] * 2.5
            self.parent.bullet_delay = self.temp[1] * 0.1

    def update(self):
        if self.aliveTime > 0 and self.flag is True:
            self.aliveTime = self.limitTime - (time.time() - self.start_time)
            self.parent.pb.current = self.aliveTime
            self.temp[0] = self.parent.speed / 2.5       #  与暴走保持一致

        # 小于是一种粗略的错误检验
        if self.aliveTime <= 0 and self.flag is True:     # 通过main函数让aliveTime减小的
            self.aliveTime = 0
            self.flag = False
            self.start_time = -1
            self.parent.speed = self.temp[0]
            self.parent.bullet_delay = self.temp[1]
            self.parent.pb.skillbar = False

    def draw(self):
        if self.aliveTime > 0:
            if pyxel.frame_count % 24 < 8:
                self.now_pic = 84
            elif 8<=pyxel.frame_count % 24<16:
                self.now_pic = 86
            else:
                self.now_pic = 88
            pyxel.blt(self.parent.x + self.parent.w-2, self.parent.y+self.parent.h, 1,self.now_pic, 128, 2, 8)
            pyxel.blt(self.parent.x , self.parent.y+self.parent.h, 1,self.now_pic, 128, 2, 8)

# 子弹数加1
class rtskill1:
    def __init__(self, parent):
        self.limitTime = 15
        self.aliveTime = 0              # 技能好了要重置
        self.flag = False                # 技能开关,只有开了才会减
        self.parent = parent            # 想要改变parent中的mode
        self.start_time = 0
        self.total_time = 0             # 之前的limitTime就相当于时total_time

    def release(self):
        if self.parent.attack_mode % 10 < 1:  # 0、1两种种模式。(第一次捡)
            self.start_time = time.time()
            self.parent.attack_mode += 1
            self.aliveTime += self.limitTime
        else:
            self.aliveTime += self.limitTime
            self.start_time = time.time()

        self.flag = True
        self.total_time = self.aliveTime

    def update(self):
        # 开启之前
        if self.flag and self.aliveTime > 0:
            self.aliveTime = self.total_time - (time.time() - self.start_time)

        # 小于是一种粗略的错误检验
        if self.aliveTime <= 0 and self.flag is True:
            self.aliveTime = 0
            self.flag = False
            self.total_time = 0
            self.parent.attack_mode -= 1            # 回归0
            if self.parent.attack_mode < 0:
                self.parent.attack_mode = 0

    def draw(self):
        pass
# 暴走
class rtskill2:
    def __init__(self, parent):
        self.limitTime = 15
        self.aliveTime = 0  # 技能好了要重置
        self.flag = False  # 技能开关,只有开了才会减
        self.turnon = False
        self.parent = parent  # 想要改变parent中的mode
        self.temp = [0]
        self.total_time = 0
        self.start_time = 0

    def release(self):
        if self.turnon is False:  # (第一次捡)
            self.turnon = True
            self.start_time = time.time()
            self.aliveTime += self.limitTime
            self.temp[0] = self.parent.speed
            self.parent.speed = self.temp[0] * 2
            print(self.parent.speed)
        else:
            self.aliveTime += self.limitTime
            self.start_time = time.time()

        self.flag = True
        self.total_time = self.aliveTime

    def update(self):
        if self.flag and self.turnon and self.aliveTime > 0:
            self.temp[0] = self.parent.speed / 2  # 与其保持一致,防止暴走与敏捷冲突
            self.aliveTime = self.total_time - (time.time() - self.start_time)
            print(self.temp[0], self.parent.speed)

        # 小于是一种粗略的错误检验
        if self.aliveTime <= 0 and self.flag:
            self.aliveTime = 0
            self.flag = False
            self.turnon = False
            self.parent.speed = self.temp[0]
            print(self.parent.speed)
            self.total_time = 0

    def draw(self):
        pass
#榴弹
class rtskill3:
    def __init__(self, parent):
        self.limitTime = 10
        self.aliveTime = 0  # 技能好了要重置
        self.flag = False  # 技能开关,只有开了才会减
        self.parent = parent  # 想要改变parent中的mode
        self.start_time = 0
        self.total_time = 0

    def release(self):
        if (self.parent.attack_mode / 10) % 10 < 2:  # 0、1两种种模式。(第一次捡)
            self.parent.attack_mode += 10
            self.aliveTime += self.limitTime
            self.start_time = time.time()
        else:
            self.aliveTime += self.limitTime
            self.start_time = time.time()
        self.flag = True
        self.total_time = self.aliveTime

    def update(self):
        # 中间过程
        if self.flag and self.aliveTime > 0:
            self.aliveTime = self.total_time - (time.time() - self.start_time)
            print(self.aliveTime)

        # 小于是一种粗略的错误检验
        if self.aliveTime <= 0 and self.flag:
            self.aliveTime = 0
            self.total_time = 0
            self.flag = False
            self.parent.attack_mode -= (10 * ((self.parent.attack_mode / 10) % 10))
            if self.parent.attack_mode < 0:
                self.parent.attack_mode = 0
            print(self.parent.attack_mode)

    def draw(self):
        pass

class PlayerCopy:
    def __init__(self, shift, parent):         # shift相对偏移量
        self.parent = parent
        self.x = self.parent.x + shift              # 使用偏移量
        self.y = self.parent.y
        self.w = self.parent.w
        self.h = self.parent.h / 2
        self.alive = self.parent.alive
        self.speed = self.parent.speed
        self.bullet_delay = self.parent.bullet_delay  # 子弹发射间隔为0.75秒
        self.last_bullet_time = self.parent.last_bullet_time  # 记录上次发射子弹的时间
        self.attack_mode = self.parent.attack_mode
        self.status = self.parent.status  # 本体
        self.shift = shift          # 偏移
        self.last_shoot_time = self.parent.last_shoot_time

    def update(self):
        self.x = self.parent.x + self.shift
        self.y = self.parent.y
        self.alive = self.parent.alive
        self.speed = self.parent.speed
        self.bullet_delay = self.parent.bullet_delay  # 子弹发射间隔为0.25秒
        self.last_bullet_time = self.parent.last_bullet_time  # 记录上次发射子弹的时间
        self.attack_mode = self.parent.attack_mode
        self.status = self.parent.status

        if pyxel.btn(pyxel.KEY_J) and time.time() - self.last_shoot_time >= self.bullet_delay:
            self.last_shoot_time = time.time()
            if self.attack_mode < 10:
                if self.attack_mode == 0:
                    Bullet(
                        self.x + (PLAYER_WIDTH - BULLET_WIDTH) / 2, self.y - BULLET_HEIGHT / 2
                    )

                elif self.attack_mode == 1:
                    Bullet(
                        self.x + 1, self.y - BULLET_HEIGHT / 2
                    )
                    Bullet(
                        self.x + PLAYER_WIDTH - 3, self.y - BULLET_HEIGHT / 2
                    )

            else:
                if self.attack_mode == 10:
                    Shrapnel(
                        self.x + (PLAYER_WIDTH - SHRAPNEL_RADIUS * 2) / 2, self.y - SHRAPNEL_RADIUS
                    )
                    # 播放声音

                elif self.attack_mode == 11:
                    Shrapnel(
                        self.x - 3, self.y - SHRAPNEL_RADIUS
                    )
                    Shrapnel(
                        self.x + PLAYER_WIDTH - 3, self.y - SHRAPNEL_RADIUS
                    )
                elif self.attack_mode == 20:
                    for i in range(0, randrange(8, 12)):
                        temp = uniform(-BULLET_SPEED * 1.5, BULLET_SPEED * 1.5)
                        s = uniform(0, 2)
                        if -0.5 < temp < 0.5:
                            s = 10
                        littleBullet(self.x, self.y, -s if temp > 0 else s,
                                     temp, LITTLE_BULLET_RADIUS_ALTER)
                        pyxel.play(0, 0)
                elif self.attack_mode == 21:
                    for i in range(0, randrange(16, 24)):
                        temp = uniform(-BULLET_SPEED * 1.5, BULLET_SPEED * 1.5)
                        s = uniform(0, 2)
                        if -0.5 < temp < 0.5:
                            s = 10
                        littleBullet(self.x, self.y, -s if temp > 0 else s,
                                     temp, LITTLE_BULLET_RADIUS_ALTER)
                        pyxel.play(0, 0)

    def draw(self):
        pyxel.blt(self.x, self.y, 0, 16, 0, self.w, self.h, 15)

_9_utils.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

def collisionCheck(a, b):
    if (
            a.x + a.w > b.x
            and b.x + b.w > a.x
            and a.y + a.h > b.y
            and b.y + b.h > a.y
    ):
        return True
    return False

def edgeCheck(W, H, C, mode):
    if mode == 0:           # 出边界
        if C.x + C.w +0.5<0 or C.x-0.5>W or C.y+C.h+0.5<0 or C.y-0.5>H:
            return True
        else:
            return False

    if mode == 1:           # 碰边界
        if C.x +0.5<0 or C.x+C.w-0.5>W or C.y+0.5<0 or C.y-0.5+C.h> H:
            return True
        else:
            return False

class ListProcess:
    def __init__(self):
        pass

    def update_list(self, list):
        for elem in list:
            elem.update()

    def draw_list(self, list):
        for elem in list:
            elem.draw()

    def update_skill(self, list):
        for elem in list:
            elem.update()

    def draw_skill(self, list):
        for elem in list:
            elem.draw()

_10_game.py

from _2_enemy import *
from _3_mytimer import *
from _4_player import *
from _5_powerbar import *
from _6_recruit import *
from _7_scoreboard import *
from _8_skill import *
from _9_utils import *


start_time = time.time()
mytime = myTimer(start_time)
pb = powerBar(20)
list_process = ListProcess()
sb = ScoreBoard()


# 碰撞检测与碰撞事件是在main中进行的
def cleanup_list(list):
    i = 0
    while i < len(list):
        elem = list[i]
        if not elem.alive:
            if isinstance(elem, GenralEnemy):
                if pb.current < pb.total:
                    pb.current += 1
            list.pop(i)
        else:
            i += 1

class Background:
    def __init__(self):
        self.star_list = []
        for i in range(STAR_COUNT):
            self.star_list.append(
                (random() * pyxel.width, random() * pyxel.height, random() * 1.5 + 1)
            )

    def update(self):
        for i, (x, y, speed) in enumerate(self.star_list):
            y += speed
            if y >= pyxel.height:
                y -= pyxel.height
            self.star_list[i] = (x, y, speed)

    def draw(self):
        for (x, y, speed) in self.star_list:
            pyxel.pset(x, y, STAR_COLOR_HIGH if speed > 1.8 else STAR_COLOR_LOW)


class Blast:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.radius = BLAST_START_RADIUS
        self.alive = True

        blast_list.append(self)

    def update(self):
        self.radius += 1

        if self.radius > BLAST_END_RADIUS:
            self.alive = False

    def draw(self):
        pyxel.circ(self.x, self.y, self.radius, BLAST_COLOR_IN)
        pyxel.circb(self.x, self.y, self.radius, BLAST_COLOR_OUT)


class App:
    def __init__(self):
        pyxel.init(SCREEN_WIDTH, SCREEN_HEIGHT, caption="PPP")
        #向pyxel仓库中放入东西
        pyxel.image(0).set(
            0,
            0,
            [
                "00c00c00",
                "0c7007c0",
                "0c7007c0",
                "c703b07c",
                "77033077",
                "785cc587",
                "85c77c58",
                "0c0880c0",
            ],
        )

        pyxel.image(0).set(
            8,
            0,
            [
                "00088000",
                "00ee1200",
                "08e2b180",
                "02882820",
                "00222200",
                "00012280",
                "08208008",
                "80008000",
            ],
        )
        pyxel.image(0).load(16, 0, "assets/copyplayer.png")
        pyxel.image(1).load(0, 0, "assets/noguchi_tileset_128x128.png")
        pyxel.image(1).load(0, 128, "assets/boss1.png")
        pyxel.image(1).load(28, 128, "assets/boss2.png")
        pyxel.image(1).load(56, 128, "assets/boss3.png")
        pyxel.image(1).load(84, 128, "assets/skill3_1.png")
        pyxel.image(1).load(86, 128, "assets/skill3_2.png")
        pyxel.image(1).load(88, 128, "assets/skill3_3.png")

        pyxel.sound(0).set("a3a2c1a1", "p", "7", "s", 5)
        pyxel.sound(1).set("a3a2c2c2", "n", "7742", "s", 10)

        self.scene = SCENE_TITLE
        self.score = 0
        self.finish_time = -1
        self.background = Background()
        self.player = 0
        self.boss_come = False
        sb.setParent(self)

        # 循环执行update和draw
        pyxel.run(self.update, self.draw)

    # update中放入其它函数
    def update(self):
        self.background.update()

        if self.scene == SCENE_TITLE:
            self.update_title_scene()
        elif self.scene == SCENE_PLAY:
            self.update_play_scene()
        elif self.scene == SCENE_GAMEOVER:
            self.update_gameover_scene()
        elif self.scene == SCENE_WIN:
            self.update_win_scene()

    def update_title_scene(self):
        self.boss_come = False

        if pyxel.btnp(pyxel.KEY_1):
            mytime.reset()
            mytime.start_time = time.time()
            pb.set(15, 15)
            self.player = Player(pyxel.width / 2, pyxel.height - 20, 0, pb)
            self.scene = SCENE_PLAY

        elif pyxel.btnp(pyxel.KEY_2):
            mytime.reset()
            mytime.start_time = time.time()
            pb.set(20, 0)
            self.player = Player(pyxel.width / 2, pyxel.height - 20, 1, pb)
            self.scene = SCENE_PLAY

        elif pyxel.btnp(pyxel.KEY_3):
            mytime.reset()
            mytime.start_time = time.time()
            pb.set(10, 10)
            self.player = Player(pyxel.width / 2, pyxel.height - 20, 2, pb)
            self.scene = SCENE_PLAY


    def update_win_scene(self):
        list_process.update_list(bullet_list)
        list_process.update_list(enemy_list)
        list_process.update_list(blast_list)
        list_process.update_list(boss_list)
        list_process.update_list(recruit_list)

        cleanup_list(enemy_list)
        cleanup_list(bullet_list)
        cleanup_list(blast_list)
        cleanup_list(boss_list)
        cleanup_list(recruit_list)

        if pyxel.btnp(pyxel.KEY_ENTER):
            self.scene = SCENE_TITLE
            self.player.x = pyxel.width / 2
            self.player.y = pyxel.height - 20
            self.score = 0
            mytime.reset()

            enemy_list.clear()
            bullet_list.clear()
            blast_list.clear()
            boss_list.clear()
            recruit_list.clear()

    def update_play_scene(self):
        # 每0.5s 产生敌人
        if pyxel.frame_count % 30 == 0 and self.boss_come is False:
            # 随机位置
            if randrange(0, 10):
                Enemy(random() * (pyxel.width - PLAYER_WIDTH), 0, ENEMY_SPEED)
                Enemy(random() * (pyxel.width - PLAYER_WIDTH), 0, ENEMY_SPEED)
            else:
                Octopus(random() * (pyxel.width - PLAYER_WIDTH), 0)
            if randrange(0, 100) < 5:
                Recruit(randrange(10, 150),  randrange(0, 3), 0.75, ENEMY_SPEED)

        elif mytime.current >= BOSS_COME_TIME and self.boss_come is False:         # 100s 后 boss 降临
            Boss()
            self.boss_come = True
        if self.boss_come is True:
            if pyxel.frame_count % 24 == 0 and randrange(0, 10):
                Octopus(random() * (pyxel.width - PLAYER_WIDTH), 0)
            if randrange(0, 1000) < 5:
                Recruit(randrange(10, 150),  randrange(0, 3), 0.75, ENEMY_SPEED)
            if pyxel.frame_count % 300 == 0:
                Recruit(randrange(10, 150),  randrange(0, 3), 0.75, ENEMY_SPEED)

        for r in recruit_list:
            if collisionCheck(r, self.player):
                r.alive = False
                pyxel.play(1, 1)
                if r.number == 0:
                    self.player.status[1].release()
                elif r.number == 1:
                    self.player.status[2].release()
                elif r.number == 2:
                    self.player.status[3].release()

        for a in boss_list:
            for b in bullet_list:
                if (
                    a.x + a.w > b.x
                    and b.x + b.w > a.x
                    and a.y + a.h > b.y
                    and b.y + b.h > a.y
                ):
                    a.blood -= 1 if not isinstance(b, Laser) else 2
                    if a.blood <= 0:
                        a.alive = False
                        self.score += 1000
                        self.finish_time = mytime.current
                        self.scene = SCENE_WIN

                    b.alive = False if not isinstance(b, Laser) else True

                    blast_list.append(
                        Blast(b.x + BULLET_WIDTH / 2, b.y)
                    )
                    pyxel.play(1, 1)

        # 去撞boss
        for a in boss_list:
            if (
                    self.player.x + self.player.w > a.x
                    and a.x + a.w > self.player.x
                    and self.player.y + self.player.h > a.y
                    and a.y + a.h > self.player.y
            ):
                a.blood -= 1
                if a.blood <= 0:
                    a.alive = False
                    self.score += 1000
                    self.finish_time = mytime.current
                    self.scene = SCENE_WIN

                else:
                    self.scene = SCENE_GAMEOVER
                # 自爆
                blast_list.append(
                    Blast(
                        self.player.x + PLAYER_WIDTH / 2,
                        self.player.y + PLAYER_HEIGHT / 2,
                    )
                )
                pyxel.play(1, 1)

        # 判断是子弹与敌人是否相撞
        for a in enemy_list:
            for b in bullet_list:
                if (
                    a.x + a.w > b.x
                    and b.x + b.w > a.x
                    and a.y + a.h > b.y
                    and b.y + b.h > a.y
                ):
                    a.alive = False
                    b.alive = False if not isinstance(b, Laser) else True

                    blast_list.append(
                        Blast(a.x + ENEMY_WIDTH / 2, a.y + ENEMY_HEIGHT / 2)
                    )

                    pyxel.play(1, 1)

                    self.score += 10

        # 被敌人撞毁
        for enemy in enemy_list:
            if (
                self.player.x + self.player.w > enemy.x
                and enemy.x + enemy.w > self.player.x
                and self.player.y + self.player.h > enemy.y
                and enemy.y + enemy.h > self.player.y
            ):
                enemy.alive = False
                # 自爆
                blast_list.append(
                    Blast(
                        self.player.x + PLAYER_WIDTH / 2,
                        self.player.y + PLAYER_HEIGHT / 2,
                    )
                )

                pyxel.play(1, 1)

                self.scene = SCENE_GAMEOVER

        self.player.update()
        list_process.update_list(bullet_list)
        list_process.update_list(enemy_list)
        list_process.update_list(blast_list)
        list_process.update_list(self.player.status)
        list_process.update_list(boss_list)
        list_process.update_list(recruit_list)

        mytime.update()
        pb.update()
        sb.update()

        cleanup_list(enemy_list)
        cleanup_list(bullet_list)
        cleanup_list(blast_list)
        cleanup_list(boss_list)
        cleanup_list(recruit_list)

    def update_gameover_scene(self):
        list_process.update_list(bullet_list)
        list_process.update_list(enemy_list)
        list_process.update_list(blast_list)
        list_process.update_list(boss_list)
        list_process.update_list(recruit_list)

        cleanup_list(enemy_list)
        cleanup_list(bullet_list)
        cleanup_list(blast_list)
        cleanup_list(boss_list)
        cleanup_list(recruit_list)

        if pyxel.btnp(pyxel.KEY_ENTER):
            self.scene = SCENE_TITLE
            self.player.x = pyxel.width / 2
            self.player.y = pyxel.height - 20
            self.score = 0

            boss_list.clear()
            recruit_list.clear()
            enemy_list.clear()
            bullet_list.clear()
            blast_list.clear()

    def draw(self):
        pyxel.cls(0)

        self.background.draw()

        if self.scene == SCENE_TITLE:
            self.draw_title_scene()
        elif self.scene == SCENE_PLAY:
            self.draw_play_scene()
        elif self.scene == SCENE_GAMEOVER:
            self.draw_gameover_scene()
        elif self.scene == SCENE_WIN:
            self.draw_win_scene()

    def draw_win_scene(self):
        self.player.draw()
        list_process.draw_list(bullet_list)
        list_process.draw_list(enemy_list)
        list_process.draw_list(blast_list)
        list_process.draw_list(boss_list)
        list_process.draw_list(recruit_list)

        mytime.draw()
        pb.draw()
        sb.draw()

        list_process.draw_skill(self.player.status)

        pyxel.text(35, 66, "You Win", 8)
        pyxel.text(33, 80, "YOUR SCORE {0}".format(self.score), 12)
        pyxel.text(33, 90, "YOUR TIME {0}".format(self.finish_time), 12)
        # pyxel.text(33, 100, "Best SCORE {0}".format(file.read()),8)
        # pyxel.text(33, 110, "Best TIME {0}".format(file.read()),8)

        pyxel.text(10, 126, "- PRESS ENTER TO TRY AGAIN-", 13)

    def draw_title_scene(self):
        pyxel.text(26, 35, "Pyxel Shooter 0.1", pyxel.frame_count % 16)
        pyxel.text(23, 96, "- CHOOSE YOUR ROLE -", 15)
        pyxel.text(30, 116, "1 FOR SHADOW", 15)
        pyxel.text(30, 126, "2 FOR LASER", 15)
        pyxel.text(30, 136, "3 FOR TEMPO", 15)

    def draw_play_scene(self):
        self.player.draw()
        list_process.draw_list(bullet_list)
        list_process.draw_list(enemy_list)
        list_process.draw_list(blast_list)
        list_process.draw_list(boss_list)
        list_process.draw_list(recruit_list)

        mytime.draw()
        pb.draw()
        sb.draw()

        list_process.draw_skill(self.player.status)

    def draw_gameover_scene(self):
        list_process.draw_list(bullet_list)
        list_process.draw_list(enemy_list)
        list_process.draw_list(blast_list)
        list_process.draw_list(boss_list)
        list_process.draw_list(recruit_list)

        pyxel.text(40, 66, "GAME OVER", 8)
        pyxel.text(15, 126, "- PRESS ENTER RESTART-", 13)

if __name__ == '__main__':
    App()

spec文件

# -*- mode: python ; coding: utf-8 -*-

block_cipher = None


a = Analysis(['_10_game.py',
    '_0_global.py',
    '_1_bullet.py',
    '_2_enemy.py',
    '_3_mytimer.py',
    '_4_player.py',
    '_5_powerbar.py',
    '_6_recruit.py',
    '_7_scoreboard.py',
    '_8_skill.py',
    '_9_utils.py'
],
             pathex=['C:\\Users\\86183\\Desktop\\pyxel_examples'],
             binaries=[],
             datas=[('C:\\Users\\86183\\Desktop\\pyxel_examples\\assets','assets')],
             hiddenimports=[],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher,
             noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)
exe = EXE(pyz,
          a.scripts,
          [],
          exclude_binaries=True,
          name='_10_game',
          debug=False,
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          console=False )
coll = COLLECT(exe,
               a.binaries,
               a.zipfiles,
               a.datas,
               strip=False,
               upx=True,
               upx_exclude=[],
               name='_10_game')

github:

https://github.com/wfhhisagony/pyxel-shooter-0.1

打包

  • 然后pyinstaller打包的时候,遇到了一些问题,多文件打包就不建议直接使用命令行,使用spec文件。可以参考如下博客:
  • https://blog.csdn.net/lghello/article/details/105824057
  • 如果打包的文件打不开,就使用命令行打开,看看哪里报错了再照着改。我就是这样,发现它少了个pyxel模块包,于是把整个pyxel模块包复制到了打包的游戏文件里。然后我又发现个问题就是运行的时候总出现命令行,原来要把spec文件里的console=True改为False

启发

  • 父类继承减少代码冗余
  • 建立批量管理频繁更新变量的函数、
  • 以功能相似的模块分文件,不要都写在一起
  • 使用一个类来定义宏,经常要调要改的常量或变量尽量使用宏和全局变量,全局变量以后在改回去也行。
  • 找类之间的共同点以便建立父子关系和共用函数
  • 宏定义修改起来方便
  • 循环事件的分流法
  • 类中传入的参数尽量原子化
  • 方法和属性搞错(多加括号)
  • 熟练使用python数组方便对数据批量操作(利用同名函数、同名变量)
  • 类中可以适当增加一些工具函数
  • 中间加一层来解决关联问题可能会利于维护和开发
  • 适当设置条件开关和操控条件开关的函数
  • 适当使用全局变量

改进

  1. 利用父类减少代码冗余

  2. 将参数direction修改为yspeed或xspeed

  3. 尽量将cleanup_list移入到utils文件类中

  4. 将共用的逻辑封装进函数中

  5. 思考如何管理、处理标志开关,防止它被忘记。

  6. 可以考虑加音效和音乐

  7. 文字显示的位置都是手调的实在麻烦,希望能想出个什么方法来方便调整,是不是可以考虑图形化界面之类的方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值