坦克大战小游戏的实现
1 设计任务
1.1设计目的
运用所学Python知识,完成坦克大战小游戏,通过实践加强对所学知识的理解和巩固。
1.2设计内容
坦克大战小游戏
1.3设计指标或者要求
坦克可以自由的移动和进行射击,敌方坦克消灭完即为胜利,已方坦克被击毁或者己方大本营被摧毁,即为游戏失败。
2 设计过程
游戏基本规则:按上下左右键移动我方坦克,按空格键进行发射子弹,击中坦克,坦克爆炸消失。若我方坦克被子弹击中或撞上,我方坦克死亡按ESC键可以重生。
敌方坦克功能:白色敌方坦克为一般坦克。白色较小敌方坦克速度快,射击子弹频繁。绿色坦克为我方坦克。
地图:白色“铁”墙壁,子弹不能穿过,坦克不能穿过。黑色“地图”子弹和坦克均可以穿过此地图。
程序包含六个类:主逻辑类(MainGame)、坦克类(Tank1包含敌方坦克类(EnemyTank)和我方坦克类(myTank))、子弹类(Bullet)、 爆炸效果类(Explode),墙壁类(Wall)、音效类(Music)。
2.1 开发环境
1、开发工具:pycharm 2020
2、开发环境:python 3.7.9
3、第三方模块:pygame
在联网的情况下,打开终端,输入pip install pygame,即可安装成功。
2.2程序基本流程
1、运行:run “Main_Game.py”,进入初始化界面
2、初始化:显示窗口大小及内容、加载图片和背景音乐,开始游戏。
3、游戏控件:玩家可以通过键盘和空格键来完成移动坦克,射击子弹。还可通过按ESC键重生
4、游戏结束:关闭窗口
2.3程序界面的展示
2.3.1开始游戏界面
2.3.2结束游戏界面
3各功能模块代码实现
模块导入:import random
import pygame,time
图片素材文件存入项目下的img文件夹
.1主逻辑类(MainGame) 主要是游戏窗口的创建和调用其他类中方法的实现,并定义getEvent方法来获取程序期间的所有事件(鼠标事件,键盘事件),通过键盘操作不同的按键来执行不同的操作。 class MainGame(): # 显示主窗口 window = None SCREEN_height = 700 SCREEN_width = 1000 #创建我方坦克 Tank_P1=None #存储所有敌方坦克 EnemyTank_list=[] #要创建的地方坦克的数量 EnemyTank_count=5 #存储我方子弹的列表 Bullet_list=[] #存储敌方坦克列表 Enemy_Bullet_list=[] #爆炸效果列表 Explode_list=[] #创建墙壁列表 Wall_list=[] #创建我方老鹰 Glede_P1=None #我方坦克的生命值 MyT_liveCount=5 # 开始游戏方法 def startGame(self): pygame.display.init() #创建窗口加载窗口(借鉴官方文档) MainGame.window = pygame.display.set_mode([MainGame.SCREEN_width, MainGame.SCREEN_height]) #创建我方坦克 self.CreatMyTank() #创建敌方坦克 self.creatEnemyTank() #创建墙壁 self.creatWall() #初始化glede方法 MainGame.Glede_P1=glede() # 设置一下游戏标题 pygame.display.set_caption("坦克大战v1.0") # 让窗口持续刷新 while True: # 给窗口一个刷新的颜色 MainGame.window.fill(COLOR_BLACK) #在循环中持续完成事件的获取 self.getEvent() #将绘制文字的小画布黏贴到窗口中 MainGame.window.blit(self.getTextSurface("剩余敌方坦克%d辆"%len(MainGame.EnemyTank_list)),(5,5)) if len(MainGame.EnemyTank_list)==0: MainGame.window.blit(self.getTextSurface2("你赢了!"), (420, 240)) if MainGame.Glede_P1 and MainGame.Glede_P1.live==True: # 将我方的老鹰展示到窗口中 self.Glede_P1.displayGlede() else: del MainGame.Glede_P1 MainGame.Glede_P1 = None # 展示结束图片效果 self.displayEndGame() if MainGame.Tank_P1 and MainGame.Tank_P1.live==True: # 将我方坦克加入到窗口中 MainGame.Tank_P1.displayTank() else: del MainGame.Tank_P1 MainGame.Tank_P1=None if MainGame.Glede_P1 and MainGame.Glede_P1.live and MainGame.MyT_liveCount>0 and len(MainGame.EnemyTank_list)>0: MainGame.window.blit(self.getTextSurface1("剩余生命数%d,按ESC键重生!" %MainGame.MyT_liveCount ),(330, 250)) else: # 展示结束图片效果 self.displayEndGame() #判断事件类型是否为按键按下,如果是,继续判断是哪一个按键,来进行对应的处理 if event.type==pygame.KEYDOWN: if MainGame.Tank_P1 and MainGame.Tank_P1.live and MainGame.Glede_P1 and MainGame.Glede_P1.live: # 具体按哪一个键的处理 if event.key == pygame.K_LEFT: print("坦克向左调头移动") # 修改坦克方向 MainGame.Tank_P1.direction = 'L' # 完成移动操作(调用坦克移动方法) MainGame.Tank_P1.stop = False 【省略其他方向移动的代码】 elif event.key == pygame.K_SPACE: print("坦克发射子弹") if len(MainGame.Bullet_list) < 3: # 产生一颗子弹 m = Bullet(MainGame.Tank_P1) # 将子弹加入子弹列表 MainGame.Bullet_list.append(m) # 播放音乐 # music = Music('img/fire.wav') # music.play() else: print("子弹数量不足") # 松开按键时坦克停止移动 if event.type == pygame.KEYUP: #松开的是方向键才停止移动 if event.key==pygame.K_LEFT or event.key==pygame.K_UP or event.key==pygame.K_DOWN or event.key==pygame.K_RIGHT: if MainGame.Tank_P1 and MainGame.Tank_P1.live: # 修改坦克的状态 MainGame.Tank_P1.stop = True #左上角文字绘制功能 def getTextSurface(self,text): #初始化字体模块 pygame.font.init() #选中一个合适的字体 font=pygame.font.SysFont('kaiti',18) #使用对应的字符完成相关内容的绘制 textSurface=font.render(text,True,COLOR_RED) return textSurface def getTextSurface1(self, text): # 初始化字体模块 pygame.font.init() # 选中一个合适的字体 font = pygame.font.SysFont('kaiti', 28) # 使用对应的字符完成相关内容的绘制 textSurface1 = font.render(text, True, COLOR_BLUE) return textSurface1 def getTextSurface2(self, text): # 初始化字体模块 pygame.font.init() # 选中一个合适的字体 font = pygame.font.SysFont('kaiti', 35) # 使用对应的字符完成相关内容的绘制 textSurface1 = font.render(text, True, COLOR_RED) return textSurface1 # 结束游戏方法 def endGame(self): print("Game over!!!") # 结束python解释器 exit() #展示结束游戏图片 def displayEndGame(self): self.image1=pygame.image.load("img/GameOver.jpg") #图片所在区域 self.rect=self.image1.get_rect() self.rect.left=210 self.rect.top=110 MainGame.window.blit(self.image1, self.rect) 3.2坦克类(Tank1,myTank,EnemyTank) class Tank1(BaseItem): def __init__(self,left,top): self.images = { 'U':pygame.image.load("img\p1tankU.gif"), 'D':pygame.image.load("img\p1tankD.gif"), 'L':pygame.image.load("img\p1tankL.gif"), 'R':pygame.image.load("img\p1tankR.gif") } self.direction='U' self.image=self.images[self.direction] #坦克所在区域 self.rect=self.image.get_rect() #指定坦克初始化位置 分别距X,Y轴的位置 self.rect.left=left self.rect.top=top #速度属性 self.speed=5 #坦克的移动开关 self.stop=True #新增步数属性 self.step=15 #新增属性live用来记录坦克是否活着 self.live=True #新增属性:用来记录坦克移动之前的坐标(用于坐标还原时使用) self.oldLeft=self.rect.left self.oldTop=self.rect.top #坦克移动的方法 def move(self): self.oldLeft = self.rect.left self.oldTop = self.rect.top if self.direction=='L': if self.rect.left>0: self.rect.left-=self.speed 【省略其他方向移动的代码】 def stay(self): self.rect.left=self.oldLeft self.rect.top=self.oldTop #新增碰撞墙壁的方法 def hitWalls(self): for wall in MainGame.Wall_list: if pygame.sprite.collide_rect(wall,self): self.stay() #射击方法 def shot(self): return Bullet(self) #展示展示 (将坦克这个surface绘制到窗口中 blit()) def displayTank(self): #1.重新设置坦克的图片 self.image=self.images[self.direction] #2.将坦克加入到窗口中 MainGame.window.blit(self.image, self.rect) class MyTank(Tank1): def __init__(self,left,top): super(MyTank, self).__init__(left,top) #新增主动碰撞到敌方坦克的方法 def hitEnemyTank(self): for eTank in MainGame.EnemyTank_list: if pygame.sprite.collide_rect(eTank,self): self.stay() class EnemyTank(Tank1): def __init__(self,left,top,speed): super(EnemyTank,self).__init__(left,top) self.images = { 'U': pygame.image.load("img\enemy1U.gif"), 'D': pygame.image.load("img\enemy1D.gif"), 'L': pygame.image.load("img\enemy1L.gif"), 'R': pygame.image.load("img\enemy1R.gif") } self.direction = self.randDirection() self.image = self.images[self.direction] # 坦克所在区域 self.rect = self.image.get_rect() # 指定坦克初始化位置 分别距X,Y轴的位置 self.rect.left = left self.rect.top = top # 速度属性 self.speed = speed # 坦克的移动开关 self.stop = True #步数属性 self.step=35 #判断方向 def randDirection(self): num=random.randint(1,4) if num == 1: return 'U' elif num == 2: return 'D' elif num == 3: return 'L' elif num == 4: return 'R' #随机移动 def randMove(self): if self.step <= 0: self.direction = self.randDirection() self.step = 35 else: self.move() self.step -= 1 #展示敌方坦克 def displayEnemyTank(self): super().displayTank() #敌方坦克发射子弹的概率 def shot(self): num=random.randint(0,1000) if num<=20: return Bullet(self) #敌方坦克碰到我方坦克时让其停下来 def hitMyTank(self): if MainGame.Tank_P1 and MainGame.Tank_P1.live: if pygame.sprite.collide_rect(selfMainGame.Tank_P1): self.stay() 3.3子弹类(Bullet) class Bullet(BaseItem): def __init__(self,tank): #子弹速度属性 self.speed=7 #子弹是否活着 self.live=True #图片 self.image=pygame.image.load("img/enemymissile.gif") #方向 self.direction=tank.direction #位置 self.rect=self.image.get_rect() #子弹的移动方法 def BulletMove(self): 跟坦克移动方法类似 #展示子弹的方法 def dispalyBullet(self): MainGame.window.blit(self.image,self.rect) #我方子弹碰撞敌方坦克的方法 def hitEnemyTank(self): for eTank in MainGame.EnemyTank_list: if pygame.sprite.collide_rect(eTank,self): #产生一个爆炸效果 explode=Explode(eTank) #将爆炸效果加入到爆炸列表 MainGame.Explode_list.append(explode) self.live=False eTank.live=False #敌方子弹碰撞我方坦克的方法 def hitMyTank(self): if pygame.sprite.collide_rect(self,MainGame.Tank_P1): #产生爆炸效果,并加入到爆炸效果列表中 explode=Explode(MainGame.Tank_P1) MainGame.Explode_list.append(explode) #修改子弹状态 self.live=False #修改我方坦克状态 MainGame.Tank_P1.live=False #子弹与墙壁的碰撞 def hitWalls(self): for wall in MainGame.Wall_list: #两个精灵之间的碰撞检测,使用矩形。 if pygame.sprite.collide_rect(wall,self): #修改子弹的live属性 self.live=False wall.hp-=1 if wall.hp<=0: wall.live=False 3.4爆炸效果类(Explode) class Explode(): def __init__(self,tank): self.rect=tank.rect self.step=0 self.live=True self.images=[ pygame.image.load('img/blast0.gif'), pygame.image.load('img/blast1.gif'), pygame.image.load('img/blast2.gif'), pygame.image.load('img/blast3.gif'), pygame.image.load('img/blast4.gif') ] self.image=self.images[self.step] #展示爆炸效果 def displayExplode(self): if self.step<len(self.images): MainGame.window.blit(self.image,self.rect) self.step+=1 else: self.live=False self.step=0 3.5墙壁类(Wall) class Wall(): def __init__(self,left,top): #导入墙壁图像文件 self.image=pygame.image.load('img/steels.gif') self.rect=self.image.get_rect()#图像在窗口中显示的位置 self.rect.left=left self.rect.top=top #用来判断是否在窗口中显示 self.live=True #用来记录墙壁的生命值 self.hp=3 #展示墙壁的方法 def displayWall(self): MainGame.window.blit(self.image,self.rect) 3.6音效类(Music) class Music(): def __init__(self, fileName): self.fileName = fileName # 初始化混合器 pygame.mixer.init() #导入音效文件 pygame.mixer.music.load(self.fileName) # 开始播放音乐 def play(self): pygame.mixer.music.load(self.fileName)
4 软件设计过程中遇到的问题以及解决办法
4.1我方子弹发射数量问题
如果只定义一个子弹属性的话,则玩来玩去只是一枚子弹,我们这时需要用容器。
在MainGame类属性中添加一个Bullet_list=[]用于存储子弹列表,然后在MainGame中的blitBullet方法中将其遍历,并将子弹加入到窗口中。
当然,我们也要在blitmyTank方法中遍历MainGame.myTank_list列表,并将子弹存储在我方坦克列表,其代码为MainGame.my_Bullet_list.append(eBullet),即可发射多枚子弹,但我们就游戏体验来说最好将子弹发射数量设置为每次只能发送3颗,以维持游戏的平衡。
4.2坦克移动问题
先用pygame.event.get()方法获取到程序期间的所有事件,然后赋值给eventList,遍历这个列表,对不同的键盘按键操作进行不同的处理。当按下上下左右键时,调用不同的方法,根据按键的不同,展示不同方向的坦克,当持续按一个方向键时,让坦克朝着这个方向按给定的速度进行平移,以达到坦克移动方法的实现。
4.3我方子弹与敌方坦克碰撞问题
我方子弹碰撞敌方坦克时,我方子弹和敌方坦克应均处于死亡(属性live=False)状态,要实现这个效果,即要调用pygame模块里的精灵类sprite,首先创建一个类BaseItem使其继承于精灵类,然后让Tank1类和Bullet类继承于BaseItem类,并用模块中的pygame.sprite.collide_rect方法检测两个精灵的碰撞,如果我方子弹,碰撞到了敌方坦克,即将敌方坦克的live属性改成False,即敌方坦克便不会显示在窗口中。
这里是引用
程序源码下载链接:https://download.csdn.net/download/qq_41787812/19967878