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数组方便对数据批量操作(利用同名函数、同名变量)
- 类中可以适当增加一些工具函数
- 中间加一层来解决关联问题可能会利于维护和开发
- 适当设置条件开关和操控条件开关的函数
- 适当使用全局变量
改进
-
利用父类减少代码冗余
-
将参数direction修改为yspeed或xspeed
-
尽量将cleanup_list移入到utils文件类中
-
将共用的逻辑封装进函数中
-
思考如何管理、处理标志开关,防止它被忘记。
-
可以考虑加音效和音乐
-
文字显示的位置都是手调的实在麻烦,希望能想出个什么方法来方便调整,是不是可以考虑图形化界面之类的方法