1、Pygame模块

2、创建窗口

import pygame

#初始化pygame
pygame.init()
#创建一个窗口
pygame.display.set_mode((800,600))
#设置窗口标题
pygame.display.set_caption("坦克大战1.0")
while True:
    #刷新显示窗口
    pygame.display.update()

3、需求分析

1.坦克类

1.1我方坦克类

1.2敌方坦克类

坦克的显示

坦克的移动

坦克的射击

2.子弹类

显示子弹

子弹的移动

3.墙壁类

显示墙壁

4.爆炸效果类

显示爆炸效果

5.音效类

播放音效

6.游戏主窗口类

开始游戏

结束游戏

4、基本结构的编写

本游戏主要分为两个对象,分别是我方坦克和敌方坦克。用户可以通过控制我方的坦克来摧毁敌方的坦克保护自己的“家”,把所有的敌方坦克消灭完达到胜利。敌方的坦克在初始的时候是默认5个的(这可以自己设置),当然,如果我方坦克被敌方坦克的子弹打中,游戏结束。从面向对象分析该项目有以下类组成:

坦克类

class Tank():
    def __init__(self) -> None:
        pass
    def display_tank(self) -> None:
        """
        显示坦克
        """
        pass
    def move(self) -> None:
        """
        移动坦克
        """
        pass
    def shot(self) -> None:
        """
        坦克的射击
        """
        pass
class Mytank(Tank):
    """
    我方坦克类
    """
    def __init__(self) -> None:
        pass
class Enemytank(Tank):
    """
    敌方坦克类
    """
    def __init__(self) -> None:
        pass

子弹类

class Bullet:
    """ 
    子弹类 
    """
    def __init__(self) -> None:
        pass
    def display_bullet(self) -> None:
        """
        显示子弹
        """
        pass
    def move(self) -> None:
        """
        子弹的移动
        """
        pass

墙壁类

class Wall:
    """
    墙壁类
    """
    def __init__(self) -> None:
        pass
    def diaplay_wall(self) -> None:
        """
        显示墙壁
        """
        pass

爆炸效果类

class Explode:
    """
    爆炸效果类
    """
    def __init__(self) -> None:
        pass
    def display_explode(self) -> None:
        """
        显示爆炸效果
        """
        pass

音效类

class Music:
    """
    音效类
    """
    def __init__(self) -> None:
        pass
    def play(self) -> None:
        """
        播放音效
        """
        pass

游戏主窗口类

class Main():
    """
    游戏主窗口类
    """
    def __init__(self) -> None:
        pass
    def start_Game(self) -> None:
        """
        开始游戏
        """
        pass
    def end_Game(self) -> None:
        """
        结束游戏
        """
        pass

5、显示游戏窗口

在游戏设计的前期,要先创建游戏的界面,也就是要为所设计的游戏创建一个窗口

import pygame

# 设置通用属性
BG_COLOR = pygame.Color(255,255,255)
SCREEN_WIDTH = 700
SCREEN_HEIGHT = 500

class MainGame:
  '''
   游戏主窗口类
   '''
  # 游戏主窗口对象
  window =None
    

  def __init__(self) -> None:
    pass
  def start_game(self) -> None:
    '''
     开始游戏
     '''
    # 初始化游戏窗口
    pygame.display.init()
    # 创建一个窗口
    MainGame.window = pygame.display.set_mode((SCREEN_WIDTH,SCREEN_HEIGHT))
    # 设置窗口标题
    pygame.display.set_caption('坦克大战1.0')
    # 刷新窗口
    while True:
      # 给窗口设置填充色
      MainGame.window.fill(BG_COLOR)
      pygame.display.update()
  def end_game(self) -> None:
    '''
     结束游戏
     '''
    pass


if __name__ == "__main__":
  # 调用MainGame类中的start_game方法,开始游戏
  MainGame().start_game()

6、添加提示文字

import pygame

#设置通用属性
GB_COLOR = pygame.Color(0,0,0)
SCREEN_WIDTH = 700
SCREEN_HEIGHT = 500
TEXT_COLOR = pygame.Color(255,0,0)

class MainGame:
    """
    游戏主窗口类
    """
    #游戏主窗口对象
    window = None
    def __init__(self) -> None:
        pass
    def start_Game(self) -> None:
        """
        开始游戏
        """
        # 初始化游戏窗口
        pygame.display.init()
        # 创建一个窗口
        MainGame.window=pygame.display.set_mode((SCREEN_WIDTH,SCREEN_HEIGHT))
        # 设置窗口标题
        pygame.display.set_caption("坦克大战1.0")
        # 刷新游戏窗口
        while True:
            #给窗口设置填充色
            MainGame.window.fill(GB_COLOR)
            #增加提示文字
            #1.要增加文字内容
            num = 6
            text = self.get_text_surface(f'敌方坦克剩余数量{num}')
            #2.如何吧文字加上
            MainGame.window.blit(text,(10,10))
            pygame.display.update()
            
    def get_text_surface(self,text:str) -> None:
        """
        获取文字的 图片
        """
        #初始化字体模块
        pygame.font.init()
        #获取可以使用的字体
        #print(pygame.font.get_fonts())
        #创建字体
        font = pygame.font.SysFont("kaiti",18)
        #绘制文字信息
        text_surface = font.render(text,True,TEXT_COLOR)
        #将绘制的文字信息返回
        return text_surface

7、增加事件监听

事件监听用于在程序中监听特定的事件,以便在事件发生时能够执行相应的代码。事件可以是用户交互、系统消息等。

添加事件监听,控制上、下、左、右四个方向键,实现针对不同的键改变坦克的方向及移动功能,点击关闭退出游戏。

def get_event(self) -> None:
    '''
     获取事件
     '''
    # 获取所有事件
    event_list = pygame.event.get() 
    # 遍历事件
    for event in event_list:
        # 判断是什么事件,然后做出相应的处理
        if event.type == pygame.QUIT:
            # 点击关闭按钮
            self.end_game()
        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('坦克向下移动')
def end_game(self) -> None:
    '''
     结束游戏
     '''
    print('谢谢使用,欢迎再次使用')
    exit()

8、加载我方坦克

通过加载图片来表示游戏中的坦克,此坦克代表我方坦克,完善坦克类。

class Tank:
  '''
   坦克类
   '''
  def __init__(self,left:int,top:int) -> None:
    # 设置我方坦克的图片资源
    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 = 'L'
    # 获取图片信息
    self.image = self.images.get(self.direction)
    # 获取图片的矩形
    self.rect = self.image.get_rect()
    # 设置我方坦克位置
    self.rect.left = left
    self.rect.top = top

  def display_tank(self) -> None:
    '''
     显示坦克
     '''
    MainGame.window.blit(self.image,self.rect)

开始游戏方法,创建坦克,将坦克添加到窗口。

 def start_game(self) -> None:
    '''
     开始游戏
     '''
    # 初始化游戏窗口
    pygame.display.init()
    # 创建一个窗口
    MainGame.window = pygame.display.set_mode((SCREEN_WIDTH,SCREEN_HEIGHT))
    # 设置窗口标题
    pygame.display.set_caption('坦克大战1.0')
    # 创建一个我方 坦克
    my_tank = Tank(350,200)
    # 刷新窗口
    while True:
      # 给窗口设置填充色
      MainGame.window.fill(BG_COLOR)
      # 增加提示文字
      # 1.要增加文字内容
      num = 6
      text = self.get_text_surface(f'敌方坦克剩余数量{num}')
      # 2.如何把文字加上
      MainGame.window.blit(text,(10,10))
      # 增加事件
      self.get_event()
      # 显示 我方坦克
      my_tank.display_tank()
      pygame.display.update() 

9、修改我方坦克朝向

class Tank:
  '''
   坦克类
   '''
  def __init__(self,left:int,top:int) -> None:
    # 设置我方坦克的图片资源
    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 = 'L'
    # 获取图片信息
    self.image = self.images.get(self.direction)
    # 获取图片的矩形
    self.rect = self.image.get_rect()
    # 设置我方坦克位置
    self.rect.left = left
    self.rect.top = top

  def display_tank(self) -> None:
    '''
     显示坦克
     '''
    # 获取最新坦克的朝向位置图片
    self.image = self.images.get(self.direction)
    MainGame.window.blit(self.image,self.rect)

class MainGame:
  '''
   游戏主窗口类
   '''
  # 游戏主窗口对象
  window =None
  # 设置我放坦克
  my_tank = None

  def __init__(self) -> None:
    pass
  def start_game(self) -> None:
    '''
     开始游戏
     '''
    # 初始化游戏窗口
    pygame.display.init()
    # 创建一个窗口
    MainGame.window = pygame.display.set_mode((SCREEN_WIDTH,SCREEN_HEIGHT))
    # 设置窗口标题
    pygame.display.set_caption('坦克大战1.0')
    # 创建一个我方 坦克
    MainGame.my_tank = Tank(350,200)
    # 刷新窗口
    while True:
      # 给窗口设置填充色
      MainGame.window.fill(BG_COLOR)
      # 增加提示文字
      # 1.要增加文字内容
      num = 6
      text = self.get_text_surface(f'敌方坦克剩余数量{num}')
      # 2.如何把文字加上
      MainGame.window.blit(text,(10,10))
      # 增加事件
      self.get_event()
      # 显示 我方坦克
      MainGame.my_tank.display_tank()
      pygame.display.update()

  def get_text_surface(self,text:str) -> None:
    '''
     获取文字的图片
     '''
    # 初始化字体模块
    pygame.font.init()
    # 获取可以使用的字体
    # print(pygame.font.get_fonts())
    # 创建字体
    font = pygame.font.SysFont('kaiti',18)
    # 绘制文字信息
    text_surface = font.render(text,True,TEXT_COLOR)
    # 将绘制的文字信息返回
    return text_surface
  
  def get_event(self) -> None:
    '''
     获取事件
     '''
    # 获取所有事件
    event_list = pygame.event.get() 
    # 遍历事件
    for event in event_list:
      # 判断是什么事件,然后做出相应的处理
      if event.type == pygame.QUIT:
        # 点击关闭按钮
        self.end_game()
      if event.type == pygame.KEYDOWN:
        # 按下键盘
        if event.key == pygame.K_LEFT:
          print('坦克向左移动')
          # 修改方向
          MainGame.my_tank.direction = 'L'
          # 通过坦克对象调用即可
          MainGame.my_tank.move()
        elif event.key == pygame.K_RIGHT:
          print('坦克向右移动')
          # 修改方向
          MainGame.my_tank.direction = 'R'
          MainGame.my_tank.move()
        elif event.key == pygame.K_UP:
          print('坦克向上移动')
          # 修改方向
          MainGame.my_tank.direction = 'U'
          MainGame.my_tank.move()
        elif event.key == pygame.K_DOWN:
          # 修改方向
          MainGame.my_tank.direction = 'D'
          print('坦克向下移动')
          MainGame.my_tank.move()

10、移动我方坦克

坦克类中添加速度属性,实现我方坦克移动。

class Tank:
  '''
   坦克类
   '''
  def __init__(self,left:int,top:int) -> None:
    # 设置我方坦克的图片资源
    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 = 'L'
    # 获取图片信息
    self.image = self.images.get(self.direction)
    # 获取图片的矩形
    self.rect = self.image.get_rect()
    # 设置我方坦克位置
    self.rect.left = left
    self.rect.top = top
    # 设置移动速度
    self.speed = 10
  def display_tank(self) -> None:
    '''
     显示坦克
     '''
    # 获取最新坦克的朝向位置图片
    self.image = self.images.get(self.direction)
    MainGame.window.blit(self.image,self.rect)
  def move(self) -> None:
    '''
     坦克的移动
     '''
    if self.direction == "L":
      # 判断坦克的位置是否已左边界
      if self.rect.left > 0:
        # 修改坦克的位置 离左边的距离  - 操作
        self.rect.left = self.rect.left - self.speed
    elif self.direction == "R":
      # 判断坦克的位置是否已右边界
      if self.rect.left + self.rect.width < SCREEN_WIDTH:
        # 修改坦克的位置 离左边的距离  + 操作
        self.rect.left = self.rect.left + self.speed
    elif self.direction == "U":
      # 判断坦克的位置是否已上边界
      if self.rect.top > 0:
        # 修改坦克的位置 离上边的距离  - 操作
        self.rect.top = self.rect.top - self.speed
    elif self.direction == "D":
      # 判断坦克的位置是否已下边界
      if self.rect.top + self.rect.height < SCREEN_HEIGHT:
        # 修改坦克的位置 离上边的距离  + 操作
        self.rect.top = self.rect.top + self.speed

在上面方法中,按一下坦克移动一下,想按下键时候,坦克一直移动,弹起坦克停止移动。

在坦克类中添加移动开关属性,按下上、下、左、右四个方向键修改坦克的方向及开关状态stop = False。松开方向键,更改移动开关状态stop = True。

#获取程序期间所有事件(鼠标事件,键盘事件)
  def getEvent(self):
    #1.获取所有事件
    eventList = pygame.event.get()
    #2.对事件进行判断处理(1、点击关闭按钮  2、按下键盘上的某个按键)
    for event in eventList:
      #判断event.type 是否QUIT,如果是退出的话,直接调用程序结束方法
      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.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:
          print("发射子弹")
      #结束游戏方法
      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

11、移动我方坦克类的优化

一直按一个方向的键盘可以一直移动

class MainGame:
    """
    游戏主窗口类
    """
    #游戏主窗口对象
    window = None
    #设置我方坦克
    my_tank = None

    def __init__(self) -> None:
        pass
    def start_game(self) -> None:
        """
        开始游戏
        """
        # 初始化游戏窗口
        pygame.display.init()
        # 创建一个窗口
        MainGame.window=pygame.display.set_mode((SCREEN_WIDTH,SCREEN_HEIGHT))
        # 设置窗口标题
        pygame.display.set_caption("坦克大战1.0")
        #创建我方坦克
        MainGame.my_tank = Tank(350,200)
        #

        # 刷新游戏窗口
        while True:
            sleep(0.02)
            #给窗口设置填充色
            MainGame.window.fill(GB_COLOR)
            #增加提示文字
            #1.要增加文字内容
            num = 6
            text = self.get_text_surface(f'敌方坦克剩余数量{num}')
            #2.如何把文字加上
            MainGame.window.blit(text,(10,10))
            #增加事件
            self.get_event()
            #显示我方坦克
            MainGame.my_tank.display_tank()
            #移动我方坦克
            if MainGame.my_tank.remove:
                MainGame.my_tank.move()

            pygame.display.update()
            
    def get_text_surface(self,text:str) -> None:
        """
        获取文字的 图片
        """
        #初始化字体模块
        pygame.font.init()
        #获取可以使用的字体
        #print(pygame.font.get_fonts())
        #创建字体
        font = pygame.font.SysFont("kaiti",18)
        #绘制文字信息
        text_surface = font.render(text,True,TEXT_COLOR)
        #将绘制的文字信息返回
        return text_surface
    
    def get_event(self) -> None:
        """
        获取事件
        """
        #获取所有事件
        event_list = pygame.event.get()
        #遍历事件
        for event in event_list:
            #判断是什么事件,然后做出相应的处理
            if event.type==pygame.QUIT:
                #点击关闭按钮
                self.end_game()
            if event.type == pygame.KEYDOWN:
                #按下键盘
                if event.key == pygame.K_LEFT:
                    print("坦克向左移动")
                    #修改方向
                    MainGame.my_tank.direction = "L"
                    #修改坦克移动的状态为True
                    MainGame.my_tank.remove = True

                elif event.key == pygame.K_RIGHT:
                    print("坦克向右移动")
                    #修改方向
                    MainGame.my_tank.direction = "R" 
                    #修改坦克移动的状态为True
                    MainGame.my_tank.remove = True

                elif event.key == pygame.K_UP:
                    print("坦克向上移动")
                    #修改方向
                    MainGame.my_tank.direction = "U"
                    #修改坦克移动的状态为True
                    MainGame.my_tank.remove = True

                elif event.key == pygame.K_DOWN:
                    print("坦克向下移动")
                    #修改方向
                    MainGame.my_tank.direction = "D"
                    #修改坦克移动的状态为True
                    MainGame.my_tank.remove = True
            if event.type == pygame.KEYUP and event.key in (pygame.K_LEFT,pygame.K_RIGHT,pygame.K_UP,pygame.K_DOWN):
                MainGame.my_tank.remove = False
    def end_game(self) -> None:
        """
        结束游戏
        """
        print("谢谢使用,欢迎再次使用")
        exit()

12、加载敌方坦克

初始化敌方坦克。

class EnemyTank(Tank):
    """
    敌方坦克类
    """
    def __init__(self,left,top,speed) -> None:
        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.rand_direction()
        #获取图片信息
        self.image = self.images.get(self.direction)
        #获取图片的矩形信息
        self.rect = self.image.get_rect()
        #设置敌方坦克位置
        self.rect.left = left
        self.rect.top = top
        #设置移动速度
        self.speed = speed

生成随机的四个方向

def rand_direction(self) -> str:
        """
        生成随机方向
        """
        chice = random.randint(1,4)
        if chice == 1:
            return "U"
        elif chice == 2:
            return "D"
        elif chice == 3:
            return "L"
        elif chice == 4:
            return "R"

创建敌方坦克

def create_enemy_tank(self) -> None:
        """
        创建敌方坦克
        """
        self.enemy_top = 100
        self.enemy_speed = 10
        for i in range(self.enemy_tanks_count):
            #创建敌方坦克
            left = random.randint(0,600)
            #生成敌方坦克的位置
            e_tank = EnemyTank(left,self.enemy_top,self.enemy_speed)
            #将敌方坦克添加到列表中
            self.enemy_tanks_list.append(e_tank)

显示敌方坦克

def display_enemy_tank(self) -> None:
        """
        显示敌方坦克
        """
        for e_tank in self.enemy_tanks_list:
            e_tank.display_tank()

13、坦克类的优化

class Tank:
  '''
   坦克类
   '''
  def display_tank(self) -> None:
    '''
     显示坦克
     '''
    # 获取最新坦克的朝向位置图片
    self.image = self.images.get(self.direction)
    MainGame.window.blit(self.image,self.rect)
  def move(self) -> None:
    '''
     坦克的移动
     '''
    if self.direction == "L":
      # 判断坦克的位置是否已左边界
      if self.rect.left > 0:
        # 修改坦克的位置 离左边的距离  - 操作
        self.rect.left = self.rect.left - self.speed
    elif self.direction == "R":
      # 判断坦克的位置是否已右边界
      if self.rect.left + self.rect.width < SCREEN_WIDTH:
        # 修改坦克的位置 离左边的距离  + 操作
        self.rect.left = self.rect.left + self.speed
    elif self.direction == "U":
      # 判断坦克的位置是否已上边界
      if self.rect.top > 0:
        # 修改坦克的位置 离上边的距离  - 操作
        self.rect.top = self.rect.top - self.speed
    elif self.direction == "D":
      # 判断坦克的位置是否已下边界
      if self.rect.top + self.rect.height < SCREEN_HEIGHT:
        # 修改坦克的位置 离上边的距离  + 操作
        self.rect.top = self.rect.top + self.speed
  def shot(self) -> None:
    '''
     坦克的射击
     '''
    pass

class MyTank(Tank):
  '''
   我方坦克类
   '''
  def __init__(self,left:int,top:int) -> None:
    # 设置我方坦克的图片资源
    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 = 'L'
    # 获取图片信息
    self.image = self.images.get(self.direction)
    # 获取图片的矩形
    self.rect = self.image.get_rect()
    # 设置我方坦克位置
    self.rect.left = left
    self.rect.top = top
    # 设置移动速度
    self.speed = 10
    # 设置移动开关, False 表示不移动, True 表示移动
    self.remove = False

class MainGame:
  '''
   游戏主窗口类
   '''
  # 游戏主窗口对象
  window =None
  # 设置我放坦克
  my_tank = None
  # 存储敌方坦克的列表
  enemy_tank_list = []
  # 设置敌方坦克的数量
  enemy_tank_count = 6

  def __init__(self) -> None:
    pass
  def start_game(self) -> None:
    '''
     开始游戏
     '''
    # 初始化游戏窗口
    pygame.display.init()
    # 创建一个窗口
    MainGame.window = pygame.display.set_mode((SCREEN_WIDTH,SCREEN_HEIGHT))
    # 设置窗口标题
    pygame.display.set_caption('坦克大战1.0')
    # 创建一个我方 坦克
    MainGame.my_tank = MyTank(350,200)
    # 创建敌方坦克
    self.create_enemy_tank()
    # 刷新窗口
    while True:
      sleep(0.02)
      # 给窗口设置填充色
      MainGame.window.fill(BG_COLOR)
      # 增加提示文字
      # 1.要增加文字内容
      # num = 6
      text = self.get_text_surface(f'敌方坦克剩余数量{MainGame.enemy_tank_count}')
      # 2.如何把文字加上
      MainGame.window.blit(text,(10,10))
      # 增加事件
      self.get_event()
      # 显示 我方坦克
      MainGame.my_tank.display_tank()
      # 显示敌方坦克
      self.display_enemy_tank()
      # 移动坦克
      if MainGame.my_tank.remove:
        MainGame.my_tank.move()
      pygame.display.update()

14、敌方坦克随机移动

  def rand_move(self):
    '''
     随机移动
     '''
    # 判断步长是否为0
    if self.step <= 0:
      # 如果小于0,更换方向
      self.direction = self.rand_direction()
      # 重置步长
      self.step = 20
    else:
      # 如果大于0,移动
      self.move()
      # 步长减1
      self.step -=1

将敌方坦克加到窗口中

def display_enemy_tank(self) -> None:
    '''
     显示敌方坦克
     '''
    for e_tank in self.enemy_tank_list:
        # 显示敌方坦克
        e_tank.display_tank()
        # 移动敌方坦克
        e_tank.rand_move()

在开始游戏方法,加载敌方坦克

 #循环展示敌方坦克
 self.display_enemy_tank()

15、完善子弹类

子弹位置的计算方式:

初始化子弹

 '''
   子弹类
   '''
  def __init__(self,tank) -> None:
    # 加载图片
    self.image = pygame.image.load('./img/enemymissile.gif')
    # 获取子弹的方向
    self.direction = tank.direction
    # 获取子弹的图形
    self.rect = self.image.get_rect()
    # 设置子弹的位置
    if self.direction == "L":
      # 子弹的位置 = 坦克的位置 - 子弹的宽度
      self.rect.left = tank.rect.left - self.rect.width
      # 子弹的位置 = 坦克的位置 + 坦克的高度/2 - 子弹的高度/2
      self.rect.top = tank.rect.top + tank.rect.height/2 - self.rect.height/2
    elif self.direction == "R":
      # 子弹的位置 = 坦克的位置 + 坦克的宽度
      self.rect.left = tank.rect.left + tank.rect.width
      # 子弹的位置 = 坦克的位置 + 坦克的高度/2 - 子弹的高度/2
      self.rect.top = tank.rect.top + tank.rect.height/2 - self.rect.height/2
    elif self.direction == "U":
      # 子弹的位置 = 坦克的位置 + 坦克的宽度/2 - 子弹的宽度/2
      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":
      # 子弹的位置 = 坦克的位置 + 坦克的宽度/2 - 子弹的宽度/2
      self.rect.left = tank.rect.left + tank.rect.width/2 - self.rect.width/2
      # 子弹的位置 = 坦克的位置 + 坦克的高度
      self.rect.top = tank.rect.top + tank.rect.height
    # 设置子弹的速度
    self.speed = 10

16、我方坦克发射子弹

添加显示子弹

class Bullet:
  '''
   子弹类
   '''    
  def display_bullet(self) -> None:
    '''
     显示子弹
     '''
    MainGame.window.blit(self.image,self.rect)

按空格键产生子弹,并将子弹添加到子弹列表中

class MainGame:
  '''
   游戏主窗口类
   '''
  # 游戏主窗口对象
  window =None
  # 设置我放坦克
  my_tank = None
  # 存储敌方坦克的列表
  enemy_tank_list = []
  # 设置敌方坦克的数量
  enemy_tank_count = 6
  # 存储我方子弹的列表
  my_bullet_list = []
    def get_event(self) -> None:
    '''
     获取事件
     '''
    # 获取所有事件
    event_list = pygame.event.get() 
    # 遍历事件
    for event in event_list:
      # 判断是什么事件,然后做出相应的处理
      if event.type == pygame.QUIT:
        # 点击关闭按钮
        self.end_game()
      if event.type == pygame.KEYDOWN:
        # 按下键盘
        if event.key == pygame.K_LEFT:
          print('坦克向左移动')
          # 修改方向
          MainGame.my_tank.direction = 'L'
          # 修改坦克移动的状态  
          MainGame.my_tank.remove = True
        elif event.key == pygame.K_RIGHT:
          print('坦克向右移动')
          # 修改方向
          MainGame.my_tank.direction = 'R'  
          # 修改坦克移动的状态  
          MainGame.my_tank.remove = True
        elif event.key == pygame.K_UP:
          print('坦克向上移动')
          # 修改方向
          MainGame.my_tank.direction = 'U'
          # 修改坦克移动的状态  
          MainGame.my_tank.remove = True
        elif event.key == pygame.K_DOWN:
          # 修改方向
          MainGame.my_tank.direction = 'D'
          print('坦克向下移动')
          # 修改坦克移动的状态  
          MainGame.my_tank.remove = True
        elif event.key == pygame.K_SPACE:
          # 发射子弹
          print('发射子弹')
          # 创建子弹
          m_bullet = Bullet(MainGame.my_tank)
          # 将子弹添加到列表中
          MainGame.my_bullet_list.append(m_bullet)


      if event.type == pygame.KEYUP and event.key in (pygame.K_LEFT,pygame.K_RIGHT,pygame.K_UP,pygame.K_DOWN):
        # 修改坦克移动的状态  
        MainGame.my_tank.remove = False

将子弹添加到窗口

 def start_game(self) -> None:
    '''
     开始游戏
     '''
    # 初始化游戏窗口
    pygame.display.init()
    # 创建一个窗口
    MainGame.window = pygame.display.set_mode((SCREEN_WIDTH,SCREEN_HEIGHT))
    # 设置窗口标题
    pygame.display.set_caption('坦克大战1.0')
    # 创建一个我方 坦克
    MainGame.my_tank = MyTank(350,200)
    # 创建敌方坦克
    self.create_enemy_tank()
    # 刷新窗口
    while True:
      sleep(0.02)
      # 给窗口设置填充色
      MainGame.window.fill(BG_COLOR)
      # 增加提示文字
      # 1.要增加文字内容
      # num = 6
      text = self.get_text_surface(f'敌方坦克剩余数量{MainGame.enemy_tank_count}')
      # 2.如何把文字加上
      MainGame.window.blit(text,(10,10))
      # 增加事件
      self.get_event()
      # 显示 我方坦克
      MainGame.my_tank.display_tank()
      # 显示敌方坦克
      self.display_enemy_tank()
      # 移动坦克
      if MainGame.my_tank.remove:
        MainGame.my_tank.move()
      # 显示我方子弹
      self.display_my_bullet()
      pygame.display.update()

17、我方子弹移动

修改子弹类

  #子弹的移动方法
   def move(self) -> None:
    '''
     子弹的移动
     '''
    # 根据子弹生成的方向来的移动
    if self.direction == "L":
      # 判断子弹是否超出屏幕
      if self.rect.left > 0:
        self.rect.left -= self.speed
    elif self.direction == "R":
      # 判断子弹是否超出屏幕
      if self.rect.left + self.rect.width < 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 < SCREEN_HEIGHT:
        self.rect.top += self.speed

修改MainGame类,显示子弹方法

  def display_my_bullet(self) -> None:
    '''
     显示我方子弹
     '''
    for my_bullet in MainGame.my_bullet_list:
      # 显示我方子弹
      my_bullet.display_bullet()
      # 移动我方子弹
      my_bullet.move()

18、子弹的消亡与数量控制

修改子弹类,创建live属性,与根据移动位置修改live状态

class Bullet:
  '''
   子弹类
   '''
  def __init__(self,tank) -> None:
    # 设置子弹的状态
    self.live = True

  def move(self) -> None:
    '''
     子弹的移动
     '''
    # 根据子弹生成的方向来的移动
    if 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 + self.rect.width < SCREEN_WIDTH:
        self.rect.left += self.speed
      else:
        self.live = False
    elif 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 + self.rect.height < SCREEN_HEIGHT:
        self.rect.top += self.speed
      else:
        self.live = False

修改MainGame类,修改显示子弹类,与事件监听

def display_my_bullet(self) -> None:
  '''
     显示我方子弹
     '''
  for my_bullet in MainGame.my_bullet_list:
    # 判断子弹是否存活
    if my_bullet.live:
      # 显示我方子弹
      my_bullet.display_bullet()
      # 移动我方子弹
      my_bullet.move()
    else:
      # 从列表中移除
      MainGame.my_bullet_list.remove(my_bullet)

def get_event(self) -> None:
    '''
     获取事件
     '''
    # 获取所有事件
    event_list = pygame.event.get() 
    # 遍历事件
    for event in event_list:
      # 判断是什么事件,然后做出相应的处理
      if event.type == pygame.QUIT:
        # 点击关闭按钮
        self.end_game()
      if event.type == pygame.KEYDOWN:
        # 按下键盘
        if event.key == pygame.K_LEFT:
          print('坦克向左移动')
          # 修改方向
          MainGame.my_tank.direction = 'L'
          # 修改坦克移动的状态  
          MainGame.my_tank.remove = True
        elif event.key == pygame.K_RIGHT:
          print('坦克向右移动')
          # 修改方向
          MainGame.my_tank.direction = 'R'  
          # 修改坦克移动的状态  
          MainGame.my_tank.remove = True
        elif event.key == pygame.K_UP:
          print('坦克向上移动')
          # 修改方向
          MainGame.my_tank.direction = 'U'
          # 修改坦克移动的状态  
          MainGame.my_tank.remove = True
        elif event.key == pygame.K_DOWN:
          # 修改方向
          MainGame.my_tank.direction = 'D'
          print('坦克向下移动')
          # 修改坦克移动的状态  
          MainGame.my_tank.remove = True
        elif event.key == pygame.K_SPACE:
          # 判断子弹是否上限
          if len(MainGame.my_bullet_list) < 5:
            # 发射子弹
            print('发射子弹')
            # 创建子弹
            m_bullet = Bullet(MainGame.my_tank)
            # 将子弹添加到列表中
            MainGame.my_bullet_list.append(m_bullet)

      if event.type == pygame.KEYUP and event.key in (pygame.K_LEFT,pygame.K_RIGHT,pygame.K_UP,pygame.K_DOWN):
        # 修改坦克移动的状态  
        MainGame.my_tank.remove = False

19、敌方坦克发射子弹

实现发射子弹方法

  def shot(self):
    '''
     敌方坦克的射击
     '''
    num = random.randint(1,100)
    if num < 5:
      return Bullet(self)

敌方坦克加入窗口后,发射子弹,并将子弹添加到敌方子弹列表中

 #将敌方坦克加入到窗口中
  def display_enemy_tank(self) -> None:
    '''
     显示敌方坦克
     '''
    for e_tank in self.enemy_tank_list:
      # 显示敌方坦克
      e_tank.display_tank()
      # 移动敌方坦克
      e_tank.rand_move()
      # 发射子弹
      e_bullet = e_tank.shot()
      # 判断是否有子弹
      if e_bullet:
        # 将子弹增加到列表中
        MainGame.enemy_bullet_list.append(e_bullet)

将敌方发射的子弹添加到窗口

  def start_game(self) -> None:
    '''
     开始游戏
     '''
    # 初始化游戏窗口
    pygame.display.init()
    # 创建一个窗口
    MainGame.window = pygame.display.set_mode((SCREEN_WIDTH,SCREEN_HEIGHT))
    # 设置窗口标题
    pygame.display.set_caption('坦克大战1.0')
    # 创建一个我方 坦克
    MainGame.my_tank = MyTank(350,200)
    # 创建敌方坦克
    self.create_enemy_tank()
    # 刷新窗口
    while True:
      sleep(0.02)
      # 给窗口设置填充色
      MainGame.window.fill(BG_COLOR)
      # 增加提示文字
      # 1.要增加文字内容
      # num = 6
      text = self.get_text_surface(f'敌方坦克剩余数量{MainGame.enemy_tank_count}')
      # 2.如何把文字加上
      MainGame.window.blit(text,(10,10))
      # 增加事件
      self.get_event()
      # 显示 我方坦克
      MainGame.my_tank.display_tank()
      # 显示敌方坦克
      self.display_enemy_tank()
      # 移动坦克
      if MainGame.my_tank.remove:
        MainGame.my_tank.move()
      # 显示我方子弹
      self.display_my_bullet()
      # 显示敌方子弹
      self.display_enemy_bullet()
      pygame.display.update()

20、我方子弹与敌方坦克的碰撞检测

在游戏开发中,通常把显示图像的对象叫做精灵Spire,精灵需要有两个属性,image要显示的图像,rect图像要显示在屏幕的位置。

在Pygame框架中,使用pygame. sprite模块中的内置函数可以实现碰撞检测。代码如下:

pygame.sprite.collide_rect(first, second) #返回布尔值

修改坦克类,增加是否存活状态

class Tank:
  '''
   坦克类
   '''
  def __init__(self) -> None:
    self.live = True

在子弹类中增加我方子弹碰撞敌方坦克的方法,如果发生碰撞,修改我方子弹及敌方坦克live属性的状态值。

  def hit_enemy_tank(self):
    for e_tank in MainGame.enemy_tank_list:
      # 判断子弹是否击中坦克
      if collide_rect(self,e_tank):
        # 修改子弹的状态
        self.live = False
        e_tank.live = False

在我方子弹移动后判断子弹是否与敌方坦克碰撞。

  def display_my_bullet(self) -> None:
    '''
     显示我方子弹
     '''
    for my_bullet in MainGame.my_bullet_list:
      # 判断子弹是否存活
      if my_bullet.live:
        # 显示我方子弹
        my_bullet.display_bullet()
        # 移动我方子弹
        my_bullet.move()
        # 判断我方子弹是否击中敌方坦克
        my_bullet.hit_enemy_tank()
      else:
        # 从列表中移除
        MainGame.my_bullet_list.remove(my_bullet)

修改MainGame类敌方坦克显示逻辑,根据敌方坦克是否存活

def display_enemy_tank(self) -> None:
  '''
     显示敌方坦克
     '''
  for e_tank in self.enemy_tank_list:
    #  判断敌方坦克是否存活
    if e_tank.live:
      # 显示敌方坦克
      e_tank.display_tank()
      # 移动敌方坦克
      e_tank.rand_move()
      # 发射子弹
      e_bullet = e_tank.shot()
      # 判断是否有子弹
      if e_bullet:
        # 将子弹增加到列表中
        MainGame.enemy_bullet_list.append(e_bullet)
      else:
        # 从列表中移除
        self.enemy_tank_list.remove(e_tank)

21、爆炸效果

初始化爆炸类

 def __init__(self,tank:Tank) -> None:
    # 加载爆炸效果的图片
    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.rect = tank.rect
    # 设置爆炸效果的索引
    self.step = 0
    # 获取需要渲染的图像
    self.image = self.images[self.step]
    # 设置爆炸的状态
    self.live = True

展示爆炸效果。

 def display_explode(self) -> None:
    '''
     显示爆炸效果
     '''
    # 判断当前爆照的效果是否播放完毕
    if self.step < len(self.images):
      # 获取当前爆炸效果的图像
      self.image = self.images[self.step]
      # 获取下一张爆炸效果的图像的索引
      self.step += 1
      # 绘制爆炸效果
      MainGame.window.blit(self.image,self.rect)
    else:
      # 初始化爆炸效果的索引
      self.step = 0
      # 设置爆炸效果的状态,代表爆炸过了
      self.live = False

在我方子弹碰撞敌方坦克的方法中,如果检测到碰撞,产生爆炸类,并将爆炸效果添加到爆炸列表。

  def hit_enemy_tank(self):
    for e_tank in MainGame.enemy_tank_list:
      # 判断子弹是否击中坦克
      if collide_rect(self,e_tank):
        # 爆炸效果
        explode = Explode(e_tank)
        MainGame.explode_list.append(explode)
        # 修改子弹的状态
        self.live = False
        e_tank.live = False

将爆炸效果添加到窗口

  #新增方法: 展示爆炸效果列表
  def display_explode(self) -> None:
    '''
     显示爆炸效果
     '''
    for explode in MainGame.explode_list:
      # 判断是否活着
      if explode.live:
        # 显示爆炸效果
        explode.display_explode()
      else:
        # 从列表中移除
        MainGame.explode_list.remove(explode)

22、敌方子弹与我方坦克碰撞检测

子弹类中,新增敌方子弹与我方坦克的碰撞。如果发生碰撞,修改敌方子弹、我方坦克的状态及产生爆炸效果。

  #新增敌方子弹与我方坦克的碰撞方法
  def hit_my_tank(self):
    # 判断我方坦克是否活着
    if MainGame.my_tank and MainGame.my_tank.live:
      # 判断字段是否击中我方坦克
      if collide_rect(self,MainGame.my_tank):
        # 爆炸效果
        explode = Explode(MainGame.my_tank)
        MainGame.explode_list.append(explode)
        # 修改子弹的状态
        self.live = False
        MainGame.my_tank.live = False

添加敌方子弹到窗口中时候,如果子弹还活着,显示子弹、调用子弹移动并判断敌方子弹是否与我方坦克发生碰撞。

  #将敌方子弹加入到窗口中
  def display_my_bullet(self) -> None:
    '''
     显示我方子弹
     '''
    for my_bullet in MainGame.my_bullet_list:
      # 判断子弹是否存活
      if my_bullet.live:
        # 显示我方子弹
        my_bullet.display_bullet()
        # 移动我方子弹
        my_bullet.move()
        # 判断我方子弹是否击中敌方坦克
        my_bullet.hit_enemy_tank()
      else:
        # 从列表中移除
        MainGame.my_bullet_list.remove(my_bullet)

23、我方坦克无线重生

坦克大战游戏中一般我方坦克有重生的功能,当按下键盘的Esc键时候,让我方坦克重生。重生指的就是重新创建我方坦克

  def create_my_tank(self) -> None:
    '''
     创建我方坦克
     '''
    MainGame.my_tank = MyTank(350,200)
  #获取事件
  def getEvent(self):
    #获取所有事件
    eventList= pygame.event.get()
    #遍历事件
    for event in event_list:
      if event.type == pygame.KEYDOWN:
        # 如果我方坦克死亡,按下esc键,重新生成我方坦克
        if not MainGame.my_tank and event.key == pygame.K_ESCAPE:
            print('重新生成我方坦克')
            # 按下esc键,重新生成我方坦克
            self.create_my_tank()

24、加载墙壁

初始化墙壁类

class Wall:
  '''
   墙壁类
   '''
  def __init__(self,left,top) -> None:
    # 加载图片
    self.image = pygame.image.load('./img/steels.gif')
    # 获取墙壁的图形
    self.rect = self.image.get_rect()
    # 设置墙壁的位置
    self.rect.left = left
    self.rect.top = top
  def display_wall(self) -> None:
    '''
     显示墙壁
     '''
    MainGame.window.blit(self.image,self.rect)

创建墙壁

  def create_wall(self) -> None:
    '''
     创建墙壁
     '''
    top = 200
    for i in range(6):
      # 创建墙壁
      wall = Wall(i*128,top)
      # 添加到墙壁列表中
      MainGame.wall_list.append(wall)

墙壁加入到窗口

def display_wall(self) -> None:
    '''
     显示墙壁
     '''
    for wall in MainGame.wall_list:
        # 显示墙壁
        wall.display_wall()

25、子弹不能穿墙

子弹不能穿墙指子弹碰到墙壁后消失。因此,子弹类中新增方法,子弹与墙壁的碰撞,如果子弹与墙壁碰撞,修改子弹的状态。另外还需要将墙壁的生命值减少,如果墙壁的生命值小于等于零时候修改墙壁的状态。

  #新增子弹与墙壁的碰撞
  def hit_wall(self):
    '''
     碰撞墙壁
     '''
    for wall in MainGame.wall_list:
      # 判断是否碰撞
      if collide_rect(self,wall):
        #  修改子弹的状态
        self.live = False

设置我方与敌方坦克子弹移动时,增加碰撞检测

  def display_enemy_bullet(self) -> None:
    '''
     显示敌方子弹
     '''
    for e_bullet in MainGame.enemy_bullet_list:
      # 显示子弹
      if e_bullet.live:
        # 如果子弹存活,显示子弹
        e_bullet.display_bullet()
        e_bullet.move()
        # 判断是否击中我方坦克
        e_bullet.hit_my_tank()
        # 判断是否击中墙壁
        e_bullet.hit_wall()
      else:
        # 如果子弹不存活,从列表中移除
        MainGame.enemy_bullet_list.remove(e_bullet)
  def display_my_bullet(self) -> None:
    '''
     显示我方子弹
     '''
    for my_bullet in MainGame.my_bullet_list:
      # 判断子弹是否存活
      if my_bullet.live:
        # 显示我方子弹
        my_bullet.display_bullet()
        # 移动我方子弹
        my_bullet.move()
        # 判断我方子弹是否击中敌方坦克
        my_bullet.hit_enemy_tank()
        # 判断我方子弹是否击中墙壁
        my_bullet.hit_wall()
      else:
        # 从列表中移除
        MainGame.my_bullet_list.remove(my_bullet)

26、设置子弹耐久值

设置墙壁生命值与存活状态

class Wall:
  '''
   墙壁类
   '''
  def __init__(self,left,top) -> None:
    # 加载图片
    self.image = pygame.image.load('./img/steels.gif')
    # 获取墙壁的图形
    self.rect = self.image.get_rect()
    # 设置墙壁的位置
    self.rect.left = left
    self.rect.top = top
    # 设置墙壁的生命值
    self.hp = 3
    # 设置墙壁的状态
    self.live = True

修改子弹碰撞墙壁逻辑

class Bullet:
  '''
   子弹类
   '''
  def hit_wall(self):
    '''
     碰撞墙壁
     '''
    for wall in MainGame.wall_list:
      # 判断是否碰撞
      if collide_rect(self,wall):
        #  修改子弹的状态
        self.live = False
        # 修改墙壁的生命值
        wall.hp -= 1
        # 判断墙壁是否依然显示
        if wall.hp <= 0:
          wall.live = False

修改显示墙壁逻辑

 def display_wall(self) -> None:
    '''
     显示墙壁
     '''
    for wall in MainGame.wall_list:
      if wall.live:
        # 显示墙壁
        wall.display_wall()
      else:
        # 从列表中移除
        MainGame.wall_list.remove(wall)

27、坦克不能穿墙功能

坦克不能穿墙指坦克一旦碰到墙壁则不能再移动,也就是需要修改坦克的坐标为移动之前的。因此在坦克类中新增属性oldLeft、oldTop记录移动之前的坐标,新增stay()、hitWalls()方法。

class Tank:
  '''
   坦克类
   '''
  def __init__(self) -> None:
    self.live = True
    # 记录坦克原来的位置
    self.old_left = 0
    self.old_top = 0 
  def move(self) -> None:
    '''
     坦克的移动
     '''
    # 记录坦克原来的位置,为了方便还原碰撞后的位置
    self.old_left = self.rect.left
    self.old_top = self.rect.top
    if self.direction == "L":
      # 判断坦克的位置是否已左边界
      if self.rect.left > 0:
        # 修改坦克的位置 离左边的距离  - 操作
        self.rect.left = self.rect.left - self.speed
    elif self.direction == "R":
      # 判断坦克的位置是否已右边界
      if self.rect.left + self.rect.width < SCREEN_WIDTH:
        # 修改坦克的位置 离左边的距离  + 操作
        self.rect.left = self.rect.left + self.speed
    elif self.direction == "U":
      # 判断坦克的位置是否已上边界
      if self.rect.top > 0:
        # 修改坦克的位置 离上边的距离  - 操作
        self.rect.top = self.rect.top - self.speed
    elif self.direction == "D":
      # 判断坦克的位置是否已下边界
      if self.rect.top + self.rect.height < SCREEN_HEIGHT:
        # 修改坦克的位置 离上边的距离  + 操作
        self.rect.top = self.rect.top + self.speed

  def tank_hit_wall(self) -> None:
    '''
     坦克和墙壁的碰撞
     '''
    for wall in MainGame.wall_list:
      # 检测当前坦克是否能和墙壁发生碰撞
      if pygame.sprite.collide_rect(self,wall):
        # 将位置还原到碰撞前的位置
        self.rect.left = self.old_left
        self.rect.top = self.old_top

坦克移动时,检测是否碰撞

def display_enemy_tank(self) -> None:
  '''
     显示敌方坦克
     '''
  for e_tank in self.enemy_tank_list:
    #  判断敌方坦克是否存活
    if e_tank.live:
      # 显示敌方坦克
      e_tank.display_tank()
      # 移动敌方坦克
      e_tank.rand_move()
      # 判断是否与墙壁发生碰撞
      e_tank.tank_hit_wall()
      # 发射子弹
      e_bullet = e_tank.shot()
      # 判断是否有子弹
      if e_bullet:
        # 将子弹增加到列表中
        MainGame.enemy_bullet_list.append(e_bullet)
      else:
        # 从列表中移除
        self.enemy_tank_list.remove(e_tank)
 # 判断我方坦克是否死亡
if MainGame.my_tank and MainGame.my_tank.live:
  # 移动坦克
  if MainGame.my_tank.remove:
    MainGame.my_tank.move()
    # 检测我方坦克是否与墙壁发生碰撞
    MainGame.my_tank.tank_hit_wall()

28、双方坦克之间碰撞检测

如果我方坦克碰撞到敌方坦克,则我方坦克再不能继续移动。同理如果敌方坦克碰撞到我方坦克也不能继续移动。

在坦克类中新增坦克与坦克碰撞的检测方法

class Tank:
  def tank_collide_tank(self,tank):
    '''
       检测2个坦克是否碰撞
     '''
    # 判断是否都存活
    if self.live and tank.live:
      if pygame.sprite.collide_rect(self,tank):
        # 将位置还原到碰撞前的位置
        self.rect.left = self.old_left
        self.rect.top = self.old_top

我方坦克移动后,调用是否与敌方坦克发生碰撞。

      #根据坦克的开关状态调用坦克的移动方法
      if MainGame.TANK_P1 and not MainGame.TANK_P1.stop:
        MainGame.TANK_P1.move()
        #调用碰撞墙壁的方法
        MainGame.TANK_P1.hitWalls()
        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):
        # 让敌方坦克停下来  stay()
        self.stay()

敌方坦克添加到窗口时候,调用是否与我方坦克碰撞。

  #将敌方坦克加入到窗口中
  def blitEnemyTank(self):
    for eTank in MainGame.EnemyTank_list:
      if eTank.live:
        eTank.displayTank()
        # 坦克移动的方法
        eTank.randMove()
        #调用敌方坦克与墙壁的碰撞方法
        eTank.hitWalls()
        #敌方坦克是否撞到我方坦克
        eTank.hitMyTank()
        # 调用敌方坦克的射击
        eBullet = eTank.shot()
        # 如果子弹为None。不加入到列表
        if eBullet:
          # 将子弹存储敌方子弹列表
          MainGame.Enemy_bullet_list.append(eBullet)
      else:
        MainGame.EnemyTank_list.remove(eTank)

29、音效处理

music是pygame中控制流音频的pygame模块,音乐模块与pygame.mixer紧密相连, pygame.mixer是一个用来处理声音的模块,其含义为“混音器”。游戏中对声音的处理一般包括制造声音和播放声音两部分。

初始化混合器

pygame.mixer.init()

加载一个播放音乐的文件

pygame.mixer.music.load()

开始播放音乐流

pygame.mixer.music.play()

初始化音效类

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

创建坦克时,添加音效。

  #创建我方坦克的方法
  def creatMyTank(self):
    # 创建我方坦克
    MainGame.TANK_P1 = MyTank(400, 300)
    #创建音乐对象
    music = Music('img/start.wav')
    #调用播放音乐方法
    music.play()

我方坦克发射子弹时,添加音效。

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

30、项目完整代码

https://gitee.com/bitwli-luyao/tank.git

import pygame,time,random
_display = pygame.display
COLOR_BLACK = pygame.Color(0, 0, 0)
COLOR_RED = pygame.Color(255, 0, 0)
version = 'v1.25'
class MainGame():
  #游戏主窗口
  window = None
  SCREEN_HEIGHT = 500
  SCREEN_WIDTH = 800
  #创建我方坦克
  TANK_P1 = None
  #存储所有敌方坦克
  EnemyTank_list = []
  #要创建的敌方坦克的数量
  EnemTank_count = 5
  #存储我方子弹的列表
  Bullet_list = []
  #存储敌方子弹的列表
  Enemy_bullet_list = []
  #爆炸效果列表
  Explode_list = []
  #墙壁列表
  Wall_list = []
  #开始游戏方法
  def startGame(self):
    _display.init()
    #创建窗口加载窗口(借鉴官方文档)
    MainGame.window = _display.set_mode([MainGame.SCREEN_WIDTH,MainGame.SCREEN_HEIGHT])
    self.creatMyTank()
    self.creatEnemyTank()
    self.creatWalls()
    #设置一下游戏标题
    _display.set_caption("坦克大战"+version)
    #让窗口持续刷新操作
    while True:
      #给窗口完成一个填充颜色
      MainGame.window.fill(COLOR_BLACK)
      #在循环中持续完成事件的获取
      self.getEvent()
      #将绘制文字得到的小画布,粘贴到窗口中
      MainGame.window.blit(self.getTextSurface("剩余敌方坦克%d辆"%len(MainGame.EnemyTank_list)),(5,5))
      #调用展示墙壁的方法
      self.blitWalls()
      if MainGame.TANK_P1 and MainGame.TANK_P1.live:
        # 将我方坦克加入到窗口中
        MainGame.TANK_P1.displayTank()
      else:
        del MainGame.TANK_P1
        MainGame.TANK_P1 = None
      #循环展示敌方坦克
      self.blitEnemyTank()
      #根据坦克的开关状态调用坦克的移动方法
      if MainGame.TANK_P1 and not MainGame.TANK_P1.stop:
        MainGame.TANK_P1.move()
        #调用碰撞墙壁的方法
        MainGame.TANK_P1.hitWalls()
        MainGame.TANK_P1.hitEnemyTank()
      #调用渲染子弹列表的一个方法
      self.blitBullet()
      #调用渲染敌方子弹列表的一个方法
      self.blitEnemyBullet()
      #调用展示爆炸效果的方法
      self.displayExplodes()
      time.sleep(0.02)
      #窗口的刷新
      _display.update()
  #创建我方坦克的方法
  def creatMyTank(self):
    # 创建我方坦克
    MainGame.TANK_P1 = MyTank(400, 300)
    #创建音乐对象
    music = Music('img/start.wav')
    #调用播放音乐方法
    music.play()
  #创建敌方坦克
  def creatEnemyTank(self):
    top = 100
    for i in range(MainGame.EnemTank_count):
      speed = random.randint(3,6)
      #每次都随机生成一个left值
      left = random.randint(1, 7)
      eTank = EnemyTank(left*100,top,speed)
      MainGame.EnemyTank_list.append(eTank)
  #创建墙壁的方法
  def creatWalls(self):
    for i in range(6):
      wall = Wall(130*i,240)
      MainGame.Wall_list.append(wall)
  def blitWalls(self):
    for wall in MainGame.Wall_list:
      if wall.live:
        wall.displayWall()
      else:
        MainGame.Wall_list.remove(wall)
  #将敌方坦克加入到窗口中
  def blitEnemyTank(self):
    for eTank in MainGame.EnemyTank_list:
      if eTank.live:
        eTank.displayTank()
        # 坦克移动的方法
        eTank.randMove()
        #调用敌方坦克与墙壁的碰撞方法
        eTank.hitWalls()
        #敌方坦克是否撞到我方坦克
        eTank.hitMyTank()
        # 调用敌方坦克的射击
        eBullet = eTank.shot()
        # 如果子弹为None。不加入到列表
        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()
      else:
        MainGame.Bullet_list.remove(bullet)
  #将敌方子弹加入到窗口中
  def blitEnemyBullet(self):
    for eBullet in MainGame.Enemy_bullet_list:
      # 如果子弹还活着,绘制出来,否则,直接从列表中移除该子弹
      if eBullet.live:
        eBullet.displayBullet()
        # 让子弹移动
        eBullet.bulletMove()
        #调用是否碰撞到墙壁的一个方法
        eBullet.hitWalls()
        if MainGame.TANK_P1 and MainGame.TANK_P1.live:
          eBullet.hitMyTank()
      else:
        MainGame.Enemy_bullet_list.remove(eBullet)
  #新增方法: 展示爆炸效果列表
  def displayExplodes(self):
    for explode in MainGame.Explode_list:
      if explode.live:
        explode.displayExplode()
      else:
        MainGame.Explode_list.remove(explode)
  #获取程序期间所有事件(鼠标事件,键盘事件)
  def getEvent(self):
    #1.获取所有事件
    eventList = pygame.event.get()
    #2.对事件进行判断处理(1、点击关闭按钮  2、按下键盘上的某个按键)
    for event in eventList:
      #判断event.type 是否QUIT,如果是退出的话,直接调用程序结束方法
      if event.type == pygame.QUIT:
        self.endGame()
      #判断事件类型是否为按键按下,如果是,继续判断按键是哪一个按键,来进行对应的处理
      if event.type == pygame.KEYDOWN:
        #点击ESC按键让我方坦克重生
        if event.key == pygame.K_ESCAPE and not MainGame.TANK_P1:
          #调用创建我方坦克的方法
          self.creatMyTank()
        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:
            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("子弹数量不足")
            print("当前屏幕中的子弹数量为:%d" % len(MainGame.Bullet_list))
      #结束游戏方法
      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:
          if MainGame.TANK_P1 and MainGame.TANK_P1.live:
            # 修改坦克的移动状态
            MainGame.TANK_P1.stop = True
  #左上角文字绘制的功能
  def getTextSurface(self,text):
    # 初始化字体模块
    pygame.font.init()
    #查看系统支持的所有字体
    # fontList = pygame.font.get_fonts()
    # print(fontList)
    # 选中一个合适的字体
    font = pygame.font.SysFont('kaiti',18)
    # 使用对应的字符完成相关内容的绘制
    textSurface = font.render(text,True,COLOR_RED)
    return textSurface
  def endGame(self):
    print("谢谢使用")
    #结束python解释器
    exit()
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]
    #坦克所在的区域  Rect->
    self.rect = self.image.get_rect()
    #指定坦克初始化位置 分别距x,y轴的位置
    self.rect.left = left
    self.rect.top = top
    #新增速度属性
    self.speed = 5
    #新增属性: 坦克的移动开关
    self.stop = True
    #新增属性  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
    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 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(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()


class EnemyTank(Tank):
  def __init__(self,left,top,speed):
    super(EnemyTank, self).__init__(left,top)
    # self.live = True
    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]
    # 坦克所在的区域  Rect->
    self.rect = self.image.get_rect()
    # 指定坦克初始化位置 分别距x,y轴的位置
    self.rect.left = left
    self.rect.top = top
    # 新增速度属性
    self.speed = speed
    self.stop = True
    #新增步数属性,用来控制敌方坦克随机移动
    self.step = 30


  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 displayEnemtTank(self):
  #   super().displayTank()
  #随机移动
  def randMove(self):
    if self.step <= 0:
      self.direction = self.randDirection()
      self.step = 50
    else:
      self.move()
      self.step -= 1
  def shot(self):
    num = random.randint(1,1000)
    if num <= 20:
      return Bullet(self)
  def hitMyTank(self):
    if MainGame.TANK_P1 and MainGame.TANK_P1.live:
      if pygame.sprite.collide_rect(self, MainGame.TANK_P1):
        # 让敌方坦克停下来  stay()
        self.stay()
class Bullet(BaseItem):
  def __init__(self,tank):
    #图片
    self.image = pygame.image.load('img/enemymissile.gif')
    #方向(坦克方向)
    self.direction = tank.direction
    #位置
    self.rect = self.image.get_rect()
    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 + tank.rect.width / 2 - self.rect.width / 2
    #速度
    self.speed = 7
    #用来记录子弹是否活着
    self.live = True
  #子弹的移动方法
  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
  #展示子弹的方法
  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):
        #产生一个爆炸效果
        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
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.live = False
      self.step = 0
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):
    self.fileName = fileName
    #先初始化混合器
    pygame.mixer.init()
    pygame.mixer.music.load(self.fileName)
  #开始播放音乐
  def play(self):
    # pygame.mixer.music.play()
    pass
MainGame().startGame()


Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐