-
前言部分
鄙人本学期手贱选了一门Python,这也完全得益于公众号里铺天盖地的Python广告,让我对Python的印象深刻,觉得没有Python干不了的活。然而在下实在不才,一学期逃了N次课,直到期末考试,才意识到自己又浪费了很多时间。
追悔莫及之余,遂决定自己写这小小程序,于是乎,下了一堆压缩包,看了一些不知其所云的速成教程后,打开软件,成功运行HelloWorld之后,还是打开了百度。。。
没办法,差的太多,确实不会,在经过大概一周之后,我终于终于把这个难搞的坦克大战搞出来了。中间的过程属实痛苦,过于血腥,就不过多描述了。大伙直接看结果吧。本程序属于阉割之后又阉割版,少了很多功能。如原版中有各种各样的子弹,己方坦克还可以升级的玩法,在本程序中,这些功能均不存在!
本程序只是简单的生成一个金色的己方坦克,随机生成五个灰色的地方坦克,让他们开始战斗,一方被另一方的子弹击中后从屏幕移除,仅此而已。
应付一下老师平时的作业应该没问题吧。。。 -
关于程序所用到的技术
- pygame(这个是python生态系统的一个库,安装使用请自行百度,不过多赘述,可能在日后的某一天,鄙人也可能会总结一下)
- 面向对象
-
代码说明
- 系统设计思想与编程思路
在本游戏编写过程中,首先使用ptgame搭建了基本的游戏窗口。通过分析,又得出游戏中的对象有:己方坦克,敌方坦克,墙,并把它们都封装成了相应的类。在本程序中通过一个主游戏逻辑类来完成各个对象的调用,和游戏的运行。最后通过调用主游戏逻辑类来运行本游戏。己方坦克和敌方坦克在功能和属性上又存在诸多相同之处,所以在本游戏中敌方坦克继承了己方坦克。又鉴于本程序中坦克和子弹需要用到精灵的碰撞方法,所以创建了一个继承至精灵类的中间类,然后让坦克类和精灵类继承至中间类,这样两个不同的类既可以都使用精灵类的功能,又没有任何的关联,也是体现了高内聚、低耦合的程序设计思想。
在程序的主逻辑类中会创建很多类变量来供后面的程序使用,这些类变量主要就是程序中所用到的一些对象的细化、比如具体子弹数量,具体地方坦克数量等。这些大于1的对象都会被存放在集合中,接着深入主逻辑类会有pygame的主循环和一些绘制对象的方法,在主循环里会不停的监测事件并调用各个绘制的方法。在每个绘制方法中也都通过具体的需求做了相应的逻辑判断。 - 游戏中的类
MainGame:主游戏逻辑类
Window:坦克游戏的运行窗口,所有的图像都会绘制在这里
TANK_P1:用于存储己方坦克
ENEMY_TANKS:存储地方坦克的列表
EnemyTank_count:存储敌方坦克的数量,数量为5,便于其它地方调用
BULLET_LIST:用于存储己方坦克发射的子弹的列表
E_BULLET_LIST:用于存储敌方坦克发射的子弹的列表
EXPLODES:用于存储屏幕中的爆炸效果的列表
WALLS:用于存储游戏中的墙的列表
SCREEN_WIDTH:屏幕的宽度
SCREEN_HEIGHT:屏幕的高度
init():本类的构造方法,主要用于调用stattGame()方法
startGame():游戏主程序,游戏开始的主接口,所有的事件监测和其它方法的调用
endgame():结束游戏的运行并关闭窗口
createEnemyTank():随机参数创建敌方坦克,并加入敌方坦克列表
blitEnemyTank():遍历敌方坦克列表,并调用每个敌方坦克的绘制方法,在窗口显示
blitBullet():遍历己方子弹列表,并调用每个子弹的绘制方法,在窗口中显示
blitEnemyBullet():遍历敌方子弹列表,并调用每个子弹的绘制方法,在窗口中显示
blitExplode():遍历爆炸效果列表,并调用每个爆炸的绘制方法,在窗口中显示
creatWalls():随机创建窗口中的墙,并加入墙列表
blitWalls():遍历墙列表,并调用每面墙的绘制方法,在窗口中显示
- 系统设计思想与编程思路
BaseItem:继承至pygame的精灵类,主要用于让子弹类和坦克类能够使用精灵的碰撞方法
init():本类的构造方法,用于执行精灵类的构造方法
Tank(BaseItem):己方坦克类,继承至BaseItem,需要传递left和top两个参数
self.images:用于存储坦克上下左右四个状态时的图像
self.direction:坦克当前的方向
self.image:坦克当前显示的图像,取决于方向
self.rect:存储了坦克的left,top,width,height
self.speed:坦克移动的速度
self.stop:坦克的停止状态,用于解决持续按下方向键坦克移动问题
self.live:坦克的生存状态,被击中为False
self.oldLeft:记录坦克上一次的left,用于解决坦克与墙的碰撞问题
self.oldTop:记录坦克上一次的top,用于解决坦克与墙的碰撞问题
move():根据坦克的方向来移动坦克
stayPosition():使坦克的坐标回到上一次,用于解决墙壁的碰撞
tankHitWall():用于检测坦克和墙壁的碰撞
shot():用于坦克发射子弹,返回的是一个Bullet
drawTank():在游戏窗口中绘制坦克
EnemyTank(Tank):敌方坦克类,继承至Tank,需要传递left,top和speed三个参数
self.images:重写了父类的images,用于存储地方坦克四个方向的图像
self.direction:随机给敌方坦克的初始方向
self.speed:敌方坦克的移动速度
self.step:敌方坦克在一个方向一次移动的像素数,用于解决敌方坦克随机移动问题
self.stop:敌方坦克的移动状态
randDirection():随机返回一个坦克的方向,供direction属性调用
randMove():敌方坦克的随机移动方法
shot():敌方坦克随机发射子弹
drawEnemyTank(self):在游戏窗口中绘制敌方坦克
Bullet(BaseItem):子弹类,继承至BaseItem类,需要传递一个tank参数,主要是为了子弹发射的位置
self.image:子弹的图像
self.direction:子弹的方向,和坦克的方向一致,主要是为了设定子弹的运动方向
self.rect:子弹的left,top,width和height
elf.speed:子弹的移动速度
self.live:决定子弹是否在屏幕中消失
bulletMove():根据子弹的方向运动子弹
drawBullet():在游戏窗口中绘制子弹
hitEnemyTank():己方坦克的子弹击中敌方坦克后产生爆炸效果并改变live
hitOurTank():敌方的子弹击中己方后产生爆炸效果并改变live
hitWall():子弹打中墙后在游戏窗口中消失
Explode():爆炸效果类,在坦克被击中处产生一个爆炸效果,需要一个tank参数
self.rect:爆炸效果的left,top,width和height
self.index:用于遍历images属性中图片的索引
self.images:存储爆炸效果的各个图片
self.image:爆炸效果当前显示的图片
self.live:决定爆炸效果的显示状态
drawExplode():在屏幕中绘制爆炸效果
Wall():墙类,游戏中的障碍物,阻挡所有坦克的行进,所有子弹都无法穿墙,需要传递left,和top两个参数
self.image:存储墙的图像
self.rect:墙壁的left,top,width和height,取决于传递的两个参数
drawWall():在游戏窗口中绘制墙壁
-
代码部分
写这些代码的时候,为了防止自己日后看不懂,特意留下了很详细的注释,大概的逻辑大伙应该可以看得出来。可能在日后的某天鄙人也会继续完善一下本帖,总之,水平有限,仅供参考。。。
这个是程序中img文件夹中的内容,网上搜一下应该可以搜到这个素材包
这个是代码的运行截图
import pygame,time,random
# 主游戏逻辑类
class MainGame():
# 游戏主窗口
window = None
# 屏幕宽和高
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 500
# 创建己方坦克
TANK_P1 = None
# 创建列表用于存储敌方坦克
ENEMY_TANKS = []
# 敌方坦克的数量
EnemyTank_count = 5
# 创建列表用于存储我方坦克发射的子弹
BULLET_LIST = []
# 创建列表用于存储敌方坦克发射的子弹
E_BULLET_LIST = []
# 创建列表用于存储屏幕中的爆炸效果
EXPLODES = []
# 创建列表用于存储墙壁
WALLS = []
def __init__(self):
self.startGame()
# 开始游戏
def startGame(self):
pygame.init()
# 设置窗口大小
MainGame.window = pygame.display.set_mode([MainGame.SCREEN_WIDTH,MainGame.SCREEN_HEIGHT])
# 加载坦克初始位置
MainGame.TANK_P1 = Tank(385,350)
# 加载敌方坦克初始位置
self.createEnemyTank()
# 创建墙壁
self.creatWalls()
# 设置窗口标题
pygame.display.set_caption("坦克大战")
# 游戏主程序
while True:
pygame.display.update()
# 给主窗口背景填充黑色
MainGame.window.fill(pygame.Color(0,0,0))
# 获取事件并执行相应的操作
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.endGame()
elif event.type == pygame.KEYDOWN:
if MainGame.TANK_P1 and MainGame.TANK_P1.live:
if event.key == pygame.K_LEFT: # 坦克向左移动
MainGame.TANK_P1.direction = "L"
MainGame.TANK_P1.stop = False
elif event.key == pygame.K_RIGHT: # 坦克向右移动
MainGame.TANK_P1.direction = "R"
MainGame.TANK_P1.stop = False
elif event.key == pygame.K_UP: # 坦克向上移动
MainGame.TANK_P1.direction = "U"
MainGame.TANK_P1.stop = False
elif event.key == pygame.K_DOWN: # 坦克向下移动
MainGame.TANK_P1.direction = "D"
MainGame.TANK_P1.stop =