美文网首页
tank大战

tank大战

作者: Chaweys | 来源:发表于2022-01-01 18:58 被阅读0次

    # -*-coding:utf-8 -*-
    # @Author : hudechao
    # @Time : 2021/12/12 18:50
    
    """
    坦克大战游戏的需求(基于面向对象的分析):
    1、项目中有哪些类
    2、每个类中有哪些方法
    
    1、主类
       开始游戏
       结束游戏
    
    2、坦克类(我方坦克,地方坦克)
       移动
       射击
       显示坦克的方法
    
    3、子弹类
       移动
       显示子弹的方法
    
    4、爆炸效果类
       展示爆炸效果
    
    5、强壁类
       属性:是否可以通过
    
    6、音效类
       播放音乐
    
    
    v1.25
    优化功能:
    1、实现音效处理
        我方坦克出生音效
        发射子弹音效
        子弹打中坦克音效
    """
    import pygame
    import time
    import random
    version = "v1.25"
    _display = pygame.display
    COLOR_BLACK = pygame.Color(0,0,0)
    COLOR_RED = pygame.Color(255,0,0)
    
    class MainGame:
        #游戏主窗口
        window = None
        #宽
        SCREEN_WIDTH = 800
        #高
        SCREEN_HEIGHT = 500
        #创建我方坦克
        TANK_P1 = None
        #新增属性开关:控制坦克的移动停止,默认Ture坦克停止
        Tank_Flag = True
        #敌方坦克容器
        EnamyTank_list = []
        #敌方坦克数量
        EanmyTank_count = 5
        #我方坦克子弹容器
        TankBullet_list = []
        #敌方坦克子弹容器
        EanmyBullet_list = []
        #爆炸效果列表
        Explode_list = []
        #墙壁类列表
        Walls_list = []
    
    
        #启动游戏方法
        def startGame(self):
            _display.init()
            #初始化显示模块,返回的是surface对象
            MainGame.window = _display.set_mode([MainGame.SCREEN_WIDTH,MainGame.SCREEN_HEIGHT])
            #给显示游戏窗口添加标题
            _display.set_caption("坦克大战"+version)
    
            #创建我方坦克:只需创建一次
            self.createMyTank()
    
            #创建敌方坦克:调用方法创建多个敌方坦克
            self.createEnamyTank()
    
            #创建墙壁类
            self.createWall()
    
            while True:
                #因为要持续显示游戏窗口,所以需要加循环
                #给游戏窗口添加背景填充色
                MainGame.window.fill(COLOR_BLACK)
    
                #循环获取事件
                self.getEvent()
    
                #循环设置字体画布在游戏的主画布上
                #dest=(1,1)代表游戏画布的坐标x=1,y=1,即左上角
                MainGame.window.blit(self.getTextSurface("剩余敌方坦克%d辆"%len(MainGame.EnamyTank_list)),(1,1))
    
                if MainGame.TANK_P1 and MainGame.TANK_P1.live:
                    #加载我方坦克到游戏主窗口
                    MainGame.TANK_P1.displayTank()
                else:
                    #如果我方坦克失活,删除我方坦克内存
                    del MainGame.TANK_P1
                    MainGame.TANK_P1 = None
    
                #加载敌方坦克到游戏窗口:移动方法记载到了这里,再循环之下,所以会一直移动,所以需要重写父类的移动方法父类的移动方法
                self.biltEnamyTank()
    
                #展示墙壁类
                self.biltWall()
    
                #判断坦克的开关=Ture时停止移动坦克,如果开关=False坦克继续移动:我方坦克能控制移动是因为设置了移动开关:Tank_Flag
                if not MainGame.Tank_Flag and MainGame.TANK_P1:
                    MainGame.TANK_P1.move()
                    #调用我方坦克的碰撞墙壁的方法
                    MainGame.TANK_P1.hitWalls()
                    #调用我方坦克是否碰撞到敌方坦克
                    MainGame.TANK_P1.hitEnamyTank()
    
                #循环展示我方子弹到游戏主窗口
                self.biltBullet()
                #循环展示敌方子弹到游戏主窗口
                self.ebiltBullet()
                #循环展示爆炸效果
                self.disExplode()
    
                # 循环显示游戏窗口
                time.sleep(0.02)
                _display.update()
    
    
        #获取游戏运行期间所有的事件
        def getEvent(self):
            #获取所有事件
            eventList = pygame.event.get()
            for event in eventList:
                #判断事件类型,如果是QUIT,则退出游戏
                if event.type == pygame.QUIT:
                    self.stopGame()
                #判断事件类型,如果是按键类型,则根据具体按键事件具体响应
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE and not MainGame.TANK_P1:
                        self.createMyTank()
                    if MainGame.TANK_P1 and MainGame.TANK_P1.live:
                        if event.key == pygame.K_LEFT:
                            print("坦克向左掉头移动")
                            # 重新设置我方坦克的方向
                            MainGame.TANK_P1.direction = "L"
                            # 移动我方坦克-按键一次移动一次,需要优化
                            # MainGame.TANK_P1.move()
                            # 按下键后打开坦克的移动开关
                            MainGame.Tank_Flag = False
                        elif event.key == pygame.K_RIGHT:
                            print("坦克向右掉头移动")
                            # 重新设置我方坦克的方向
                            MainGame.TANK_P1.direction = "R"
                            # 移动我方坦克-优化移动
                            # MainGame.TANK_P1.move()
                            # 按下键后打开坦克的移动开关
                            MainGame.Tank_Flag = False
                        elif event.key == pygame.K_UP:
                            print("坦克向上掉头移动")
                            # 重新设置我方坦克的方向
                            MainGame.TANK_P1.direction = "U"
                            # 移动我方坦克-优化移动
                            # MainGame.TANK_P1.move()
                            # 按下键后打开坦克的移动开关
                            MainGame.Tank_Flag = False
                        elif event.key == pygame.K_DOWN:
                            print("坦克向下掉头移动")
                            # 重新设置我方坦克的方向
                            MainGame.TANK_P1.direction = "D"
                            # 移动我方坦克-优化移动
                            # MainGame.TANK_P1.move()
                            # 按下键后打开坦克的移动开关
                            MainGame.Tank_Flag = False
                        elif event.key == pygame.K_SPACE:
                            print("发射子弹")
                            if len(MainGame.TankBullet_list) < 3:
                                # 创建子弹
                                # m = Bullet(MainGame.TANK_P1),或者
                                m = MainGame.TANK_P1.shot()
                                # 将子弹加入到子弹列表里
                                MainGame.TankBullet_list.append(m)
                                music = Music('img/fire.wav')
                                music.playMusic()
                            else:
                                print("子弹数量不足")
                            print("当前子弹数量:{}".format(len(MainGame.TankBullet_list)))
    
                #判断事件类型,松开按键就停止坦克的移动
                if event.type == pygame.KEYUP:
                    if event.key == pygame.K_UP or event.key == pygame.K_DOWN or event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
                        if MainGame.TANK_P1 and MainGame.TANK_P1.live:
                            #松开按键,关闭坦克的移动开关
                            MainGame.Tank_Flag = True
    
        #游戏内文字绘制方法
        def getTextSurface(self,text):
            #初始化字体模块
            pygame.font.init()
            #获取所有支持的字体
            # print(pygame.font.get_fonts())
            #设置选用的字体
            font = pygame.font.SysFont('kaiti',25)
            #字体模块设置后返回依然是一个surface-小画布
            textSurface = font.render(text,True,COLOR_RED)
            return textSurface
    
        #创建我方坦克
        def createMyTank(self):
            MainGame.TANK_P1 = MyTank(400, 400)
            #创建我方坦克后播放开始音效
            music = Music('img/start.wav')
            music.playMusic()
    
        #创建敌方坦克
        def createEnamyTank(self):
            #敌方坦克的位置规定,top固定,left变化
            top = 100
            for i in range(MainGame.EanmyTank_count):
                #每个坦克的速度随机
                speed = random.randint(1, 2)
                #left决定敌方坦克的水平位置,每个坦克需要随机不同位置
                left = random.randint(1,7)*100
                MainGame.EnamyTank_list.append(EnamyTank(left,top,speed))
    
        #展示敌方坦克
        def biltEnamyTank(self):
            #敌方坦克类调用父类的展示坦克方法
            for eTank in MainGame.EnamyTank_list:
                if eTank.live:
                    #敌方坦克加载到游戏主窗口
                    eTank.displayTank()
                    #敌方坦克移动:调用了父类的移动方法
                    #eTank.move()
                    eTank.randMove()
                    #调用敌方坦克的碰撞墙壁方法
                    eTank.hitWalls()
                    #调用敌方坦克碰撞到我方坦克方法
                    eTank.hitMyTank()
                    #敌方坦克射击子弹
                    ebullet=eTank.shot()
                    if ebullet is not None:
                        MainGame.EanmyBullet_list.append(ebullet)
                else:
                    MainGame.EnamyTank_list.remove(eTank)
    
        #我方子弹渲染
        def biltBullet(self):
            #遍历子弹列表展示
            for bullet in MainGame.TankBullet_list:
                if bullet.live:
                    #我方子弹展示
                    bullet.displayBullet()
                    #我方移动子弹
                    bullet.bulletMove()
                    #我方子弹是否击中敌方坦克
                    bullet.hitEanmyTank()
                    #我方子弹是否击中墙壁
                    bullet.hitWalls()
                else:
                    #子弹消失
                    bullet.live = False
                    MainGame.TankBullet_list.remove(bullet)
    
        #敌方子弹渲染
        def ebiltBullet(self):
            #遍历子弹列表展示
            for ebullet in MainGame.EanmyBullet_list:
                if ebullet.live:
                    #子弹展示
                    ebullet.displayBullet()
                    #移动子弹
                    ebullet.bulletMove()
                    if MainGame.TANK_P1 and MainGame.TANK_P1.live:
                        #敌方子弹是否击中我方坦克
                        ebullet.hitMyTank()
                    #敌方子弹是否击中墙壁
                    ebullet.hitWalls()
                else:
                    #子弹消失
                    ebullet.live = False
                    MainGame.EanmyBullet_list.remove(ebullet)
    
        #展示爆炸效果
        def disExplode(self):
            for explode in MainGame.Explode_list:
                if explode.live:
                    explode.displayExplode()
                    #展示爆炸效果
                    music = Music('img/hit.wav')
                    music.playMusic()
                else:
                    #移除爆炸效果类,移除后即不会再展示了
                    MainGame.Explode_list.remove(explode)
    
        #创建墙壁
        def createWall(self):
            for i in range(6):
                wall = Wall(190*i,240)
                MainGame.Walls_list.append(wall)
        #展示墙壁
        def biltWall(self):
            for wall in MainGame.Walls_list:
                if wall.live:
                    wall.displayWall()
                else:
                    MainGame.Walls_list.remove(wall)
    
        #结束游戏方法
        def stopGame(self):
            print("谢谢使用")
            exit()
    
    
    class BaseItem(pygame.sprite.Sprite):
        """
        Tank坦克类和Bullet子弹类都继承该类,该类又继承精灵类,从而实现:
        Tank坦克类和Bullet子弹类的碰撞消失
        因为Tank类和Bullet类都需要共同的精灵类属性
        """
        def __init__(self, color, width, height):
            # Call the parent class (Sprite) constructor
            pygame.sprite.Sprite.__init__(self)
    
    
    
    #坦克父类
    class Tank(BaseItem):
        #初始化坦克属性
        def __init__(self,left,top):
            #每个方向都是一个坦克的图片,存在一个字典里,从该字典里获取各个不同方向的坦克图片:U D L R 代表上下左右
            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也是一个个surface
            self.image = self.images[self.direction]
            #初始化坦克所在的区域,self.image.get_rect()返回Rect对象,该对象包含Rect(left, top, width, height) -> Rect
            #坦克的宽和高就使用图片本身的宽和高,坦克的位置就使用传入的入参left和right
            self.rect = self.image.get_rect()
            self.rect.left = left
            self.rect.top = top
            #坦克速度属性
            self.speed = 5
            #坦克是否存活的属性:默认存活
            self.live = True
            #记录坦克的移动之前的旧坐标:
            self.oldLeft = self.rect.left
            self.oldTop = self.rect.top
    
        #移动方法
        def move(self):
            """
            坦克的移动就是修改坐标值:
            向左移动就是left值减少,向右移动就是left值增加,
            向上移动就是top值减少,向下移动就是top值增加
            :return:
            """
            #移动之前先记录坦克的坐标:这里只要坦克的位置坐标发生改变,就会覆盖重写旧坐标
            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 < MainGame.SCREEN_WIDTH:
                    self.rect.left += self.speed
            elif self.direction == "U":
                if self.rect.top > 0:
                    self.rect.top -= self.speed
            elif self.direction == "D":
                #往下移动需要加上坦克的高度
                if self.rect.top + self.rect.height < MainGame.SCREEN_HEIGHT:
                    self.rect.top += self.speed
    
    
        #射击方法
        def shot(self):
            return Bullet(self)
    
        #展示坦克方法
        def displayTank(self):
            """
            坦克对象也是一张一张的surface,展示坦克就是把每个坦克的surface加载到游戏主窗口中,使用到blit()
            :return:
            """
            #重新设置坦克的位置
            self.image = self.images[self.direction]
            #加载坦克到游戏主窗口中
            MainGame.window.blit(self.image,self.rect)
    
        def stay(self):
            #坦克碰撞后恢复到之前的坐标
            self.rect.left = self.oldLeft
            self.rect.top = self.oldTop
    
        #坦克与墙壁的碰撞方法
        def hitWalls(self):
            for wall in MainGame.Walls_list:
                if pygame.sprite.collide_circle(self,wall):
                    #如果坦克与墙壁发生碰撞,则恢复坦克的坐标为移动之前的坐标
                    self.stay()
    
    
    class MyTank(Tank):
        #我方坦克类
        def __init__(self,left,top):
            super().__init__(left,top)
        #判断我方坦克是否碰撞到敌方坦克
        def hitEnamyTank(self):
            for etank in MainGame.EnamyTank_list:
                if pygame.sprite.collide_rect(self,etank):
                    #调用父类的还原坦克位置方法
                    self.stay()
    
    class EnamyTank(Tank):
        #敌方坦克类:继承Tank父类
        def __init__(self,left,top,speed):
            #需要使用父类的默认属性,则需要显示的调用父类的初始化方法
            super().__init__(left,top)
            #每个方向都是一个坦克的图片,存在一个字典里,从该字典里获取各个不同方向的坦克图片:U D L R 代表上下左右
            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.showDriection()
            #初始化时坦克的图片:self.image也是一个个surface
            self.image = self.images[self.direction]
            #初始化坦克所在的区域,self.image.get_rect()返回Rect对象,该对象包含Rect(left, top, width, height) -> Rect
            #坦克的宽和高就使用图片本身的宽和高,坦克的位置就使用传入的入参left和right
            self.rect = self.image.get_rect()
            self.rect.left = left
            self.rect.top = top
            #坦克速度属性
            self.speed = speed
            #坦克步数属性
            self.step = 10
    
        #随机展示敌方坦克方向
        def showDriection(self):
            i = random.randint(1,4)
            if i == 1:
                return 'U'
            elif i == 2:
                return 'D'
            elif i == 3:
                return 'L'
            elif i == 4:
                return 'R'
    
        #随机移动敌方坦克
        def randMove(self):
            if self.step <= 0:
                self.direction = self.showDriection()
                self.step = 10
            else:
                self.move()
                self.step -= 1
    
        #重写父类的射击方法
        def shot(self):
            num = random.randint(1,1000)
            if num <= 20:
                return Bullet(self)
            else:
                return None
    
        #判断敌方坦克是否碰撞到我方坦克
        def hitMyTank(self):
            if MainGame.TANK_P1 and MainGame.TANK_P1.live:
                if pygame.sprite.collide_rect(self,MainGame.TANK_P1):
                    #调用父类的还原坦克位置方法
                    self.stay()
    
    class Bullet(BaseItem):
        #子弹类
        def __init__(self,tank):
            #加载子弹图片
            self.image = pygame.image.load("img/enemymissile.gif")
            self.rect = self.image.get_rect()
            #设置子弹的方向:初始方向等同于坦克的方向
            self.direction = tank.direction
            #设置子弹的位置:判断四个方位的位置
            if self.direction == "U":
                #子弹的left = 坦克的left + 坦克宽度/2 - 子弹宽度/2
                #子弹的top = 坦克的top - 子弹的高度
                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":
                #子弹的left = 坦克的left + 坦克宽度/2 - 子弹宽度/2
                #子弹的top = 坦克的top + 坦克的高度
                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":
                #子弹的left = 坦克的left - 子弹的高度
                #子弹的top = 坦克的top + 坦克的高度/2 - 子弹的宽度/2
                self.rect.left = tank.rect.left - self.rect.height
                self.rect.top = tank.rect.top + tank.rect.height/2 - self.rect.width/2
    
            elif self.direction == "R":
                #子弹的left = 坦克的left + 坦克的宽度 +子弹的宽度
                #子弹的top = 坦克的top + 坦克的高度/2 - 子弹的宽度/2
                self.rect.left = tank.rect.left + tank.rect.width
                self.rect.top = tank.rect.top + tank.rect.height/2 - self.rect.width/2
    
            #子弹速度
            self.speed = 10
            #子弹属性,显示子弹是否还活着
            self.live = True
    
        #移动子弹方法
        def bulletMove(self):
            if self.direction == "U":
                if self.rect.top > 0:
                    #才允许子弹移动
                    self.rect.top = 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.rect.top + self.speed
                else:
                    #子弹消失
                    self.live = False
            elif self.direction == "L":
                if self.rect.left > 0:
                    self.rect.left = 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.rect.left + self.speed
                else:
                    #子弹消失
                    self.live = False
    
        #展示子弹到游戏主窗口方法
        def displayBullet(self):
            MainGame.window.blit(self.image,self.rect)
    
    
        #判断我方子弹是否集中敌方坦克方法
        def hitEanmyTank(self):
            #循环判断敌方坦克的列表是否被子弹击中
            for eTank in MainGame.EnamyTank_list:
                #pygame.sprite.collide_rect(var1,var2)需要两个参数
                #这里self指当前子弹对象,eTank指敌方坦克对象
                if pygame.sprite.collide_rect(self,eTank):
                    #子弹消失
                    self.live = False
                    #敌方坦克消失
                    eTank.live = False
                    #击中敌方坦克就将爆炸效果加入到爆炸效果列表中
                    explode=Explode(eTank)
                    MainGame.Explode_list.append(explode)
    
    
        #判断敌方坦克子弹是否击中我方坦克
        def hitMyTank(self):
            if MainGame.TANK_P1 and MainGame.TANK_P1.live:
                #因为我方坦克只有一辆,不需要循环坦克列表
                if pygame.sprite.collide_rect(self,MainGame.TANK_P1):
                    #产生爆炸效果类,并加入到爆炸效果列表
                    explode = Explode(MainGame.TANK_P1)
                    MainGame.Explode_list.append(explode)
                    if MainGame.TANK_P1 and MainGame.TANK_P1.live:
                        #我方坦克失活
                        MainGame.TANK_P1.live = False
                    #敌方子弹失活
                    self.live = False
    
        #判断子弹是否击中墙壁
        def hitWalls(self):
            for wall in MainGame.Walls_list:
                #如果子弹和墙壁发生碰撞,子弹属性失活
                if pygame.sprite.collide_rect(self,wall):
                    self.live = False
                    wall.hp -=1
                    if wall.hp < 0:
                        wall.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):
            length = len(self.images)
            if self.step < length:
                self.image = self.images[self.step]
                MainGame.window.blit(self.image, self.rect)
                self.step += 1
            else:
                self.live = False
    
    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)
    
    class Music:
        #音乐类
        def __init__(self,filename):
            #filename指音效文件
            self.filename = filename
            #报错:pygame.error: mixer not initialized,在使用pygame.mixer.music模块必须先初始化pygame.mixer模块
            pygame.mixer.init()
            #加载音乐文件
            pygame.mixer.music.load(self.filename)
        #播放音乐方法
        def playMusic(self):
            #播放音效文件
            pygame.mixer.music.play()
    
    
    
    MainGame().startGame()
    

    相关文章

      网友评论

          本文标题:tank大战

          本文链接:https://www.haomeiwen.com/subject/jwfaqrtx.html