坦克大战小游戏的实现
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文件夹
3.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