前言:当时(去年三月)接下来这个项目的时候,由于各种关系还没有开始着手,七月在天津实训的时候学了会儿(以后也许会发出来吧,不过都是半成品),之后就准备考研。这个项目还是得做的,本来就打算这个寒假完成,其实考研期间还挺犹豫,因为还不知道做什么游戏,前几天开了个会商榷了一下,ycb(YYyyCCCcccBb )说他五六月份已经把飞机大战的雏形搭建好了,本来我的本意是想做主剧情类的,但是他都搭建好了也只能用雏形做咯。(刚开始以为是他从哪里download下来的,后来发现他有好多拼写错误以为他自己写的,后来才知道他从书上抄下来的),最近差不多花了五六天(的晚上)对他给的雏形进行理解(连个注释都没有)和深度优化(那个雏形的效果简直不能看),然后增加了点功能。我是负责第一关的,所以以后还会再改进(再更)。
总体效果:
先说下不足(以后有时间会完成):1.暂时没找到合适的各种背景图和logo 以及本机的贴图
2.除了清屏道具以外其他道具使用的实现
3.金币商城内武器系统未上线
4.死亡、下一关特效及提示等
5.显示文档只能第一页(懒得写翻页了)
实现(优化)了的内容:1.菜单各按钮的功能 2.金币商城内购买的提升属性的提升 3.敌机、石头、bonus的随机性 4.部分彩蛋和道具
在这里介绍一下写的部分函数:
Display.py:(没有写完,只能显示前十行)
import pygame.font
from Codes.Common.DrawBase import DrawOn,_DrawCallInstance
from Codes.Base.Screen import _ScreenInstance
from Codes.Logic.GameStats import _GameStatsInstance
class Display(DrawOn):
def __init__(self,text):
super().__init__()
super(Display,self).definePriority(9)
self.screen=_ScreenInstance.screen
self.screen_rect=(0,0,1200,800)
self.bg_color=(255,255,255)
self.text_color=(0,0,0)
pygame.font.init()
self.font_format=pygame.font.SysFont('SimHei',20)
self.total=10 #一页显示最多个数
self.length=len(text)
self.text=text
self.width, self.height=200,111
self.image=pygame.image.load(r'D:/MyG/icons/back.png')
self.image=pygame.transform.scale(self.image,(self.width,self.height))
self.image_rect=(0,0)
self.rect=pygame.Rect(0,0,self.width, self.height)
def pre_draw(self):
_DrawCallInstance.add(self)
def draw(self):
i=0
k=0
self.screen.fill(self.bg_color, self.screen_rect)
self.screen.blit(self.image,(0,0,200,111))
while k
实例化这个类的时候需要输入一个txt(字符串数组,数组中每个值代表一行),show/pre_show/draw函数在很多类中都有,每次循环update的时候都会调用各个show,show调用pre_show(感觉直接就把preshow的代码写在show里也可以),把这个类的实例化对象放入一个类似于队列的drawcall,drawcall对队列中所有对象进行优先级排序(Priority),优先级大的对象会先调用自身的draw在屏幕上绘制。
self.rect是定义的点击事件触发的范围,check函数就是对应的触发事件,在这里image为back,就是显示文本后的返回键,点击返回键(self.rect的范围),就会触发check函数 (这个在UImgr.py里),更改对应的几个状态值。
GameStats包含了几个状态值:
from Codes.Base.GameSettings import _GameSettingsInstance
class GameStats():
def __init__(self ):
pass
def init(self):
self.reset_stats()
self.game_active=False
self.display_active=False
self.rank_active=False
self.help_active=False
self.store_active=False
def reset_stats(self):
self.ships_left = _GameSettingsInstance.ship_limit#left=剩下的生命
def printstats(self):
print('game'+str(self.game_active))
print('display'+str(self.display_active))
print('rank'+str(self.rank_active))
print('help'+str(self.help_active))
print('store'+str(self.store_active))
_GameStatsInstance= GameStats()
在UImgr里会根据当前不同的状态值处理不同的点击事件和调用对应的show,且最多同时只有一个状态为True:
状态名 | 为True时的含义 |
game_active | 显示对战界面 |
display_active | 显示日志(log) |
rank_active | 显示排名(rank) |
help_active | 显示帮助(help) |
store_active | 显示金币商城界面 |
都为False | 显示菜单 |
UImgr:
from Codes.UI.BeginGame import BeginGame
from Codes.UI.QuitGame import QuitGame
from Codes.UI.HelpGame import HelpGame
from Codes.UI.RankGame import RankGame
from Codes.UI.StoreSys import Store
from Codes.UI.logo import logo
from Codes.Logic.GameStats import _GameStatsInstance
class UIMgr():
def init(self):
self.BeginUI=BeginGame()
self.QuitUI=QuitGame()
self.HelpUI=HelpGame()
self.RankUI=RankGame()
self.storeUI=Store()
self.logoUI=logo()
def update(self,delta):
if not _GameStatsInstance.game_active and not _GameStatsInstance.display_active and not _GameStatsInstance.rank_active and not _GameStatsInstance.help_active and not _GameStatsInstance.store_active:
self.BeginUI.show()
self.QuitUI.show()
self.HelpUI.show()
self.RankUI.show()
self.logoUI.show()
if _GameStatsInstance.display_active and not _GameStatsInstance.store_active:
self.logoUI.DisplayUI.show()
if _GameStatsInstance.rank_active and not _GameStatsInstance.store_active:
self.RankUI.DisplayUI.show()
if _GameStatsInstance.help_active and not _GameStatsInstance.store_active:
self.HelpUI.DisplayUI.show()
if _GameStatsInstance.store_active:
self.storeUI.show()
def check_Mouse(self,mouse_x,mouse_y,dispatcher):
if not _GameStatsInstance.game_active and not _GameStatsInstance.display_active and not _GameStatsInstance.rank_active and not _GameStatsInstance.help_active and not _GameStatsInstance.store_active:#游戏未开始前的点击
#四个按钮的触发条件:所UI均处于关闭(false)
if self.BeginUI.rect.collidepoint(mouse_x,mouse_y) :
self.BeginUI.check()
if self.QuitUI.rect.collidepoint(mouse_x,mouse_y) :
self.QuitUI.check()
if self.RankUI.rect.collidepoint(mouse_x,mouse_y) :
self.RankUI.check()
if self.HelpUI.rect.collidepoint(mouse_x,mouse_y) :
self.HelpUI.check()
if self.logoUI.rect.collidepoint(mouse_x,mouse_y) :
self.logoUI.check()
if _GameStatsInstance.display_active:
if self.logoUI.DisplayUI.rect.collidepoint(mouse_x,mouse_y) :
self.logoUI.DisplayUI.check()
if _GameStatsInstance.rank_active:
if self.RankUI.DisplayUI.rect.collidepoint(mouse_x,mouse_y) :
self.RankUI.DisplayUI.check()
if _GameStatsInstance.help_active:
if self.HelpUI.DisplayUI.rect.collidepoint(mouse_x,mouse_y) :
self.HelpUI.DisplayUI.check()
if _GameStatsInstance.store_active:
for button in self.storeUI.buttons:
if button.rect.collidepoint(mouse_x,mouse_y) :
button.check()
break
_UIMgrInstance=UIMgr()
初始化时,分别对开始、退出、排名等类进行实例化,每次update时,检查状态情况,如果都为false,则调用五个(logo.begin.quit.rank.help)的show,实现显示菜单,如果display/rank/help为active的时候,则调用对象里的displayUI(已实例化Display类)的显示,实现显示不同的三个文本,如果store为True,则显示金币商店;check_Mouse在Handle.py被调用:每次收到鼠标点击时,执行check_Mouse:根据不同的当前状态执行对应的check
Handle.py和Event.py:
import sys
import pygame
from Codes.Common.Event import _EventPatcher
from Codes.UI.UIMgr import _UIMgrInstance
class Handle():
def init(self):
self.add(pygame.K_q, self.exit)
# 配置游戏基本信息
def update(self, delta):
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.KEYDOWN:
_EventPatcher.dispatch(event.key)
elif event.type == pygame.KEYUP:
if event.key == pygame.K_a or event.key == pygame.K_d or event.key == pygame.K_w or event.key == pygame.K_s or event.key == pygame.K_p or pygame.K_r or pygame.K_e:
_EventPatcher.dispatch(event.key)
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse_x, mouse_y = pygame.mouse.get_pos()
_UIMgrInstance.check_Mouse(mouse_x, mouse_y, self)
def add(self, type, element):
_EventPatcher.addEvent(type, element)
def exit(self):
sys.exit()
_HandleInstance = Handle()
import sys
import pygame
class Event():
def __init__(self):
self.map2ListDistributeEvnet={'key':[]}
def init(self):
self.map2ListDistributeEvnet.clear()
def addEvent(self,type,element):
if type in self.map2ListDistributeEvnet:
self.map2ListDistributeEvnet(type).append(element)
else:
self.map2ListDistributeEvnet[type]=[]
self.map2ListDistributeEvnet[type].append(element)
def subEvent(self,type,element):
if type in self.map2ListDistributeEvnet:
if element in self.map2ListDistributeEvnet[type]:
self.map2ListDistributeEvnet[type].remove(element)
def dispatch(self,type):
if type not in self.map2ListDistributeEvnet :
print('按键未注册')
return
for event in self.map2ListDistributeEvnet[type]:
event()
_EventPatcher =Event()
Handle的update根据不同的键盘/鼠标输入处理进行不同的dispatch,Event.py定义个一个事件分发器类(ycb,飞机大战总结)。
菜单上五个按钮的定义和点击事件实现:
import pygame.font
from Codes.Logic.GameStats import _GameStatsInstance
from Codes.Base.Screen import _ScreenInstance
from Codes.Common.DrawBase import DrawOn,_DrawCallInstance
from Codes.UI.Display import Display
import sys
from Codes.Base.GameSettings import _GameSettingsInstance
import time
import math
class logo(DrawOn):
def __init__(self ):
f=open('log.txt').readlines()
self.DisplayUI=Display(f)
super().__init__()
super(logo,self).definePriority(9)
self.screen=_ScreenInstance.screen
self.screen_rect=self.screen.get_rect()
self.width, self.height=80,80
self.caidan=0
self.caidan_active=False
#self.rect=pygame.Rect(0,0,self.width,self.height)
self.image=[pygame.image.load(r'D:/MyG/icons/logo1.png'),pygame.image.load(r'D:/MyG/icons/logo2.png'),pygame.image.load(r'D:/MyG/icons/logo3.png'),pygame.image.load(r'D:/MyG/icons/logo4.png')]
i=0
while i<4:
self.image[i]=pygame.transform.scale(self.image[i],(self.width,self.height))
i=i+1
self.image_rect=(0.5*_GameSettingsInstance.screen_width-0.5*self.width,0.2*_GameSettingsInstance.screen_height-0.5*self.height)
#self.rect=pygame.Rect(0.5*_GameSettingsInstance.screen_width-0.5*self.width,603,self.width,self.height)
self.rect=pygame.Rect(0.5*_GameSettingsInstance.screen_width-0.5*self.width,0.2*_GameSettingsInstance.screen_height-0.5*self.height,self.width,self.height)
#self.rect.center= self.screen_rect.center
def pre_draw(self):
_DrawCallInstance.add(self)
def draw(self):
frame=math.floor(time.time()%1/0.25) #一秒切分为四帧 每帧一张图片
#self.screen.fill(self.button_color, self.rect)
self.screen.blit(self.image[frame],self.image_rect)
def show(self):
self.pre_draw()
def check(self):
self.caidan+=1
if self.caidan==3:#设置彩蛋:当点击logo三次后会显示logo
self.caidan=0
_GameStatsInstance.game_active=False
_GameStatsInstance.display_active=True
import pygame.font
from Codes.Logic.GameStats import _GameStatsInstance
from Codes.Base.Screen import _ScreenInstance
from Codes.Common.DrawBase import DrawOn,_DrawCallInstance
from Codes.Battle import _BattleInstance
from Codes.Kinds.test import _GameSettingsInstance
class BeginGame(DrawOn):
def __init__(self ):
super().__init__()
super(BeginGame,self).definePriority(10)
self.screen=_ScreenInstance.screen
self.screen_rect=self.screen.get_rect()
self.width, self.height=192,74
#self.rect=pygame.Rect(0,0,self.width,self.height)
self.image=pygame.image.load(r'D:/MyG/icons/play.png')
self.image=pygame.transform.scale(self.image,(self.width,self.height))
self.image_rect=(0.5*_GameSettingsInstance.screen_width-0.5*self.width,0.4*_GameSettingsInstance.screen_height-0.5*self.height)
self.rect=pygame.Rect(0.5*_GameSettingsInstance.screen_width-0.5*self.width,0.4*_GameSettingsInstance.screen_height-0.5*self.height,self.width,self.height)
#self.rect.center= self.screen_rect.center
def pre_draw(self):
_DrawCallInstance.add(self)
def draw(self):
#self.screen.fill(self.button_color, self.rect)
self.screen.blit(self.image,self.image_rect)
def show(self):
self.pre_draw()
def check(self):
_GameStatsInstance.game_active=True
_GameStatsInstance.reset_stats()
_BattleInstance.init()
import pygame.font
from Codes.Logic.GameStats import _GameStatsInstance
from Codes.Base.Screen import _ScreenInstance
from Codes.Common.DrawBase import DrawOn,_DrawCallInstance
import sys
from Codes.Base.GameSettings import _GameSettingsInstance
from Codes.UI.Display import Display
class RankGame(DrawOn):
def __init__(self ):
super().__init__()
super(RankGame,self).definePriority(9)
f=open('rank.txt').readlines()
self.DisplayUI=Display(f)
self.screen=_ScreenInstance.screen
self.screen_rect=self.screen.get_rect()
self.width, self.height=192,74
#self.rect=pygame.Rect(0,0,self.width,self.height)
self.image=pygame.image.load(r'D:/MyG/icons/rank.png')
self.image=pygame.transform.scale(self.image,(self.width,self.height))
self.image_rect=(0.5*_GameSettingsInstance.screen_width-0.5*self.width,0.666*_GameSettingsInstance.screen_height-0.5*self.height)
#self.rect=pygame.Rect(0.5*_GameSettingsInstance.screen_width-0.5*self.width,603,self.width,self.height)
self.rect=pygame.Rect(0.5*_GameSettingsInstance.screen_width-0.5*self.width,0.666*_GameSettingsInstance.screen_height-0.5*self.height,self.width,self.height)
#self.rect.center= self.screen_rect.center
def pre_draw(self):
_DrawCallInstance.add(self)
def draw(self):
#self.screen.fill(self.button_color, self.rect)
self.screen.blit(self.image,self.image_rect)
def show(self):
self.pre_draw()
def check(self):
_GameStatsInstance.game_active=False
_GameStatsInstance.rank_active=True
import pygame.font
from Codes.Logic.GameStats import _GameStatsInstance
from Codes.Base.Screen import _ScreenInstance
from Codes.Common.DrawBase import DrawOn,_DrawCallInstance
import sys
from Codes.Base.GameSettings import _GameSettingsInstance
from Codes.UI.Display import Display
class HelpGame(DrawOn):
def __init__(self ):
super().__init__()
super(HelpGame,self).definePriority(9)
f=open('help.txt').readlines()
self.DisplayUI=Display(f)
self.screen=_ScreenInstance.screen
self.screen_rect=self.screen.get_rect()
self.width, self.height=192,74
#self.rect=pygame.Rect(0,0,self.width,self.height)
self.image=pygame.image.load(r'D:/MyG/icons/help.png')
self.image=pygame.transform.scale(self.image,(self.width,self.height))
self.image_rect=(0.5*_GameSettingsInstance.screen_width-0.5*self.width,0.533*_GameSettingsInstance.screen_height-0.5*self.height)
#self.rect=pygame.Rect(0.5*_GameSettingsInstance.screen_width-0.5*self.width,603,self.width,self.height)
self.rect=pygame.Rect(0.5*_GameSettingsInstance.screen_width-0.5*self.width,0.533*_GameSettingsInstance.screen_height-0.5*self.height,self.width,self.height)
#self.rect.center= self.screen_rect.center
def pre_draw(self):
_DrawCallInstance.add(self)
def draw(self):
#self.screen.fill(self.button_color, self.rect)
self.screen.blit(self.image,self.image_rect)
def show(self):
self.pre_draw()
def check(self):
_GameStatsInstance.game_active=False
_GameStatsInstance.help_active=True
import pygame.font
from Codes.Logic.GameStats import _GameStatsInstance
from Codes.Base.Screen import _ScreenInstance
from Codes.Common.DrawBase import DrawOn,_DrawCallInstance
import sys
from Codes.Base.GameSettings import _GameSettingsInstance
class QuitGame(DrawOn):
def __init__(self ):
super().__init__()
super(QuitGame,self).definePriority(9)
self.screen=_ScreenInstance.screen
self.screen_rect=self.screen.get_rect()
self.width, self.height=192,74
#self.rect=pygame.Rect(0,0,self.width,self.height)
self.image=pygame.image.load(r'D:/MyG/icons/quit.png')
self.image=pygame.transform.scale(self.image,(self.width,self.height))
self.image_rect=(0.5*_GameSettingsInstance.screen_width-0.5*self.width,0.8*_GameSettingsInstance.screen_height-0.5*self.height)
#self.rect=pygame.Rect(0.5*_GameSettingsInstance.screen_width-0.5*self.width,603,self.width,self.height)
self.rect=pygame.Rect(0.5*_GameSettingsInstance.screen_width-0.5*self.width,0.8*_GameSettingsInstance.screen_height-0.5*self.height,self.width,self.height)
#self.rect.center= self.screen_rect.center
def pre_draw(self):
_DrawCallInstance.add(self)
def draw(self):
#self.screen.fill(self.button_color, self.rect)
self.screen.blit(self.image,self.image_rect)
def show(self):
self.pre_draw()
def check(self):
_GameStatsInstance.game_active=False
_GameStatsInstance.reset_stats()
pygame.quit()
sys.exit()
其中logo用几个jpg实现了gif的效果:每次show时,查看当前时间 如果是0.25 则显示第一张,以此类推,点击三次logo会显示log.txt(算是彩蛋),大部分的check通过更改Gamestats的状态值即可实现界面的切换。其中logo/rank/help先读取对应的文件,再生成display对象,在UImgr中被调用对象的show。
比较特殊的,商店系统中的内容丰富一些:
import pygame
from Codes.Common.DrawBase import DrawOn,_DrawCallInstance
from Codes.Battle import _BattleInstance
from Codes.Base.Screen import _ScreenInstance
from Codes.Base.GameSettings import _GameSettingsInstance
from Codes.UI.Buy import Buy
class Store(DrawOn):
def __init__(self):
super().__init__()
super(Store,self).definePriority(2)
self.screen=_ScreenInstance.screen
self.rect=self.screen.get_rect()
self.bg_color=(255,255,255)
self.bg_width=_GameSettingsInstance.screen_width
self.bg_height=_GameSettingsInstance.screen_height
self.aliens_v=1
self.aliens_scale=1
self.bullets_scale=1
def pre_draw(self):
_DrawCallInstance.add(self)
def draw(self):
self.screen.fill(self.bg_color, self.rect)
t="当前金币:"+str(_BattleInstance.gold)+'G'
#提升/降低基本属性组 常在
text=[]
text.append("当前敌机速度:"+str(_GameSettingsInstance.aliens_v_min)+" 降低0.1,需要花费")
text.append("当前敌机体积:"+str(_GameSettingsInstance.aliens_size)+" 降低0.1,需要花费")
text.append("当前子弹上限:"+str(_GameSettingsInstance.bullets_allowed)+" 提高上限1,需要花费")
text.append("当前子弹体积:"+str(_GameSettingsInstance.bullet_width)+" 提高0.1,需要花费")
text.append("当前子弹射速:"+str(_GameSettingsInstance.bullet_speed_factor)+" 提高0.1,需要花费")
text.append("当前主机速度:"+str(_GameSettingsInstance.ship_speed_factor)+" 提高0.1,需要花费")
text.append("当前飞机生命:"+str(_GameSettingsInstance.ship_limit)+" 提高上限1,需要花费")
if _GameSettingsInstance.vip==0:
spend=[500,500,200,300,200,1000,1000]
elif _GameSettingsInstance.vip==1:#购买vip后提升属性八折 vip在道具组
spend=[400,400,160,240,160,800,800]
font1 = pygame.font.SysFont('kaiti', 16)
textSurfaceObj = font1.render(t, True, (0,0,0))
textRectObj = textSurfaceObj.get_rect()
textRectObj.center=(200,50)
self.screen.blit(textSurfaceObj, textRectObj)
i=0
self.buttons=[]
while i
show中一点点码出了要显示的内容,多行内容存在text里(似乎通过display也可以实现),然后循环输出,_GameSettingsInstance定义了一些游戏的参数,例如子弹大小、速度等等:
class GameSettings():
def __init__(self):
self.screen_width=1200
self.screen_height=800
self.bg_color=(58,60,55)
def init(self):
self.screen_width = 1200
self.screen_height = 800
self.bg_color = (155, 155, 155)
self.weapon=0 #武器 0-初始武器
self.aliens_v_min=1
self.aliens_v_max=2
self.aliens_size=100
# bullet
self.bullet_speed_factor = 3
self.bullet_width = 10
self.bullet_height = 15
self.bullet_color = 0, 0, 0
self.bullets_allowed = 3
self.item=-1
# ship
self.ship_speed_factor = 150
self.ship_limit = 2#生命值
self.vip=0#是否购买vip
self.weapon2=0#是否购买武器2
self.weapon3=0#是否购买武器2
self.use=0 #道具持有(最多一个) 0-没有
self.pac=0 #Permanent Addition Card 永久加成卡是否持有
# scale
self.speedup_scale = 1.1
self.initialize_dynamic_settings()
def setting_reset(self):
self.init()
def initialize_dynamic_settings(self):
self.bullet_speed_factor=300
def increase_speed(self):
self.ship_speed_factor*=self.speedup_scale
self.bullet_speed_factor*=self.speedup_scale
_GameSettingsInstance=GameSettings()
在Storesys中,为了实现对应的购买,每行实例化了一个Buy的按钮:
import pygame
from Codes.Common.DrawBase import DrawOn,_DrawCallInstance
from Codes.Battle import _BattleInstance
from Codes.Base.Screen import _ScreenInstance
from Codes.Base.GameSettings import _GameSettingsInstance
from Codes.Logic.GameStats import _GameStatsInstance
class Buy(DrawOn):
def __init__(self,pos,num,spend):#num:第几个按钮0-6 spend:对应金钱
super().__init__()
super(Buy,self).definePriority(3)
self.screen=_ScreenInstance.screen
self.width, self.height=79,38
self.spend=spend
self.num=num
#self.rect=pygame.Rect(0,0,self.width,self.height)
self.image=pygame.image.load(r'D:/MyG/icons/buy.png')
self.image=pygame.transform.scale(self.image,(self.width,self.height))
#self.image_rect=(0.5*_GameSettingsInstance.screen_width-0.5*self.width,0.533*_GameSettingsInstance.screen_height-0.5*self.height)
#self.rect=pygame.Rect(0.5*_GameSettingsInstance.screen_width-0.5*self.width,603,self.width,self.height)
self.rect=pygame.Rect(0,0,self.width,self.height)
self.rect.center=pos
def pre_draw(self):
_DrawCallInstance.add(self)
def draw(self):
self.screen.blit(self.image,self.rect)
def show(self):
self.pre_draw()
def check(self):
if _BattleInstance.gold>=self.spend:#能够购买
_BattleInstance.gold-=self.spend
if self.num==0:
for alien in _BattleInstance.aliens.sprites():
alien.v=round(0.9*alien.v,2)
_GameSettingsInstance.aliens_v_min=round(0.9*_GameSettingsInstance.aliens_v_min,2)
_GameSettingsInstance.aliens_v_max=round(0.9*_GameSettingsInstance.aliens_v_max,2)
if _GameSettingsInstance.aliens_v_min<0.1:#最低限制:0.1
_GameSettingsInstance.aliens_v_min=0.1
if _GameSettingsInstance.aliens_v_max<0.1:
_GameSettingsInstance.aliens_v_max=0.1
elif self.num==1:
_GameSettingsInstance.aliens_size=round(0.9*_GameSettingsInstance.aliens_size,2)
if _GameSettingsInstance.aliens_size<0.1:
_GameSettingsInstance.aliens_size=0.1
elif self.num==2:
_GameSettingsInstance.bullets_allowed+=1
elif self.num==3:
_GameSettingsInstance.bullet_width=round(1.1*_GameSettingsInstance.bullet_width,2)
_GameSettingsInstance.bullet_height=round(1.1*_GameSettingsInstance.bullet_height,2)
if _GameSettingsInstance.bullet_width>300:
_GameSettingsInstance.bullet_width=300
if _GameSettingsInstance.bullet_height>300:
_GameSettingsInstance.bullet_height=300
elif self.num==4:
_GameSettingsInstance.bullet_speed_factor=round(1.1*_GameSettingsInstance.bullet_speed_factor,2)
elif self.num==5:
_GameSettingsInstance.ship_speed_factor=round(1.1*_GameSettingsInstance.ship_speed_factor,2)
elif self.num==6:
_GameSettingsInstance.ship_limit+=1
_GameStatsInstance.ships_left+=1
elif self.num==7:#道具 商品vip卡
_GameSettingsInstance.vip=1
elif self.num==8:
_GameSettingsInstance.pac=1
elif self.num==9:
_GameSettingsInstance.use=1
elif self.num==10:
_GameSettingsInstance.use=2
elif self.num==11:
_GameSettingsInstance.use=3
由于按钮为多个,所以Buy实例化需要两个参数:第一个为按钮的坐标,第二个为按钮的编号,第三个为按钮对应需要花费的金币spend。在check中,先检查当前的金币(_BattleInstance.gold)是否大于等于spend(没钱你买nm呢),然后根据对应的编号对_GameSettingsInstance中的参数进行修改:
num | 操作 |
0 | 敌机速度降低 |
1 | 敌机尺寸降低 |
2 | 子弹数量增加 |
3 | 子弹尺寸增加 |
4 | 子弹速度增加 |
5 | 本机移速增加 |
6 | 生命增加 |
7 | 购买vip卡(仅出现一次) |
8 | 购买永久加成卡(仅出现一次) |
9 | 购买清屏道具 |
10 | 购买屏障道具 |
11 | 够买超级加倍卡道具 |
其中7-11为道具类,一直只随机出现一个,随机的参数在ship里定义,ship为本机类,同时在draw左上角显示了当前的部分参数,以及对不同的按键的不同定义:
import pygame
import time
import random
from Codes.Base.Screen import _ScreenInstance
from Codes.Logic.GameStats import _GameStatsInstance
from Codes.Common.DrawBase import DrawOn,_DrawCallInstance
from Codes.Base.GameSettings import _GameSettingsInstance
from Codes.Input.Handle import _HandleInstance
from Codes.Battle import _BattleInstance
class Ship(DrawOn):
def __init__(self):
super().__init__()
super(Ship,self).definePriority(2)
def init(self):
self.ship_speed_factor=_GameSettingsInstance.ship_speed_factor
self.image = pygame.image.load(r'D:/MyG/images/plane.png')
self.image2 = pygame.image.load(r'D:/MyG/images/plane2.png')
self.image = pygame.transform.scale(self.image,(80,100))
self.image2 = pygame.transform.scale(self.image2,(80,100))
self.rect = self.image.get_rect()
self.screen=_ScreenInstance.screen
self.screen_rect = _ScreenInstance.screen.get_rect()
self.rect.centerx = self.screen_rect.centerx
self.rect.centery = self.screen_rect.centery
self.rect.bottom = self.screen_rect.bottom
self.pos_x=float(self.rect.centerx)
self.pos_y=float(self.rect.centery)
self.moving_right=False
self.moving_left=False
self.moving_up=False
self.moving_down=False
self.handleInit()
#配置当前主角的信息
def handleInit(self):
_HandleInstance.add(pygame.K_a,self.left_Button)
_HandleInstance.add(pygame.K_w,self.up_Button)
_HandleInstance.add(pygame.K_s,self.down_Button)
_HandleInstance.add(pygame.K_d,self.right_Button)
_HandleInstance.add(pygame.K_p,self.pause) #打开商店
_HandleInstance.add(pygame.K_o,self.quitpause)#商店退出 继续游戏
_HandleInstance.add(pygame.K_e,self.use)#道具使用
_HandleInstance.add(pygame.K_r,self.position_reset)
_HandleInstance.add(pygame.K_SPACE,self.hit)
def draw(self):
if time.time()%1>0.5:
self.screen.blit(self.image, self.rect)
else:
self.screen.blit(self.image2, self.rect)
#显示分数
text="得分:"+str(_BattleInstance.score)
font1 = pygame.font.SysFont('kaiti', 16)
textSurfaceObj = font1.render(text, True, (0,255,255))
textRectObj = textSurfaceObj.get_rect()
textRectObj.center=(40,10)
self.screen.blit(textSurfaceObj, textRectObj)
#显示金钱
text="金币:"+str(_BattleInstance.gold)
font1 = pygame.font.SysFont('kaiti', 16)
textSurfaceObj = font1.render(text, True, (0,255,255))
textRectObj = textSurfaceObj.get_rect()
textRectObj.center=(200,10)
self.screen.blit(textSurfaceObj, textRectObj)
text="剩余子弹数:"+str(_GameSettingsInstance.bullets_allowed-len(_BattleInstance.bullets))
font1 = pygame.font.SysFont('kaiti', 16)
textSurfaceObj = font1.render(text, True, (0,255,255))
textRectObj = textSurfaceObj.get_rect()
textRectObj.center=(60,40)
self.screen.blit(textSurfaceObj, textRectObj)
text="剩余生命值:"+str(_GameStatsInstance.ships_left)
font1 = pygame.font.SysFont('kaiti', 16)
textSurfaceObj = font1.render(text, True, (0,255,255))
textRectObj = textSurfaceObj.get_rect()
textRectObj.center=(200,40)
self.screen.blit(textSurfaceObj, textRectObj)
tool=['无','清屏','屏障','超级加倍卡']
text="当前道具:"+tool[_GameSettingsInstance.use]
font1 = pygame.font.SysFont('kaiti', 16)
textSurfaceObj = font1.render(text, True, (0,255,255))
textRectObj = textSurfaceObj.get_rect()
textRectObj.center=(300,40)
self.screen.blit(textSurfaceObj, textRectObj)
def preDraw(self):
_DrawCallInstance.add(self)
def update(self,delta):
if self.moving_right and self.rect.right < self.screen_rect.right:
self.pos_x += self.ship_speed_factor*delta
if self.moving_left and self.rect.left > 0:
self.pos_x -= self.ship_speed_factor*delta
if self.moving_down and self.rect.bottom < self.screen_rect.bottom:
self.pos_y += self.ship_speed_factor*delta
if self.moving_up and self.rect.top > 0:
self.pos_y -= self.ship_speed_factor*delta
self.rect.centerx=self.pos_x
self.rect.centery=self.pos_y
self.preDraw()
def position_reset(self):
self.rect.centerx=_GameSettingsInstance.screen_width*0.5
self.pos_x=self.rect.centerx
self.rect.centery=_GameSettingsInstance.screen_height*0.8
self.pos_y=self.rect.centery
def hit(self):
_BattleInstance.hit();
def left_Button(self):
self.moving_left=self.moving_left^1
def right_Button(self):
self.moving_right=self.moving_right^1
def up_Button(self):
self.moving_up=self.moving_up^1
def down_Button(self):
self.moving_down=self.moving_down^1
def pause(self):
if _GameStatsInstance.game_active==True:
_GameStatsInstance.game_active=False
_GameStatsInstance.store_active=True
cost=[2000,10000,2000,1000,5000]#上面对应道具的价格
tool=['至尊VIP','永久加成卡','清屏','屏障','超级加倍卡']
length=len(tool)-_GameSettingsInstance.vip-_GameSettingsInstance.pac#还能购买的数量
item=random.randint(0,length-1)
if length==len(tool)-2:#0-2 ->2->4
item+=2
elif length==len(tool)-1 and _GameSettingsInstance.vip==1:#0123->1234
item+=1
elif length==len(tool)-1 and _GameSettingsInstance.vip==0:#0123->2340
if item!=0:
item+=1
_GameSettingsInstance.item=item
def quitpause(self):
if _GameStatsInstance.store_active==True:
_GameStatsInstance.game_active=True
_GameStatsInstance.store_active=False
def use(self):
#待编写
if _GameStatsInstance.game_active==True:
if _GameSettingsInstance.use==1:
_BattleInstance.aliens.empty()
_BattleInstance.stone.empty()
_GameSettingsInstance.use=0
def center_ship(self):
self.center=self.screen_rect.centerx
在handleinit函数里,将按键类型和操作联系到一起,并执行handle类的add添加事件。在ship类里同时对不同的操作进行了声明:wasd移动,空格射击(Batlle类的hit),p进入商店,o退出商店回到游戏,按p进入商店时,由于道具类只出现一种,在这里产生随机数,同时vip和pac(永久加成卡)只出现一次(购买之后就不会在出现),所以针对不同的情况对随机数做了一定的偏移。
战场Battle:在begingame被check后实例化,大部分的对象操作在这里完成:
import pygame
from time import sleep
import sys
import random
import math
import datetime
import re
from Codes.Kinds.Bullets import Bullet
from Codes.Kinds.Aliens import Alien
from Codes.Kinds.Stone import Stone
from Codes.Kinds.Bonus import Bonus
from pygame.sprite import Group
from Codes.Base.GameSettings import _GameSettingsInstance
from Codes.Logic.GameStats import _GameStatsInstance
from Codes.Base.Screen import _ScreenInstance
class Battle():
def init(self):#初始化战场元素
self.bullets = Group()
self.aliens = Group()
self.stone=Group()
self.bonus=Group()
self.host.position_reset()
self.score=0#积分
self.gold=0#金币--升级属性
def setHost(self, element):
self.host = element
def hit(self):#如果已经发射出的未碰撞/自杀的子弹小于子弹上限 继续装填子弹
if len(self.bullets) < _GameSettingsInstance.bullets_allowed:
new_bullet = Bullet()
new_bullet.init(self.host)
self.bullets.add(new_bullet)
def update(self, delta):
self.bullets.update(delta)
self.update_bullets(delta)
self.check_Bullet()
self.update_aliens(delta)
if self.score>10000:
#下一关
pygame.quit()
sys.exit()
def check_Bullet(self):#超过屏幕的子弹移除/自杀
for bullet in self.bullets.copy():
if bullet.rect.bottom <= 0:
#self.bullets.remove(bullet)
bullet.kill()
def create_alien(self):#生成一个外星人
alien = Alien()
self.aliens.add(alien)
print("create alien on :"+str(alien.rect.x)+','+str(alien.rect.y))
print("theta:"+str(alien.dir)+' v'+str(alien.v)+' a'+str(alien.a))
def create_stone(self):
s=Stone()
self.stone.add(s)
print("create stone on :"+str(s.rect.x)+','+str(s.rect.y))
def create_bonus(self):
b=Bonus()
self.bonus.add(b)
print("create bonus on :"+str(b.rect.x)+','+str(b.rect.y))
def update_aliens(self, delta):
self.check_fleet_edges()
self.aliens.update(delta)
self.stone.update(delta)
self.bonus.update(delta)
if pygame.sprite.spritecollideany(self.host, self.aliens):
self.ship_hit()
if pygame.sprite.spritecollideany(self.host, self.stone):
self.ship_hit()
self.check_aliens_bottom()
def check_fleet_edges(self):#检查每一个 如果到左右边则换方向 dir->180-dir 或者自尽 敌机和石头到边消失 而bonus则回折
for alien in self.aliens.sprites():
if alien.check_edges():
alien.kill()
for s in self.stone.sprites():
if s.check_edges():
s.kill()
for b in self.bonus.sprites():
if b.check_edges():
b.dir=180-b.dir
b.theta=b.dir*math.pi/180
def check_aliens_bottom(self):#外星人到底部 自杀
screen_rect = _ScreenInstance.screen.get_rect()
for alien in self.aliens.sprites():
if alien.rect.bottom >= screen_rect.bottom:
alien.kill()
for s in self.stone.sprites():
if s.rect.bottom >= screen_rect.bottom:
s.kill()
for b in self.bonus.sprites():
if b.rect.bottom >= screen_rect.bottom:
b.kill()
def ship_hit(self):#飞船撞到某玩意挂掉了复活
if _GameStatsInstance.ships_left > 0: #如果还剩下生命
_GameStatsInstance.ships_left -= 1
self.aliens.empty()#全杀
self.bullets.empty()
self.stone.empty()
self.host.center_ship()
self.host.position_reset()
print('died')
sleep(3)
else:#否则 游戏结束 积分计入txt
_GameStatsInstance.game_active = False
print('game over')
f= open(r'D:\MyG\rank.txt','r+')
f.read()
string=datetime.datetime.now().strftime('%Y-%m-%d')+' '+str(self.score)+'\n'
f.write(string)
f.close()
f= open(r'D:\MyG\rank.txt','r+')
lines=f.readlines()
f.close()
i=1
wr=[]
while i=988 and len(self.stone) < 1: #随机事件 产生石头
self.create_stone()
if i>998 and len(self.bonus) < 2:
self.create_bonus()
_BattleInstance = Battle()
Init:初始化 并重置主机的位置
Sethost:将(ship)设置为主要操作对象
Hit:[按下空格触发] 发射子弹:如果[按下空格后]当前子弹个数小于最大允许发射子弹个数,创建子弹放入
Update 刷新(一直会执行):不断执行子弹参数的更新和敌机(alien)和陨石/bonus[合并写入alien的不分函数 例如 update_aliens]
check_Bullet:检查所有子弹 如果到达屏幕上端 则自杀[kill](注释区的remove也可以)
Create_XXX:生成一个XXX
update_aliens:先调用check_fleet_edges(见下一个) 执行alien和陨石和bonus的update函数[在Alien等定义],检查本机和陨石、本机和石头是否碰撞[spritecollideany(A,B,bool) ,单精灵A和多个B进行检查碰撞 bool为False(默认) 则B碰撞后保留 为True则碰撞的经历删除],并对到达底部的alien/stone/bonus删除
check_fleet_edges:如果单个石头/敌机到达左右边缘 则自杀 如果bonus到达左右边缘 则反弹
check_aliens_bottom:如果单个石头/敌机/bonus到达底部 则自杀
ship_hit:[飞船受到alien/石头撞击后]如果剩下生命,那么生命值-1 并清空当前所有的子弹/敌机石头,本机的位置重置;如果不剩下生命了,就把积分和当前日期保存在rank中并对积分进行降序
update_bullets:子弹位置的更新,调用check_Bullet移除超出屏幕的点,并调用check_bullet_alien_collisions检查子弹和敌机的碰撞
check_bullet_alien_collisions 检查子弹和敌人、子弹和石头、子弹和bonus是否碰撞 [
Groupcollide(A,B,bool1,bool2) 多精灵A和B 如果bool1/2 为 TRUE 则 A/B消失 否则保留] 统计被碰撞(打中)而小时的敌机/bonus数量,增加对应的积分和金币,敌机等的数量会减少,如果数量小于某个阈值 进行create_XXX即可 在这里石头和bonus有一定的触发几率才会产生
其中的aliens、stone和bonus类:(非常相似)
import pygame
import random
import math
from pygame.sprite import Sprite
from Codes.Base.GameSettings import _GameSettingsInstance
from Codes.Base.Screen import _ScreenInstance
from Codes.Common.DrawBase import DrawOn,_DrawCallInstance
class Alien(Sprite,DrawOn):
def __init__(self):
super(Alien, self).__init__()
super(Alien,self).definePriority(3)
self.screen=_ScreenInstance.screen
self.image = pygame.image.load(r'D:/MyG/images/alien.png')
self.image = pygame.transform.scale(self.image,(int(_GameSettingsInstance.aliens_size),int(_GameSettingsInstance.aliens_size)))
self.rect = self.image.get_rect()
self.dir=random.randint(30,150)#运动的偏转角度 取值为30度到150度
self.theta=self.dir*math.pi/180#转为弧度制 便于计算
self.image=pygame.transform.rotate(self.image, 90-self.dir)
self.v=random.uniform(_GameSettingsInstance.aliens_v_min,_GameSettingsInstance.aliens_v_max)#初始速度
self.a=random.uniform(1,1.01)#加速度
self.rect.x = self.rect.width
self.rect.y = self.rect.height
self.rect.center=(random.randint(10,_GameSettingsInstance.screen_width),30)#随机生成点的位置
self.x = float( self.rect.x )
self.y = float( self.rect.y )
def draw(self):
#self.image=pygame.transform.rotate(self.image, 90-self.dir)
self.screen.blit(self.image, self.rect)
def preDraw(self):
_DrawCallInstance.add(self)
def update(self,delta):
#self.x+=(self.ai_settings.alien_speed_factor * self.ai_settings.fleet_direction)*delta
self.x+=math.cos(self.theta)*self.v
self.y+=math.sin(self.theta)*self.v
self.v*=self.a
self.rect.x =self.x
self.rect.y=self.y
self.rect.center=(self.x,self.y)
#print("now alien on :"+str(self.rect.x)+','+str(self.rect.y))
self.preDraw()
def check_edges(self):
screen_rect =_ScreenInstance.screen.get_rect()
if self.rect.right >= screen_rect.right:
return True
elif self.rect.left <=0:
return True
import pygame
import random
import math
from pygame.sprite import Sprite
from Codes.Base.GameSettings import _GameSettingsInstance
from Codes.Base.Screen import _ScreenInstance
from Codes.Common.DrawBase import DrawOn,_DrawCallInstance
class Stone(Sprite,DrawOn):
def __init__(self):
super(Stone, self).__init__()
super(Stone,self).definePriority(10)
self.screen=_ScreenInstance.screen
self.image = pygame.image.load(r'D:/MyG/images/stone.png')
self.scale=random.randint(80,160)
self.image = pygame.transform.scale(self.image,(self.scale,self.scale))
self.r=random.randint(30,150)
self.image=pygame.transform.rotate(self.image, self.r)
self.rect = self.image.get_rect()
self.dir=random.randint(70,110)#运动的偏转角度 取值为30度到150度
self.theta=self.dir*math.pi/180#转为弧度制 便于计算
self.v=random.uniform(1,2)#总速度为100-300
self.a=random.uniform(1,1.01)#加速度
self.rect.x = self.rect.width
self.rect.y = self.rect.height
self.rect.center=(random.randint(10,_GameSettingsInstance.screen_width),30)#随机生成点的位置
self.x = float( self.rect.x )
self.y = float( self.rect.y )
def draw(self):
#self.image=pygame.transform.rotate(self.image, 90-self.dir)
self.screen.blit(self.image, self.rect)
def preDraw(self):
_DrawCallInstance.add(self)
def update(self,delta):
#self.x+=(self.ai_settings.alien_speed_factor * self.ai_settings.fleet_direction)*delta
self.x+=math.cos(self.theta)*self.v
self.y+=math.sin(self.theta)*self.v
self.v*=self.a
self.rect.x =self.x
self.rect.y=self.y
self.rect.center=(self.x,self.y)
#
#self.r+=0.005
#print("now alien on :"+str(self.rect.x)+','+str(self.rect.y))
self.preDraw()
def check_edges(self):
screen_rect =_ScreenInstance.screen.get_rect()
if self.rect.right >= screen_rect.right:
return True
elif self.rect.left <=0:
return True
import pygame
import random
import math
from pygame.sprite import Sprite
from Codes.Base.GameSettings import _GameSettingsInstance
from Codes.Base.Screen import _ScreenInstance
from Codes.Common.DrawBase import DrawOn,_DrawCallInstance
class Bonus(Sprite,DrawOn):
def __init__(self):
super(Bonus, self).__init__()
super(Bonus,self).definePriority(12)
self.screen=_ScreenInstance.screen
self.image = pygame.image.load(r'D:/MyG/images/bonus.png')
self.image = pygame.transform.scale(self.image,(100,100))
self.rect = self.image.get_rect()
self.dir=random.randint(30,150)#运动的偏转角度 取值为30度到150度
self.theta=self.dir*math.pi/180#转为弧度制 便于计算
self.v=random.uniform(4,6)#总速度为100-300
self.a=random.uniform(1,1.01)#加速度
self.rect.x = self.rect.width
self.rect.y = self.rect.height
self.rect.center=(random.randint(10,_GameSettingsInstance.screen_width),30)#随机生成点的位置
self.x = float( self.rect.x )
self.y = float( self.rect.y )
def draw(self):
#self.image=pygame.transform.rotate(self.image, 90-self.dir)
self.screen.blit(self.image, self.rect)
def preDraw(self):
_DrawCallInstance.add(self)
def update(self,delta):
#self.x+=(self.ai_settings.alien_speed_factor * self.ai_settings.fleet_direction)*delta
self.x+=math.cos(self.theta)*self.v
self.y+=math.sin(self.theta)*self.v
self.v*=self.a
self.rect.x =self.x
self.rect.y=self.y
self.rect.center=(self.x,self.y)
#
#self.r+=0.005
#print("now alien on :"+str(self.rect.x)+','+str(self.rect.y))
self.preDraw()
def check_edges(self):
screen_rect =_ScreenInstance.screen.get_rect()
if self.rect.right >= screen_rect.right:
return True
elif self.rect.left <=0:
return True
其中check_edges为检查是否超过屏幕边缘,在battle里被某个函数调用,其他代码不列,完整代码分享至到资源了,但还没审核过,先百度网盘把:
链接:https://pan.baidu.com/s/1_IOMX0RqCeRXyYkdlWSAJw
提取码:u278
解压到D盘下运行enter即可