Python学习笔记(四)之坦克大战

Python学习笔记(四)之坦克大战

第一章 Python 基本概念
第二章 Python 序列 控制语句 函数
第三章 Python 面向编程 异常处理 文件处理
第四章 Python 坦克大战
第五章 Python 并发编程 网络通信
第六章 Python 函数式编程
第七章 Python Linux编程



前言

提示:这里可以添加本文要记录的大概内容:

例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。


提示:以下是本篇文章正文内容,下面案例可供参考

一、前期规划

1.pygame的介绍与环境搭建

在Vscode开发环境里进行对应模块安装和前期准备,主要是创建一个虚拟环境用来开发坦克大战游戏以及安装pyganme模块。
首先创建对应文件夹和py文件后,ctrl+~打开终端命令行创建虚拟环境,安装pygame2.3.0版本。
ps:创建成功后需选择对应虚拟环境重新打开命令行生效。
在这里插入图片描述
在这里插入图片描述
至此,pygame已安装成功。

2.创建窗口

import pygame
#初始函数,使用pygame的第一步;
pygame.init()
#生成主屏幕screen 
pygame.display.set_mode((600,500))
#设置标题
pygame.display.set_caption('Hello Pygame')
while True:  #游戏的主循环是一个无限循环,直到用户退出。在这个主循环里面做的事情就是不停的刷新新画面。
  #刷新屏幕
  pygame.display.update()

在这里插入图片描述

二、需求分析

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

  • 坦克类
    我方坦克
    敌方坦克
    坦克的显示 移动 射击
  • 子弹类
    显示 移动
  • 墙壁类
    显示
  • 爆炸效果类
    显示
  • 音效类
    播放音效
  • 游戏主窗口类
    开始 结束

主窗口类,主要包括开始游戏、结束游戏的功能。

class MainGame:
  '''
   游戏主窗口类
   '''
  def __init__(self) -> None:
    pass
  def start_game(self) -> None:
    '''
     开始游戏
     '''
    pass
  def end_game(self) -> None:
    '''
     结束游戏
     '''
    pass

坦克类:主要包括坦克的创建、显示、移动及射击的功能

class Tank():
  def __init__(self):
    pass
  #坦克的移动方法
  def move(self):
    pass
  #碰撞墙壁的方法
  def hitWalls(self):
    pass
  #射击方法
  def shot(self):
    pass
  #展示坦克
  def displayTank(self):
    pass

我方坦克类继承坦克类,主要包括创建、与敌方坦克的碰撞方法。

class MyTank(Tank):
  def __init__(self):
    pass
  #碰撞敌方坦克的方法
  def hitEnemyTank(self):
    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 display_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_music(self) -> None:
    '''
     播放音效
     '''
    pass

三、项目搭建

1.显示游戏窗口

主要改进点:
1.在全局将背景颜色以及窗口宽高设置通用属性,在后期方便进行修改
2.设置一个类属性windows是空对象在下面调用

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

实现效果:
在这里插入图片描述

2.添加提示文字

在主屏幕上需要添加对应的提示文字 需要创建对应的函数类def get_text_surface,主要是需要获取初始化字体模块以及可以使用的字体,将内容添加上去。
需要进行测试可以添加的字体:
在类中初始化字体并且在主窗口循环中进行打印:

    while True:
      # 给窗口设置填充色
      MainGame.window.fill(BG_COLOR)
      #增加提示文字 要增加的文字内容以及如何把文字加上
      self.get_text_surface('info')
      pygame.display.update()
 def get_text_surface(self,text:str) -> None:
  '''
     获取文字的图片
     '''
  # 初始化字体模块
  pygame.font.init()
  # 获取可以使用的字体
  print(pygame.font.get_fonts())

在终端进行循环打印自带的字体:
在这里插入图片描述

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

在开始游戏方法中调用添加提示文字方法

# 1.要增加文字内容
num = 6
text = self.get_text_surface(f'敌方坦克剩余数量{num}')
# 2.如何把文字加上
MainGame.window.blit(text,(10,10))

在这里插入图片描述

3.增加事件监听

事件监听用于在程序中监听特定的事件,以便在事件发生时能够执行相应的代码。事件可以是用户交互、系统消息等。
添加事件监听,控制上、下、左、右四个方向键,实现针对不同的键改变坦克的方向及移动功能,点击关闭退出游戏。

需要从文档中查看所支持的事件及说明:
https://www.pygame.org/docs/ref/event.html#pygame.event.get

增加判断关闭和键盘移动事件:

 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()
#主循环中增加
      self.get_event()

4.加载我方坦克

需要考虑加载初始化坦克图片加入到主窗口中,以及按下上下左右坦克会朝向不同的方向,还有初始化的时候坦克需要处于的位置。

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:
    # 创建一个我方 坦克
    my_tank = Tank(350,200)
    # 刷新窗口
    while True:
      # 显示 我方坦克
      my_tank.display_tank()

在这里插入图片描述

5.修改我方坦克朝向

触发按键事件来进行坦克方向转换,通过self.image = self.images.get(self.direction)把最新的值渲染出来

class Tank:
  '''
   坦克类
   '''
  def __init__(self,left:int,top:int) -> None:
    # 设置我方坦克的方向
    self.direction = 'L'
    # 获取图片信息
    self.image = self.images.get(self.direction)

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

class MainGame:
  #设置我方坦克
  my_tank = None
  def start_game(self) -> None:
  	# 创建一个我方 坦克
  	MainGame.my_tank = Tank(350,250)
  	# 显示 我方坦克
    MainGame.my_tank.display_tank()

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

6.移动我方坦克

在移动类中用if语句判定:

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

但是在之后代码进行修改时 若需要修改对应每次的移动值较麻烦,可设置移动速度调用,并且判断坦克移动是否超出窗口,判断的临界点是窗口的宽度或者高度-坦克的宽度



class Tank:
  '''
   坦克类
   '''
  def __init__(self,left:int,top:int) -> None:
    # 设置我方坦克位置
    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。

主要需要增加移动开关,在事件里判断,如果为true就去移动

class Tank():
	#设置移动开关,True 表示移动
    self.remove = False
class MainGame:
	 while True:
      sleep(0.02)
      #移动坦克
      if MainGame.my_tank.remove:
        MainGame.my_tank.move()
	  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'
          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:
          print('坦克向下移动')
          # 修改方向
          MainGame.my_tank.direction = 'D'
          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
在这里插入代码片

7.加载敌方坦克

主要需要渲染敌方坦克图片和位置 随机生成敌方坦克的数量和方向,注意点是随机生成敌方坦克的数量及方向

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.rand_direction()
    self.image = self.images.get(self.direction)
    # 坦克所在的区域  Rect->
    self.rect = self.image.get_rect()
    # 指定坦克初始化位置 分别距x,y轴的位置
    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'
class MainGame:

  #存储敌方坦克列表
  enemy_tank_list = []
  #设置敌方坦克数量
  enemy_tank_count = 6

  def start_game(self) -> None:
    # 创建一个敌方坦克
    self.create_enemy_tank()

    # 刷新窗口
    while True:
      # 显示敌方坦克
      self.display_enemy_tank()
    
  def create_enemy_tank(self):
    '''
    创建敌方坦克
    '''
    self.enemy_top  = 100
    self.enemy_speed = 10

    for i in range(self.enemy_tank_count):
      #生成坦克的位置
      left = random.randint(0,600)
      #speed = random.randint(1,15)
      #创建敌方坦克
      e_tank = EnemyTank(left,self.enemy_top,self.enemy_speed)
      #将敌方坦克增加至列表
      self.enemy_tank_list.append(e_tank)
      
  def display_enemy_tank(self) ->None:
    #显示坦克
    for e_tank in self.enemy_tank_list:
      e_tank.display_tank()

8.敌方坦克随机移动

设置敌方坦克可以随机移动并显示,主要逻辑为在EnemyTank类中定义函数rand_move判断敌方坦克移动步长,根据大小来变换方向在主窗口来显示。

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

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

9.完善子弹类

需要判断清楚子弹的位置
在这里插入图片描述

 '''
   子弹类
   '''
  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

10.我方坦克发射子弹

主要是在子弹类中调用显示

  def display_bullet(self) -> None:
    '''
     显示子弹
     '''
    MainGame.window.blit(self.image,self.rect)
  class MainGame:
	  #存储我方子弹
	  my_bullet_list = []
	  while True:
      # 显示我方子弹
      self.display_my_bullet()
      pygame.display.update()
      
      def display_my_bullet(self) ->None:
	    '''
	    显示我方子弹
	    '''
	    for my_bullet in MainGame.my_bullet_list:
	      #显示我方子弹
	      my_bullet.display_bullet()
	  def get_event(self) -> None:
	  for event in event_list:
        elif event.key == pygame.K_SPACE:
          #发射子弹
          print("发射子弹")
          # 创建子弹
          m_bullet = Bullet(MainGame.my_tank)
          # 将子弹添加到列表中
          MainGame.my_bullet_list.append(m_bullet)

11.我方子弹移动

修改子弹类

  #子弹的移动方法
   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()

12.子弹的消亡与数量控制

设置一个 self.live = True来进行判断和控制数量

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

13.敌方坦克发射子弹

实现发射子弹方法

  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:
      # 判断是否有子弹
      if e_bullet:
        # 将子弹增加到列表中
        MainGame.enemy_bullet_list.append(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()
      else:
        # 从列表中移除
        MainGame.my_bullet_list.remove(my_bullet)
  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()
      else:
        #如果子弹不存活,从列表中移除
        MainGame.enemy_bullet_list.remove(e_bullet)

14.我方子弹与敌方坦克的碰撞检测

在游戏开发中,通常把显示图像的对象叫做精灵Spire,精灵需要有两个属性,image要显示的图像,rect图像要显示在屏幕的位置。在Pygame框架中,使用pygame. sprite模块中的内置函数可以实现碰撞检测。
在子弹类中增加我方子弹碰撞敌方坦克的方法,如果发生碰撞,修改我方子弹及敌方坦克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

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

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

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

  def display_my_bullet(self) -> None:
    '''
     显示我方子弹
     '''
    for my_bullet in MainGame.my_bullet_list:
      # 判断子弹是否存活
      if my_bullet.live:
        # 判断我方子弹是否击中敌方坦克
        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:
      # 判断是否有子弹
      if e_bullet:
        # 将子弹增加到列表中
        MainGame.enemy_bullet_list.append(e_bullet)
      else:
        # 从列表中移除
        self.enemy_tank_list.remove(e_tank)

15.爆炸效果

初始化爆炸类,和坦克一样加载图片渲染

 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)

16.敌方子弹与我方坦克碰撞检测

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

  #新增敌方子弹与我方坦克的碰撞方法
  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)

以及在主窗口进行判定我方是否存活:

  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 MainGame.my_tank and MainGame.my_tank.live : 
          # 按下键盘
            if event.key == pygame.K_LEFT:
              print('坦克向左移动')
        if event.type == pygame.KEYUP and event.key in (pygame.K_LEFT,pygame.K_RIGHT , pygame.K_UP , pygame.K_DOWN):
            if MainGame.my_tank and MainGame.my_tank.live : 
              # 修改坦克的移动状态
              MainGame.my_tank.remove = False
    while True:
      #判断我方坦克是否死亡
      if MainGame.my_tank and MainGame.my_tank.live:      
        # 显示我方坦克
        MainGame.my_tank.display_tank()
      else:
        MainGame.my_tank = None
      if MainGame.my_tank and MainGame.my_tank.live :  
        #移动坦克    
        if MainGame.my_tank.remove:
          MainGame.my_tank.move()

17.我方坦克无线重生

坦克大战游戏中一般我方坦克有重生的功能,当按下键盘的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()

18.加载墙壁

初始化墙壁类

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)

墙壁加入到窗口

 #存储墙壁的列表
  wall_list = []
 # 创建墙壁
  self.create_wall()
 #显示墙壁
  self.display_wall()
  def display_wall(self) -> None:
    '''
     显示墙壁
     '''
    for wall in MainGame.wall_list:
      # 显示墙壁
      wall.display_wall()

19.子弹不能穿墙

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

  #新增子弹与墙壁的碰撞
  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.hit_wall()
  def display_my_bullet(self) -> None:

    for my_bullet in MainGame.my_bullet_list:
      if my_bullet.live:
        # 判断我方子弹是否击中墙壁
        my_bullet.hit_wall()

20.设置子弹耐久值

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

class Wall:
    # 设置墙壁的生命值
    self.hp = 3
    # 设置墙壁的状态
    self.live = True

修改子弹碰撞墙壁逻辑

  def hit_wall(self):
    for wall in MainGame.wall_list:
        # 修改墙壁的生命值
        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)

21.坦克不能穿墙

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

    # 记录坦克原来的位置
    self.old_left = 0
    self.old_top = 0 
  def move(self) -> None:
    # 记录坦克原来的位置,为了方便还原碰撞后的位置
    self.old_left = self.rect.left
    self.old_top = self.rect.top
    
  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.tank_hit_wall()
 # 判断我方坦克是否死亡
if MainGame.my_tank and MainGame.my_tank.live:
  # 移动坦克
  if MainGame.my_tank.remove:
    MainGame.my_tank.move()
    # 检测我方坦克是否与墙壁发生碰撞
    MainGame.my_tank.tank_hit_wall()

22.双方坦克之间的碰撞检测

如果我方坦克碰撞到敌方坦克,则我方坦克再不能继续移动。同理如果敌方坦克碰撞到我方坦克也不能继续移动。
在坦克类中新增坦克与坦克碰撞的检测方法

  def tank_collide_tank(self,tank):
    '''
       检测2个坦克是否碰撞
     '''
   # 记录坦克原来的位置,为了方便还原碰撞后的位置
    self.old_left = self.rect.left
    self.old_top = self.rect.top
    # 判断是否都存活
    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
  def display_enemy_tank(self) ->None:
    for e_tank in self.enemy_tank_list:
      if e_tank.live:
        #判断是否和我方坦克碰撞
        e_tank.tank_collide_tank(MainGame.my_tank)  

总结

111

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值