python编写坦克大战过程

目录

一、首先明白编写坦克大战有哪些需求

二、细节编写

1、完成游戏的主要逻辑

2、完成坦克

1)完成我方坦克

2)完成敌方坦克

3、完成子弹

1)我方坦克射击

2)敌方坦克

3)我方子弹和敌方坦克碰撞

 4)敌方子弹与我方坦克的碰撞

5)实现我方坦克的重生

4、爆炸效果

1)敌方坦克

2)我方坦克

 5、墙壁实现

1)子弹与墙壁碰撞

2)坦克不能穿墙

3)实现坦克之间的碰撞检测

6、音效处理

三、代码

1、成果

2、缺点


坦克大战大量应用pygame包,https://www.pygame.org/docs/,可以阅读pygame文档熟悉相关的功能

一、首先明白编写坦克大战有哪些需求

       应该思考有哪些类,以及其功能有什么。

  1. 主逻辑类:开始游戏,结束游戏,获取事件(比如鼠标事件、键盘事件)并处理
  2. 坦克类(有我方坦克,和敌方坦克):移动,射击,坦克的展示
  3. 子弹类:移动,子弹的展示
  4. 爆炸效果类:爆炸效果的展示
  5. 障碍物类(该项目只考虑墙壁):属性(是否可以通过),墙的展示
  6. 音效类:播放音乐

先开始编写框架

import pygame
class MainGame():
    def __init__(self):
        pass
    def startGame(self):#开始游戏
        pass    
    def getEvent(self):#获取事件,比如鼠标事件、键盘事件
        pass
    def endGame(self):#结束游戏
        pass
class  Tank():
    def __init__(self):
        pass
    def move(self):#坦克移动
        pass
    def shot(self):#坦克射击
        pass
    def displayTank(self):#坦克进行显示
        pass
class MyTank(Tank):
    def __init__(self):
        pass 
class EnemyTank(Tank):
    def __init__(self):
        pass
class Bullet():
    def __init__():
        pass
    def bulletMove(self):#子弹移动
        pass
    def dispalyBullet(self):#子弹显示
        pass
class Explode():
    def __init__(self):
        pass
    def displayExplode(self):#展示子弹
        pass
class Wall():
    def __init__(self):
        pass
    def dispalyWall(self):#展示墙壁
        pass
class Music():
    def __init__(self):
        pass
    def play(self):#开始播放音乐
        pass

二、细节编写

1、完成游戏的主要逻辑

import pygame
COLOR_BLACK = pygame.COLOR(0,0,0)

class MainGame():
    window = None#游戏主窗口
    SCREEN_WIDTH = 800
    SCREEN_HEIGHT = 500#窗口的宽和高

    def __init__(self):
        pass

    def startGame(self):#开始游戏
        pygame.display.init()
        MainGame.window =  pygame.display.set_mode([MainGame.SCREEN_WIDTH,MainGame.SCREEN_HEIGHT])#加载游戏窗口(看文档)
        pygame.display.set_caption('坦克大战')#设置标题

        while True:#让窗口持续刷新
            MainGame.window.fill(COLOR_BLACK)#填充为黑色
            pygame.display.update()

    def getEvent(self):#获取事件,比如鼠标事件、键盘事件
        pass

    def endGame(self):#结束游戏
        print('古德拜拜')
        exit(0)#结束Python解释器

在startGame里加入死循环,则可以一直显示窗口,但无法退出,所以要加入事件处理代码为:

    def startGame(self):#开始游戏
        pygame.display.init()
        MainGame.window =  pygame.display.set_mode([MainGame.SCREEN_WIDTH,MainGame.SCREEN_HEIGHT])#加载游戏窗口(看文档)
        pygame.display.set_caption('坦克大战')#设置标题

        while True:#让窗口持续刷新
            MainGame.window.fill(COLOR_BLACK)#填充为黑色
            self.getEvent()
            pygame.display.update()

    def getEvent(self):#获取事件,比如鼠标事件、键盘事件
        eventList = pygame.event.get()#获取
        for event in eventList:
            if event.type == pygame.QUIT:#判断是否点击退出
                self.endGame()
            if event.type == pygame.KEYDOWN:#判断按键按下,并判断是哪个键
                if event.key == pygame.K_LEFT:#左方向键
                    print('左')             
                elif event.key == pygame.K_RIGHT:
                    print('右') 
                elif event.key == pygame.K_UP:
                    print('上') 
                elif event.key == pygame.K_DOWN:
                    print('下') 
                elif event.key == pygame.K_SPACE:
                    print('射击') 

此后,运行时,按方向键会显示方向,鼠标点击叉号会退出。

为在屏幕中显示剩余坦克的文本,所以在MainGame中加入新方法:

COLOR_RED = pygame.Color(255,0,0)   #全局变量

def getTextSurface(self,text):
    pygame.font.init()#初始化字体模块
    font = pygame.font.SysFont('kaiti',18)#选中一个字体
    textSurface = font.render(text,True,COLOR_RED)#进行绘制
    return textSurface

在死循环中加入:(两个都是Surface,所以用blit函数,将一个Surface中的内容画到另一个)

 MainGame.window.blit(self.getTextSurface('剩余坦克%d'%5),(5,5))#将绘制文字的小画布粘贴到窗口中

2、完成坦克

1)完成我方坦克

在这里我们用到了坦克的照片,可以将这些照片先添加到项目中

python编写坦克大战过程_第1张图片

 需要的人儿可以下载链接:https://pan.baidu.com/s/1ueXXyqn_doGcRw3g9FiG1w 
提取码:jjgj

首先完成坦克的一个初始化

class  Tank():
    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()#坦克所在的区域
        self.rect.left = left
        self.rect.top = top#指定坦克的初始化位置,分别距离x,y轴的距离

    def move(self):#坦克移动
        pass

    def shot(self):#坦克射击
        pass

    def displayTank(self):#坦克进行显示(将坦克这个Surface绘制到窗口中,用blit())
        self.image = self.images[self.direction]#重置坦克的图像
        MainGame.window.blit(self.image,self.rect)#将坦克加入到窗口中

在MainGame中加入变量

TANK_P1 = None

 为使项目结构更清晰,在MainGame中定义一个创建我方坦克的函数:

    def creatMyTank(self):
        MainGame.TANK_P1 = Tank(650,600)#创建我方坦克

在MainGame的startGame修改为

    def startGame(self):#开始游戏
        pygame.display.init()
        MainGame.window =  pygame.display.set_mode([MainGame.SCREEN_WIDTH,MainGame.SCREEN_HEIGHT])#加载游戏窗口(看文档)
        self.creatMyTank#创建我方坦克
        pygame.display.set_caption('坦克大战')#设置标题

        while True:#让窗口持续刷新
            MainGame.window.fill(COLOR_BLACK)#填充为黑色
            self.getEvent()
            MainGame.window.blit(self.getTextSurface('剩余坦克%d'%5),(5,5))#将绘制文字的小画布粘贴到窗口中
            MainGame.TANK_P1.displayTank()#将我方坦克加入窗口
            pygame.display.update()

接下来实现移动,移动的原理:坦克距离窗口左侧为left值,距离窗口上侧为top值,坦克的移动实际就是两个值的变化,而坦克的调头就是新的图片。所以速度也是一个属性,在Tank的init加入

self.speed = 5

坦克的move函数:

    def move(self):#坦克移动
        if self.direction == 'L':
            self.rect.left -= self.speed
        elif self.direction == 'R':
            self.rect.left += self.speed
        elif self.direction == 'U':
            self.rect.top -= self.speed
        elif self.direction == 'D':
            self.rect.top += self.speed

同时还应在MainGame的按键事件处理改为

        for event in eventList:
            if event.type == pygame.QUIT:#判断是否点击退出
                self.endGame()
            if event.type == pygame.KEYDOWN:#判断按键按下,并判断是哪个键
                if event.key == pygame.K_LEFT:#左方向键
                    print('左')      
                    MainGame.TANK_P1.direction = 'L'#改变坦克的方向
                    MainGame.TANK_P1.move()#实现移动
                elif event.key == pygame.K_RIGHT:
                    print('右') 
                    MainGame.TANK_P1.direction = 'R'#改变坦克的方向
                    MainGame.TANK_P1.move()
                elif event.key == pygame.K_UP:
                    print('上') 
                    MainGame.TANK_P1.direction = 'U'#改变坦克的方向
                    MainGame.TANK_P1.move()
                elif event.key == pygame.K_DOWN:
                    print('下') 
                    MainGame.TANK_P1.direction = 'D'#改变坦克的方向
                    MainGame.TANK_P1.move()
                elif event.key == pygame.K_SPACE:
                    print('射击') 

但是此时坦克可以移出边界,所以在坦克的move函数增加判断条件:

        if self.direction == 'L':
            if self.rect.left > 0:
                self.rect.left -= self.speed
        elif self.direction == 'R':
            if self.rect.left + self.rect.height  0:
                self.rect.top -= self.speed
        elif self.direction == 'D':
            if self.rect.top + self.rect.height 

此时的坦克只有在按下方向键时才能移动,所以做优化,给坦克加一个开关,按键时打开开关,松开键时关闭,使得按下键就可一直移动:

在坦克的初始化中加入:

self.stop = True

在死循环中加入

            if MainGame.TANK_P1 and not MainGame.TANK_P1.stop:#坦克存在并且打开开关时允许移动
                MainGame.TANK_P1.move()

将MainGame中的getEvent函数中的move调用改为

MainGame.TANK_P1.stop = False

此外,还需要增加按键抬起,停止运动,所以在getEvent函数中增加新的条件判断:

            if event.type == pygame.KEYUP:#将坦克的移动状态修改
                MainGame.TANK_P1.stop = True

此时出现新的问题,就是移动时,按空格射击时,坦克会停止,因为空格抬起时,触发了条件,所以对抬起的按键加以判断

            if event.type == pygame.KEYUP:#将坦克的移动状态修改
                if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT or event.key == pygame.K_UP or event.key == pygame.K_DOWN:
                        MainGame.TANK_P1.stop = True

坦克移动太快的话,除了改变speed参数,还可以引入time库,在死循环的任意地方加入

            time.sleep(0.02)

为了使移动更加纵享丝滑,对手感进行优化:

MainGame中加入属性Num_Key = 0、getEvent的按键按下判断中添加:

                if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT or event.key == pygame.K_UP or event.key == pygame.K_DOWN:
                    MainGame.Num_Key += 1

按键抬起判断改为:

            if event.type == pygame.KEYUP:#将坦克的移动状态修改
                if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT or event.key == pygame.K_UP or event.key == pygame.K_DOWN:
                    MainGame.Num_Key -= 1
                if ((event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT or event.key == pygame.K_UP or event.key == pygame.K_DOWN) and MainGame.Num_Key == 0):
                        MainGame.TANK_P1.stop = True

2)完成敌方坦克

将Tank的初始化复制到EnemyTank,并做修改

class EnemyTank(Tank):
    def __init__(self,left,top,speed):
        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()#坦克所在的区域
        self.rect.left = left
        self.rect.top = top#指定坦克的初始化位置,分别距离x,y轴的距离
        self.speed = speed
        self.stop = True 
    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'

用到了随机模块,所以引入随机random库 ,在MainGame中添加参数:

    EnemyTank_list = []  #敌方坦克列表
    EnemyTank_count = 5#创建的坦克数量

在MainGame中添加新方法(用于产生敌方坦克和进行显示):

    def creatEnemyTank(self):#创建敌方坦克
        top = 100
        for i in range(MainGame.EnemyTank_count):
            left = random.randint(1,int(self.SCREEN_WIDTH/100)-1)#每次都随机生成一个数值
            speed = random.randint(3,4)
            eTtank = EnemyTank(left*100,top,speed)
            MainGame.EnemyTank_list.append(eTtank)

    def blitEnemyTank(self):#将坦克加入到窗口中
        for eTank in MainGame.EnemyTank_list:
            eTank.displayTank()

在startGame中调用

self.creatEnemyTank()

在其中的死循环中加入

self.blitEnemyTank()#将敌方坦克加入窗口

优化剩余坦克数量,将原来的显示剩余数量修改为:

MainGame.window.blit(self.getTextSurface('剩余坦克%d'%len(MainGame.EnemyTank_list)),(5,5))#将绘制文字的小画布粘贴到窗口中

 如果敌方坦克直接调用move函数,则坦克会向着一个方向一直移动,所以在EnemyTank新增步数属性:

self.step = 40#新增步数属性,用来控制敌方坦克随机移动

并在其中定义新的方法:

    def randMove(self):
        if self.step <= 0:
            self.direction = self.randDirection()#一定距离后改变方向
            self.step = 40#重置步数
        else:
            self.move()
            self.step -= 1

 将MainGame中的blitEnemyTank修改为:

    def blitEnemyTank(self):#将坦克加入到窗口中
        for eTank in MainGame.EnemyTank_list:
            eTank.displayTank()
            eTank.randMove()

3、完成子弹

1)我方坦克射击

完成子弹需要哪些东西:图片,方向,起始位置,速度

class Bullet():
    def __init__(self,tank):
        self.image = pygame.image.load('img/enemymissile.gif')#图像
        self.direction = tank.direction#方向
        self.rect = self.image.get_rect()
        self.speed = 7#速度
        if self.direction == 'U':#起始位置
            self.rect.left = tank.rect.left + tank.rect.width/2 - self.rect.width/2
            self.rect.top = tank.rect.top - self.rect.height
        elif self.direction == 'D':
            self.rect.left = tank.rect.left + tank.rect.width/2 - self.rect.width/2
            self.rect.top = tank.rect.top + tank.rect.height
        elif self.direction == 'L':
            self.rect.left = tank.rect.left - self.rect.width/2 - self.rect.width/2
            self.rect.top = tank.rect.top + tank.rect.width/2 - self.rect.width/2
        elif self.direction == 'R':
            self.rect.left = tank.rect.left + tank.rect.width
            self.rect.top = tank.rect.top - self.rect.width/2 + tank.rect.width/2

    def dispalyBullet(self):#子弹显示
        MainGame.window.blit(self.image,self.rect)

原理:坦克距离窗口左侧为left值,距离窗口上侧为top值,left和top都是图片的左上角相对于窗口

接下来的主逻辑:坦克发射一个子弹,则一个子弹加入一个子弹列表,主逻辑中完成所有子弹的一个显示。完善Tank里面的shot函数:

    def shot(self):#坦克射击,因为Bullet需要一个Tank参数,此时将自己传进去
        return Bullet(self)

我方发射子弹需要按键,所以在事件处理函数改为:

                elif event.key == pygame.K_SPACE:
                    m = Bullet(MainGame.TANK_P1)#产生一个子弹
                    MainGame.Bullet_list.append(m)
                    print('射击') 

同时需要在MainGame中添加新属性:

    Bullet_list = []#存储我方子弹的列表

接下来需要遍历所有子弹,进行显示,可以借鉴敌方坦克的逻辑,在MainGame定义新的方法

    def blitBullet(self):
        for bullet in MainGame.Bullet_list:
            bullet.displayBullet()

并在死循环里合适位置调用该函数:

self.blitBullet()#调用渲染子弹列表的方法

然后,实现子弹的移动,向左移动,则left减少,向上移动,则top减少

    def bulletMove(self):#子弹移动
        if self.direction == 'U':
            if self.rect.top > 0:
                self.rect.top -= self.speed
        elif self.direction == 'D':
            if self.rect.top < MainGame.SCREEN_HEIGHT - self.rect.height:
                self.rect.top += self.speed
        elif self.direction == 'L':
            if self.rect.left > 0:
                self.rect.left -= self.speed
        elif self.direction == 'R':
            if self.rect.left < MainGame.SCREEN_WIDTH - self.rect.width:
                self.rect.left += self.speed

在MainGame的blitBullet函数中的for循环加入:

bullet.bulletMove()

此时,我们的坦克可以无限发射,跟开挂一样,并且子弹不能消失,所以我们让子弹出界后消失,屏幕同一时间只能出现最多出现5发我方的子弹。可以给子弹加一个标签,出界后修改该标签,根据标签判断是不是要展示,则咋子弹的属性中加入:

self.live = True#判断子弹是否应存在

将bulletMove函数修改为:

    def bulletMove(self):#子弹移动
        if self.direction == 'U':
            if self.rect.top > 0:
                self.rect.top -= self.speed
            else:
                self.live = False
        elif self.direction == 'D':
            if self.rect.top < MainGame.SCREEN_HEIGHT - self.rect.height:
                self.rect.top += self.speed
            else:
                self.live = False
        elif self.direction == 'L':
            if self.rect.left > 0:
                self.rect.left -= self.speed
            else:
                self.live = False
        elif self.direction == 'R':
            if self.rect.left < MainGame.SCREEN_WIDTH - self.rect.width:
                self.rect.left += self.speed
            else:
                self.live = False

将MainGame的blitBullet修改为:

    def blitBullet(self):
        for bullet in MainGame.Bullet_list:
            if bullet.live:#如果子弹还存在,则展示,否则,移除子弹
                bullet.displayBullet()
                bullet.bulletMove()
            else:
                MainGame.Bullet_list.remove(bullet)

将MainGame的getEvent函数中的射击修改为:

                elif event.key == pygame.K_SPACE:
                    if len(MainGame.Bullet_list) < 5:
                        m = Bullet(MainGame.TANK_P1)#产生一个子弹
                        MainGame.Bullet_list.append(m)
                        print('射击') 

2)敌方坦克

在MainGame中加入敌方子弹列表:

    Enemy_bullet_list = []#存储敌方坦克

 并将blitEnemyTank修改为

    def blitEnemyTank(self):#将坦克加入到窗口中
        for eTank in MainGame.EnemyTank_list:
            eTank.displayTank()
            eTank.randMove()#坦克移动
            eBullet = eTank.shot()#敌方坦克射击,因为继承关系,所以有shot函数
            MainGame.Enemy_bullet_list.append(eBullet)#将敌方坦克加入列表

在MainGame中写一个展示敌方子弹的函数:

    def blitEnemyBullet(self):#将敌方子弹加入窗口
        for ebullet in MainGame.Enemy_bullet_list:
            if ebullet.live:#如果子弹还存在,则展示,否则,移除子弹
                ebullet.displayBullet()
                ebullet.bulletMove()#让子弹移动
            else:
                MainGame.Enemy_bullet_list.remove(ebullet)

然后在死循环中进行调用

self.blitEnemyBullet()#敌方坦克的子弹

此时会发现,移动时一直发射子弹,所以在EnemyTank类中重写shot函数,让其一定概率可以发射

    def shot(self):
        num = random.randint(1,50)
        if num == 1:
            return Bullet(self)

此时会有两种返回值,当不产生子弹,就会返回None,如果产生子弹,会返回Bullet,应在不产生子弹时,返回值不加入列表,否则会将产生的子弹顶替,blitEnemyTank修改为

    def blitEnemyTank(self):#将坦克加入到窗口中
        for eTank in MainGame.EnemyTank_list:
            eTank.displayTank()
            eTank.randMove()#坦克移动
            eBullet = eTank.shot()#敌方坦克射击,因为继承关系,所以有shot函数
            if eBullet:
                MainGame.Enemy_bullet_list.append(eBullet)#将敌方坦克加入列表

3)我方子弹和敌方坦克碰撞

要使用pygame的精灵类碰撞,所以要继承精灵类,项目中的Tank和Bullet都需要继承,则可以让他们先继承一个基础类,该基础类继承精灵类(在pygame的官方文档有使用案例)

class BaseItem(pygame.sprite.Sprite):
    def __init__():
        pygame.sprite.Sprite.__init__(self)
#将两个类添加父类
class Tank(BaseItem)
class Bullet(BaseItem)

在Tank中新增属性

        self.live = True

 父类定义的属性,子类要调用的话需要用父类的初始化方式,则在初始化加入

    def __init__(self,left,top,speed):
        super(EnemyTank,self).__init__(left,top)

在Bullet中新增函数:

    def hitEnemyTank(self):#新增我方子弹碰撞敌方坦克的方法
        for eTank in MainGame.EnemyTank_list:
            if pygame.sprite.collide_rect(eTank,self):
                self.live = False#子弹不再存活
                eTank.live = False#坦克不存活

MainGame的blitEnemyTank应加上条件:

    def blitEnemyTank(self):#将坦克加入到窗口中
        for eTank in MainGame.EnemyTank_list:
            if eTank.live:
                eTank.displayTank()
                eTank.randMove()#坦克移动
                eBullet = eTank.shot()#敌方坦克射击,因为继承关系,所以有shot函数
                if eBullet:
                    MainGame.Enemy_bullet_list.append(eBullet)#将敌方坦克加入列表
            else:
                MainGame.EnemyTank_list.remove(eTank)

MainGame的blitBullet的if中加入

bullet.hitEnemyTank()#调用我方子弹和敌方坦克的碰撞方法

 4)敌方子弹与我方坦克的碰撞

在Bullet类中定义新的方法:

    def hitMyTank(self):#新增敌方子弹和我方坦克
        #因为前面已经遍历了敌方子弹,所以这里只需要判断一个子弹
        if pygame.sprite.collide_rect(MainGame.TANK_P1,self):
            self.live = False#修改子弹状态
            MainGame.TANK_P1.live = False#修改我方坦克状态

 在MainGame的blitEnemyBullet中if下判断并调用:

                if MainGame.TANK_P1 and MainGame.TANK_P1.live:#坦克存在并活着才能调用
                    ebullet.hitMyTank()

我方坦克被打中后,子弹和我方坦克状态改变,接下来对我方坦克的展示进行逻辑处理,在死循环中的我方坦克显示加上条件判断,改为:

            if MainGame.TANK_P1 and MainGame.TANK_P1.live:
                MainGame.TANK_P1.displayTank()#将我方坦克加入窗口
            else:
                del MainGame.TANK_P1
                MainGame.TANK_P1 = None

 我方坦克被击中后,在动按键会报错,所以在按键KEYDOWN加上条件判断:

if MainGame.TANK_P1 and MainGame.TANK_P1.live:

 将KEYUP的第二个判断改为:

                if ((event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT or event.key == pygame.K_UP or event.key == pygame.K_DOWN) and MainGame.Num_Key == 0):
                    if MainGame.TANK_P1 and MainGame.TANK_P1.live:    
                        MainGame.TANK_P1.stop = True

5)实现我方坦克的重生

通过按ESC进行重生,所以修改getEvent方法,在KEYDOWN判断中增加:

                if event.key == pygame.K_ESCAPE and not MainGame.TANK_P1:#ESC按键重生
                    self.creatMyTank()

4、爆炸效果

1)敌方坦克

多个图片的切换实现爆炸效果的从小到大

class Explode():
    def __init__(self,tank):
        self.rect = tank.rect
        self.step = 0
        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]
        self.live = True#一开始要进行显示

    def displayExplode(self):#展示子弹
        if self.step < len(self.images):
            MainGame.window.blit(self.image,self.rect)
            self.image = self.images[self.step]
            self.step += 1
        else:
            self.step = 0
            self.live = False#不再进行显示

在MainGame中新加属性:

    Explode_list = []

在Bullet的hitEnemyTank中的if中调用,并且存放爆炸列表

explode = Explode(eTank)#产生一个爆炸效果
MainGame.Explode_list.append(explode)#将爆炸效果加入到爆炸效果列表

在MainGame中定义新方法:

    def displayExplodes(self):#新增方法,展示爆炸效果列表
        for explode in MainGame.Enemy_bullet_list:
            if explode.live:
                explode.displayExplode()
            else:
                MainGame.Explode_list.remove(explode)

接着在死循环中调用

self.displayExplodes()

2)我方坦克

在Bullet的hitMyTank中的if增加   

            explode = Explode(MainGame.TANK_P1)#产生爆炸效
            MainGame.Explode_list.append(explode)

 5、墙壁实现

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 dispalyWall(self):#展示墙壁
        MainGame.window.blit(self.iamge,self.rect)

墙壁类似敌方坦克,打掉后消失,所以在MainGame中定义新的列表:

    Wall_list = []#墙壁列表

并在MainGame定义新的方法用来生成墙:

    def creatWalls(self): #创建墙壁的方法
        for i in range(1,7):
            wall = Wall(130*i,400)
            MainGame.Wall_list.append(wall)

在startGame中调用:

        self.creatWalls()#调用展示墙壁的方法

并定义新的展示墙的方法:

    def blitWalls(self):
        for wall in MainGame.Wall_list:
            if wall.live:
                wall.dispalyWall()
            else:
                MainGame.Wall_list.remove(wall)

在死循环中进行调用:

            self.blitWalls()

1)子弹与墙壁碰撞

在子弹类中新增方法

    def hitWalls(self):#子弹与墙壁碰撞
        for wall in MainGame.Wall_list:
            if pygame.sprite.collide_rect(wall,self):
                self.live = False#修改属性
                wall.hp -= 1
            if wall.hp <= 0:
                wall.live = False

在MainGame的blitBullet和blitEnemyBullet中进行调用

2)坦克不能穿墙

思路为:记录坦克之前的坐标,如果坦克和墙壁碰撞,则还原为原来的坐标

Tank里新增属性

        self.oldLeft = self.rect.left#新增属性,记录坦克之前的坐标
        self.oldTop = self.rect.top

Tank的move里开头增加一样的,并在Tank中创建新方法:

    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()

 在死循环中对我方坦克进行调用,在允许移动的条件判断中加入:

MainGame.TANK_P1.hitWalls()

在MainGame的blitEnemyTank的if中加入

eTank.hitWalls()#调用敌方坦克与墙壁的碰撞方法

3)实现坦克之间的碰撞检测

我方坦克主动撞击敌方,则stay(),敌方主动撞击我方坦克,则敌方stay(),编写MyTank为

class MyTank(Tank):
    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()

在MainGame的blitEnemyTank的if中加入

MainGame.TANK_P1.hitEnemyTank()

在敌方坦克也定义一个

    def hitMyTank(self):
        if MainGame.TANK_P1 and MainGame.TANK_P1.live:
            if pygame.sprite.collide_rect(self,MainGame.TANK_P1):
                self.stay()

并在MainGame的blitEnemyTank进行调用

eTank.hitMyTank()

6、音效处理

编写类

class Music():
    def __init__(self,fileName):
        self.fileName = fileName
        pygame.mixer.init()
        pygame.mixer.music.load(self.fileName)

    def play(self):#开始播放音乐
        pygame.mixer.music.play()

在MainGame的creatMyTank进行调用:

    def creatMyTank(self):
        MainGame.TANK_P1 = MyTank(650,600)#创建我方坦克
        music = Music('img/start.wav')
        music.play()

在MainGame的getEvent进行调用:

                  elif event.key == pygame.K_SPACE:
                        if len(MainGame.Bullet_list) < 5:
                            m = Bullet(MainGame.TANK_P1)#产生一个子弹
                            MainGame.Bullet_list.append(m)
                            print('射击') 
                            music = Music('img/fire.wav')
                            music.play()

在MainGame的hitEnemyTank和hitMyTank调用:

            music = Music('img/hit.wav')
            music.play()

三、代码

1、成果

介个时候!!全部功能大工告成!!

再经过对代码的优化、改变,最终代码如下

import pygame,time,random
COLOR_BLACK = pygame.Color(0,0,0)
COLOR_RED = pygame.Color(255,0,0)

class MainGame():
    window = None#游戏主窗口
    SCREEN_WIDTH = 1300
    SCREEN_HEIGHT = 700
    TANK_P1 = None
    FLAG = None
    EnemyTank_list = []  #敌方坦克列表
    EnemyTank_count = 5#创建的坦克数量
    Bullet_list = []#存储我方子弹的列表
    Enemy_bullet_list = []#存储敌方坦克
    Num_Key = 0
    Explode_list = []#墙壁列表
    Wall_list = []#墙壁列表
    defeatNum = 10
    remainLive = 3

    def __init__(self):
        pass

    def startGame(self):#开始游戏
        pygame.display.init()
        MainGame.window =  pygame.display.set_mode([MainGame.SCREEN_WIDTH,MainGame.SCREEN_HEIGHT])#加载游戏窗口(看文档)
        self.creatMyTank()#创建我方坦克
        self.creatFlag()
        start = Music('img/start.wav')
        start.play()
        pygame.display.set_caption('坦克大战')#设置标题
        self.creatEnemyTank(MainGame.EnemyTank_count)
        self.creatWalls()#调用展示墙壁的方法

        while True:#让窗口持续刷新
            MainGame.window.fill(COLOR_BLACK)#填充为黑色
            self.getEvent()
            self.blitWalls()
            MainGame.window.blit(self.getTextSurface('还需要击败敌人%d'%MainGame.defeatNum),(5,5))#将绘制文字的小画布粘贴到窗口中
            MainGame.window.blit(self.getTextSurface('剩余命数%d'%MainGame.remainLive),(5,23))#将绘制文字的小画布粘贴到窗口中
            if MainGame.TANK_P1 and MainGame.TANK_P1.live:
                MainGame.TANK_P1.displayTank()#将我方坦克加入窗口
            else:
                del MainGame.TANK_P1#我方坦克被击中则删去该对象
                MainGame.TANK_P1 = None
            if MainGame.FLAG and MainGame.FLAG.live:
                MainGame.FLAG.dispalyFlag()#将旗帜加入窗口
            self.blitEnemyTank()#将敌方坦克加入窗口
            if MainGame.TANK_P1 and not MainGame.TANK_P1.stop:#坦克存在并且打开开关时允许移动
                MainGame.TANK_P1.move()
                MainGame.TANK_P1.hitWalls()
                MainGame.TANK_P1.hitEnemyTank()
            if len(self.EnemyTank_list) < 5:
                self.creatEnemyTank(MainGame.EnemyTank_count-len(self.EnemyTank_list))
            self.blitBullet()#调用渲染子弹列表的方法
            self.blitEnemyBullet()#敌方坦克的子弹
            self.displayExplodes()
            time.sleep(0.02)
            pygame.display.update()
            if MainGame.defeatNum == 0 or MainGame.remainLive == 0 or not MainGame.FLAG.live:
                break

        while True:
            self.blitEndWord()
            pygame.display.update()
    def creatMyTank(self):
        MainGame.TANK_P1 = MyTank(400,600)#创建我方坦克
        music = Music('img/add.wav')
        music.play()

    def creatEnemyTank(self,count):#创建敌方坦克     
        top = 100
        for i in range(count):
            rand = random.randint(1,3)
            if rand == 1:
                left = 100
            elif rand == 2:
                left = 650
            elif rand == 3:
                left = 1200
            speed = random.randint(3,4)
            eTtank = EnemyTank(left,top,speed)
            MainGame.EnemyTank_list.append(eTtank)

    def blitEnemyTank(self):#将坦克加入到窗口中
        for eTank in MainGame.EnemyTank_list:
            if eTank.live:
                eTank.displayTank()
                eTank.randMove()#坦克移动
                eTank.hitWalls()#调用敌方坦克与墙壁的碰撞方法
                eBullet = eTank.shot()#敌方坦克射击,因为继承关系,所以有shot函数
                eTank.hitMyTank()
                if eBullet:
                    MainGame.Enemy_bullet_list.append(eBullet)#将敌方子弹坦克加入列表
            else:
                MainGame.EnemyTank_list.remove(eTank)

    def blitBullet(self):#将我方子弹加入窗口
        for bullet in MainGame.Bullet_list:
            if bullet.live:#如果子弹还存在,则展示,否则,移除子弹
                bullet.displayBullet()
                bullet.bulletMove()
                bullet.hitEnemyTank()#调用我方子弹和敌方坦克的碰撞方法   
                bullet.hitWalls()#判断子弹撞墙
                bullet.hitFlag()
            else:
                MainGame.Bullet_list.remove(bullet)

    def displayExplodes(self):#新增方法,展示爆炸效果列表
        for explode in MainGame.Explode_list:
            if explode.live:
                explode.displayExplode()
            else:
                MainGame.Explode_list.remove(explode)

    def blitEnemyBullet(self):#将敌方子弹加入窗口
        for ebullet in MainGame.Enemy_bullet_list:
            if ebullet.live:#如果子弹还存在,则展示,否则,移除子弹
                ebullet.displayBullet()
                ebullet.bulletMove()#让子弹移动
                ebullet.hitWalls()
                ebullet.hitFlag()
                if MainGame.TANK_P1 and MainGame.TANK_P1.live:#坦克存在并活着才能调用
                    ebullet.hitMyTank()
            else:
                MainGame.Enemy_bullet_list.remove(ebullet)

    def getTextSurface(self,text):
        pygame.font.init()#初始化字体模块
        font = pygame.font.SysFont('kaiti',18)#选中一个字体
        textSurface = font.render(text,True,COLOR_RED)#进行绘制
        return textSurface

    def getEvent(self):#获取事件,比如鼠标事件、键盘事件
        eventList = pygame.event.get()#获取
        for event in eventList:
            if event.type == pygame.QUIT:#判断是否点击退出
                self.endGame()
            if event.type == pygame.KEYDOWN:#判断按键按下,并判断是哪个键
                if event.key == pygame.K_ESCAPE and not MainGame.TANK_P1:#ESC按键重生
                    self.creatMyTank()
                if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT or event.key == pygame.K_UP or event.key == pygame.K_DOWN:
                    MainGame.Num_Key += 1
                if MainGame.TANK_P1 and MainGame.TANK_P1.live:
                    if event.key == pygame.K_LEFT:#左方向键
                        print('左')      
                        MainGame.TANK_P1.direction = 'L'#改变坦克的方向
                        MainGame.TANK_P1.stop = False
                    elif event.key == pygame.K_RIGHT:
                        print('右') 
                        MainGame.TANK_P1.direction = 'R'#改变坦克的方向
                        MainGame.TANK_P1.stop = False
                    elif event.key == pygame.K_UP:
                        print('上') 
                        MainGame.TANK_P1.direction = 'U'#改变坦克的方向
                        MainGame.TANK_P1.stop = False
                    elif event.key == pygame.K_DOWN:
                        print('下') 
                        MainGame.TANK_P1.direction = 'D'#改变坦克的方向
                        MainGame.TANK_P1.stop = False
                    elif event.key == pygame.K_SPACE:
                        if len(MainGame.Bullet_list) < 5:
                            m = Bullet(MainGame.TANK_P1)#产生一个子弹
                            MainGame.Bullet_list.append(m)
                            print('射击') 
                            music = Music('img/fire.wav')
                            music.play()
            if event.type == pygame.KEYUP:#将坦克的移动状态修改
                if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT or event.key == pygame.K_UP or event.key == pygame.K_DOWN:
                    MainGame.Num_Key -= 1
                if ((event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT or event.key == pygame.K_UP or event.key == pygame.K_DOWN) and MainGame.Num_Key == 0):
                    if MainGame.TANK_P1 and MainGame.TANK_P1.live:    
                        MainGame.TANK_P1.stop = True

    def creatWalls(self): #创建墙壁的方法
        wall = Wall(0,0)
        L_left = MainGame.SCREEN_WIDTH/2 - MainGame.FLAG.rect.width - wall.rect.width
        L_top = MainGame.SCREEN_HEIGHT - wall.rect.height
        wall = Wall(L_left,L_top)
        MainGame.Wall_list.append(wall)
        for i in range(0,4):
            wall = Wall(L_left + wall.rect.width * i,L_top - wall.rect.height)
            MainGame.Wall_list.append(wall)
        wall = Wall(L_left + wall.rect.width*3,L_top)
        MainGame.Wall_list.append(wall)

        for i in range (0,8):
            wall = Wall((wall.rect.width)*3*i,400)
            MainGame.Wall_list.append(wall)

        for i in range(1,6):
            wall = Wall((wall.rect.width)*6,400 - wall.rect.height * i)
            MainGame.Wall_list.append(wall)
        for i in range(1,6):
            wall = Wall((wall.rect.width)*15,400 - wall.rect.height * i)
            MainGame.Wall_list.append(wall)

        wall = Wall((wall.rect.width)*10+(wall.rect.width)*1,400)
        MainGame.Wall_list.append(wall)
        wall = Wall((wall.rect.width)*9+(wall.rect.width)*1,400)
        MainGame.Wall_list.append(wall)

        for i in range(1,9):
            wall = Wall((wall.rect.width)*15 - wall.rect.width*i,400 - wall.rect.height*2)
            MainGame.Wall_list.append(wall)
        for i in range(1,6):
            wall = Wall((wall.rect.width)*18,400 + wall.rect.height * i)
            MainGame.Wall_list.append(wall)
        for i in range(1,6):
            wall = Wall((wall.rect.width)*3,400 + wall.rect.height * i)
            MainGame.Wall_list.append(wall)
        for i in range(1,4):
            wall = Wall((wall.rect.width)*i,400 - wall.rect.height * 4)
            MainGame.Wall_list.append(wall)
        for i in range(1,4):
            wall = Wall(MainGame.SCREEN_WIDTH - (wall.rect.width)*i,400 - wall.rect.height * 4)
            MainGame.Wall_list.append(wall)

    def blitWalls(self):
        for wall in MainGame.Wall_list:
            if wall.live:
                wall.dispalyWall()
            else:
                MainGame.Wall_list.remove(wall)

    def endGame(self):#结束游戏
        print('古德拜拜')
        exit(0)#结束Python解释器

    def blitEndWord(self):
        image = EndWord()
        image.dispalyEndWord()

    def creatFlag(self):
        MainGame.FLAG = Flag()

    def restart(self):
        self.startGame()

class BaseItem(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)

class Tank(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()#坦克所在的区域
        self.rect.left = left
        self.rect.top = top#指定坦克的初始化位置,分别距离x,y轴的距离
        self.speed = 5
        self.stop = True
        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
        elif self.direction == 'R':
            if self.rect.left + self.rect.height  0:
                self.rect.top -= self.speed
        elif self.direction == 'D':
            if self.rect.top + self.rect.height  0:
                self.rect.top -= self.speed
            else:
                self.live = False
        elif self.direction == 'D':
            if self.rect.top < MainGame.SCREEN_HEIGHT - self.rect.height:
                self.rect.top += self.speed
            else:
                self.live = False
        elif self.direction == 'L':
            if self.rect.left > 0:
                self.rect.left -= self.speed
            else:
                self.live = False
        elif self.direction == 'R':
            if self.rect.left < MainGame.SCREEN_WIDTH - self.rect.width:
                self.rect.left += self.speed
            else:
                self.live = False

    def displayBullet(self):#子弹显示
        MainGame.window.blit(self.image,self.rect)

    def hitEnemyTank(self):#新增我方子弹碰撞敌方坦克的方法
        for eTank in MainGame.EnemyTank_list:
            if pygame.sprite.collide_rect(eTank,self):
                self.live = False #子弹不再存回
                eTank.live = False   #坦克不再存活
                explode = Explode(eTank)#产生一个爆炸效果
                MainGame.Explode_list.append(explode)#将爆炸效果加入到爆炸效果列表
                music = Music('img/hit.wav')
                music.play()
                MainGame.defeatNum -= 1

    def hitMyTank(self):#新增敌方子弹和我方坦克
        #因为前面已经遍历了敌方子弹,所以这里只需要判断一个子弹
        if pygame.sprite.collide_rect(self,MainGame.TANK_P1):
            self.live = False#修改子弹状态
            MainGame.TANK_P1.live = False#修改我方坦克状态
            explode = Explode(MainGame.TANK_P1)#产生爆炸效
            MainGame.Explode_list.append(explode)
            music = Music('img/hit.wav')
            music.play()
            MainGame.remainLive -= 1

    def hitWalls(self):#子弹与墙壁碰撞
        for wall in MainGame.Wall_list:
            if pygame.sprite.collide_rect(wall,self):
                self.live = False#修改属性
                wall.hp -= 1
            if wall.hp <= 0:
                wall.live = False

    def hitFlag(self):#子弹与旗帜碰撞
        if pygame.sprite.collide_rect(MainGame.FLAG,self):
            self.live = False#修改属性
            MainGame.FLAG.live = False

class Explode():
    def __init__(self,tank):
        self.rect = tank.rect
        self.step = 0
        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]
        self.live = True#一开始要进行显示

    def displayExplode(self):#展示子弹
        if self.step < len(self.images):
            MainGame.window.blit(self.image,self.rect)
            self.image = self.images[self.step]
            self.step += 1
        else:
            self.step = 0
            self.live = False#不再进行显示

class Wall():
    def __init__(self,left,top):
        self.image = pygame.image.load('img/walls.gif')
        self.rect = self.image.get_rect()
        self.rect.left = left
        self.rect.top = top
        self.live = True#因为子弹打墙壁要是否消失,所以定义该属性
        self.hp = 1 #墙壁生命值

    def dispalyWall(self):#展示墙壁
        MainGame.window.blit(self.image,self.rect)

class EndWord():
    def __init__(self):
        self.images = [pygame.image.load('img/over.gif'),
                      pygame.image.load('img/win.gif')]
        if MainGame.remainLive == 0:
            self.image = self.images[0]
        if MainGame.defeatNum == 0:
            self.image = self.images[1]
        if not MainGame.FLAG.live:
            self.image = self.images[0]
        self.rect = self.image.get_rect()
        self.rect.left = 650
        self.rect.top = 350

    def dispalyEndWord(self):#展示
        MainGame.window.blit(self.image,self.rect)

class Flag():
    def __init__(self):
        self.image = pygame.image.load('img/flag.gif')
        self.rect = self.image.get_rect()
        self.rect.left = MainGame.SCREEN_WIDTH/2 - self.rect.width/2
        self.rect.top = MainGame.SCREEN_HEIGHT - self.rect.height
        self.live = True

    def dispalyFlag(self):#展示
        MainGame.window.blit(self.image,self.rect)

class Music():
    def __init__(self,fileName):
        self.fileName = fileName
        pygame.mixer.init()
        pygame.mixer.music.load(self.fileName)

    def play(self):#开始播放音乐
        pygame.mixer.music.play()


MainGame().startGame()

。。。真不好意思,竟然这么长,嘿嘿,效果展示如下:

python编写坦克大战过程_第2张图片

 python编写坦克大战过程_第3张图片

 python编写坦克大战过程_第4张图片

 我方击败10个敌人,则胜利,我方死亡3次,或旗帜被攻打,会失败

2、缺点

胜利或者失败之后不能通过按键重新开始,障碍物也只有墙壁,敌方坦克只有一种,不过捏,学习为主,加上这些东西,要好多时间,会操作就好了~

你可能感兴趣的:(自作游戏,Python,python)