Python坦克大战

模块安装(安装pygame)

  • 1.pip 安装

    • windows+R–>cmd–>命令行输入 pip install pygame
  • 2.pycharm中安装

    • file–>settting–>Project Interpreter–>右侧±-install–>搜索框输入pygame–>下方 install package
  • 3.下载好安装包之后直接安装

    • 在Python官网下载好pygame-1.9.6-cp37-cp37m-win_amd64.whl,打开命令窗口,切换到安装包目录,执行 pip install pygame-1.9.6-cp37-cp37m-win_amd64.whl
  • 使用 pip list 查看是否有pygame

面向对象分析

  • 游戏原理分析

    • 游戏原理:和动画原理相同,快速切换图片,为了避免上一次贴图,每次刷新前,重贴所有图片
  • 实现框架的搭建(类的设计)

    • 主逻辑类

    • 基本坦克类

    • 我方坦克类

    • 敌方坦克类

    • 子弹类

    • 墙壁类

    • 爆炸类

框架搭建

  • 参考代码

    - 主逻辑类
    class MainGame:
    
        def start(self):
            """开始游戏"""
            pass
    
        def game_over(self):
            """结束游戏"""
            pass
    
    # - 基本坦克类
    class BaseTank:
        pass
    
    # - 我方坦克类
    class HeroTank:
        pass
    
    # - 敌方坦克类
    class EnemyTank:
        pass
    
    # - 子弹类
    class Bullet:
        pass
    # - 墙壁类
    class Wall:
        pass
    
    # - 爆炸类
    class Bomb:
        pass
    

主逻辑类

  • 属性:游戏主窗口

  • 方法:开始游戏

    • 窗口初始化

    • 设置窗口

    • 设置标题(坦克大战v_1.0)

    • 窗口背景

    • 游戏应该在无限循环中

      class MainGame:
          #游戏主窗口
          window = None
      
          def start(self):
              """开始游戏"""
              # 调用窗口初始化
              pygame.display.init()
              # 创建窗口
              MainGame.window = pygame.display.set_mode((900,500))
              # 设置窗口标题
              pygame.display.set_caption("坦克大战v_1.0")
              while True:
                  #窗口背景颜色
                  MainGame.window.fill((0,0,0))
                  #刷新
                  pygame.display.update()
      
          def game_over(self):
              """结束游戏"""
              pass
      
主逻辑内进行事件检测
  • 获取新事件

    • pygame.event.get():
      • 鼠标点击窗口事件 pygame.QUIT
      • 键盘按下事件 pygame.KEYDOWN
  • 键盘长按事件

    • pygame.key.getpressed()
  • 参考代码(在主逻辑代码中添加)

        def deal_event(self):
            """事件检测"""
            # print(pygame.event.get())
            for event in pygame.event.get():
                # 1. 鼠标点击关闭窗口事件
                if event.type == pygame.QUIT:
                    print("点击关闭窗口按钮")
                    sys.exit()
                elif event.type==pygame.KEYDOWN:
                    # print("按下键盘")
                    if event.key==pygame.K_LEFT:
                        print("左移")
                    elif event.key==pygame.K_RIGHT:
                        print("右移")
                elif event.type==pygame.MOUSEBUTTONDOWN:
                    print("鼠标点击事件")
    

我方坦克分析

  • 由于我方坦克和敌方坦克有相似属性和方法,所以可以定义基本坦克类,让我方坦克和敌方坦克继承基本坦克类

  • 基本坦克类:

    • 属性:图片、方向、图片矩形区域、坦克位置、移动速度、是否活着
    • 方法:移动、贴图
  • 参考代码(定义基本坦克类,让我方坦克类继承)

    class BaseTank:
        def __init__(self,x,y):
            """基本坦克类的属性"""
            # 加载图片文件,返回图片对象
            #将坦克图片储存在字典中
            self.images = {
           
                "U":pygame.image.load("tank_img/p1tankU.gif"),
                "D":pygame.image.load("tank_img/p1tankU.gif"),
                "L":pygame.image.load("tank_img/p1tankU.gif"),
                "R":pygame.image.load("tank_img/p1tankU.gif"),
            }
            #给初始化坦克一个方向
            self.direction = "U"
            #根据坦克方向获取坦克图片
            self.image = self.images[self.direction]
            #获取图片矩形区域
            self.rect = self.image.get_rect()
            #根据传入的参数,决定坦克的位置
            self.rect.x = x #坦克的x坐标
            self.rect.y = y #坦克的y坐标
            #移动速度
            self.speed = 3
            #是否活着
            self.live = True
        
        def display_tank(self):
            """贴坦克图片的方法"""
            #先获取坦克图片
            self.image = self.images[self.direction]
            #贴坦克图片
            MainGame.window.blit(self.image,self.rect)
    
    # - 我方坦克类
    class HeroTank(BaseTank):
        def __init__(self,x,y):
            super(HeroTank, self).__init__(x,y)
            self.speed = 2
    
  • 创建我方坦克,并加载图片

    • 在主逻辑中,一开始就存在我方坦克,定义P类属性记录
    • 先定义创建我方坦克的方法(create_hero_tank),再定义加载坦克图片的方法(load_hero_tank)
    • 在开始游戏时,调用创建坦克对象的方法,在循环中加载坦克图片
    #主逻辑中记录坦克
    P1 = None
    
        def create_tank(self):
            """创建我方坦克"""
            #判断是否创建了我方坦克
            if not MainGame.P1:
                MainGame.P1 = HeroTank(500,400) #坦克的初始位置
    
        def load_heor_tank(self):
            """加载我方坦克"""
            if MainGame.P1 and MainGame.P1.live:
                #如果坦克活着就调用坦克贴图的方法
                MainGame.P1.display_tank()
            else:
                #如果坦克死了,就删除坦克对象
                del MainGame.P1
                MainGame.P1 = None
    #在开始游戏时调用 self.create_tank()
    #在开始游戏循环中调用  self.load_heor_tank()
    
  • 实现我方坦克移动的方法

    • 在基本坦克类添加坦克移动方法

    • 判断坦克的方向属性,是哪个方向就向哪个方向移动(向右x为正,向下y为正)

      • 移动方式:图片坐标+坦克速度 (图片坐标-坦克速度)
          def move(self):
              """坦克移动的方法"""
              if self.direction=="U": #坦克的方向
                  if self.rect.y>0: #坦克的y坐标在边界内就一直移动
                      self.rect.y -= self.speed
              elif self.direction=="D": #坦克的方向
                  if self.rect.y<SCREEN_HEIGHT-self.rect.height: #坦克的y坐标在边界内就一直移动
                      self.rect.y += self.speed
              elif self.direction=="L": #坦克的方向
                  if self.rect.x>0: #坦克的x坐标在边界内就一直移动
                      self.rect.x -= self.speed
              elif self.direction=="R": #坦克的方向
                  if self.rect.x<SCREEN_WIDTH-self.rect.width: #坦克的x坐标在边界内就一直移动
                      self.rect.x -= self.speed
      #在加载我方坦克中调用坦克移动方法  MainGame.P1.move()
      #此时坦克只能向上移动
      
  • 优化坦克移动的方法

    • 检测键盘长按事件,获取按键状态
    • 检测到键盘改变方向之后,调用父类移动方法
    #我方坦克重写move()方法
        def move(self):
            """我方坦克移动方法"""
            # 键盘长按事件,获取键盘上所有按键状态,按下1,没按0
            keys_status = pygame.key.get_pressed()
            # print(keys_status)
            if keys_status[pygame.K_UP]: #按键“上”被按下
                self.direction = "U" #修改方向属性
                super(HeroTank, self).move() #调用父类中移动方法
            elif keys_status[pygame.K_DOWN]:
                self.direction = "D"
                super(HeroTank, self).move()
            elif keys_status[pygame.K_LEFT]:
                self.direction = "L"
                super(HeroTank, self).move()
            elif keys_status[pygame.K_RIGHT]:
                self.direction = "R"
                super(HeroTank, self).move()
    

敌方坦克分析

  • 敌方坦克和基本坦克类有相同的属性,如位置,同时也有自己的属性,如随机方向和随机速度、图片等,所以让敌方坦克类继承基本坦克类,再复写敌方坦克的属性和方法

    # - 敌方坦克类
    class EnemyTank:
        def __init__(self,x,y):
            super(EnemyTank, self).__init__(x,y)
            # 将敌方坦克图片储存在字典中
            self.images = {
           
                "U": pygame.image.load("tank_img/enemy1U.gif"),
                "D": pygame.image.load("tank_img/enemy1D.gif.gif"),
                "L": pygame.image.load("tank_img/enemy1L.gif.gif"),
                "R": pygame.image.load("tank_img/enemy1R.gif.gif"),
            }
            #随机方向
            self.direction = self.random_direction()
            #根据方向取出对应的图片
            self.image = self.images[self.direction]
            #随机速度
            self.speed = random.randint(1,2)
    
        def random_direction(self):
            directons = ["U","D","L","R"]
            return random.choice(directons)
    
  • 主逻辑中,新增创建敌方坦克方法create_enemy_tank和加载敌方坦克load_enemy_tank的方法

  • 在最开始添加ENEMY_TANK_COUNT常量记录敌方坦克数量,在主逻辑中定义存储敌方坦克对象的列表enemy_tank_list

  • 在游戏开始,调用创建敌方坦克方法create_enemy_tank,在游戏循环中,调用加载敌方坦克load_enemy_tank的方法

    ENEMY_TANK_COUNT = 5
    class MainGame:
        #主逻辑中定义类属性
        #记录敌方坦克
        enemy_tank_list = []
        
        
    	def create_enemy_tank(self):
            """创建敌方坦克"""
            #创建多辆坦克
            for i in range(ENEMY_TANK_COUNT):
                #随机敌方坦克的坐标
                e_tank = EnemyTank(random.randint(0,8)*100,100)
                #创建敌方坦克后添加到主逻辑的敌方坦克列表中
                MainGame.enemy_tank_list.append(e_tank)
    
        def load_enemy_tank(self):
            """加载敌方坦克"""
            #获取每一辆敌方坦克对象
            for e_tank in MainGame.enemy_tank_list:
                #判断坦克是否活着
                if e_tank.live:
                    #如果活着,贴坦克图片
                    e_tank.display_tank()
                else:
                    MainGame.enemy_tank_list.remove(e_tank)
    在游戏开始,调用创建敌方坦克方法self.create_enemy_tank(),
    在游戏循环中,调用加载敌方坦克self.load_enemy_tank()    
    此时坦克只能朝一个方向移动
    

敌方坦克随机移动

  • 敌方坦克中新增step属性

    • 作用:记录移动的步数,超过步数,重新生成方向
  • 敌方坦克中:重写父类的移动方法,如果步长大于0,调用父类移动方法,同时步长-1,否则重新生成方向和步长

    #记录步数(每个方向走的步数)
            self.step = 50
    #敌方坦克类:重写父类中的移动方法        
        def move(self):
            """敌方坦克移动"""
            if self.step>0:
                super(EnemyTank, self).move()
                self.step -= 1
            else:
                #重新生成方向
                self.direction = self.random_direction()
                # self.step = 50
                self.step = (5-self.speed)*random.randint(40,60)        
    

我方坦克发射子弹

  • 子弹类

    • 属性:图片,是否或者,速度,方向,子弹位置

    • 方法:贴图,移动

      class Bullet:
          def __init__(self,tank):
              #图片
              self.image = pygame.image.load("tank_img/tankmissile.gif")
              #是否活着
              self.live = True
              #移动速度
              self.speed = 5
              #子弹方向和坦克方向一致
              self.direction = tank.direction
              #设置子弹位置
              self.rect = self.image.get_rect()
              self.rect.x = tank.rect.centerx #子弹中心就是坦克中心
              self.rect.y = tank.rect.centery
      
          def display_bullet(self):
              """子弹贴图方法"""
              MainGame.window.blit(self.image,self.rect)
      
  • 由于是坦克发射子弹,所以要在父类中新增发射方法

  • 在按下空格按键的时候,创建子弹,所有在事件检测中添加代码

    #基本坦克类中,新增发射子弹方法
    class BaseTank:
        def shot(self):
            bullet = Bullet(self)
            return bullet
        
    #事件检测中添加我方坦克发射方法
        def deal_event(self):
            """事件检测"""
            for event in pygame.event.get():
                if event.type == pygame.KEYDOWN: #键盘按下事件
                    if event.key==pygame.K_SPACE:
                        # print("发射子弹")
                        #创建一个子弹对象,添加到我方子弹列表中
                        bullet = MainGame.P1.shot()
                        MainGame.bullet_hero_list.append(bullet)
    #在主逻辑类中,新增存储子弹的列表
    bullet_hero_list = [] #记录我方子弹
    
  • 在主逻辑类中,新增加载我方子弹的方法

        def load_bullet_hero(self):
            """加载我方坦克发射的子弹"""
            #取出每一颗子弹
            for b in MainGame.bullet_hero_list:
                #判断子弹是否活着
                if b.live:
                    #贴子弹图片
                    b.display_bullet()
                else:
                    MainGame.bullet_hero_list.remove(b)
    #在开始游戏循环中,调用加载子弹方法 self.load_bullet_hero()
    #此时子弹还不能移动
    
  • 新增子弹移动方法

        def move(self):
            """子弹移动方法"""
            #子弹方向朝上,应向上移动
            if self.direction == "U":
                #在边界内,子弹正常移动,超出边界不移动
                if self.rect.centery > -self.rect.height//2:
                    self.rect.centery -= self.speed
                else:
                    self.live = False
            elif self.direction == "D":
                # 在边界内,子弹正常移动,超出边界不移动
                if self.rect.centery < SCREEN_HEIGHT+self.rect.height//2:
                    self.rect.centery += self.speed
                else:
                    self.live = False
            elif self.direction == "L":
                # 在边界内,子弹正常移动,超出边界不移动
                if self.rect.centerx >-self.rect.width:
                    self.rect.centerx -= self.speed
                else:
                    self.live = False
            elif self.direction == "R":
                # 在边界内,子弹正常移动,超出边界不移动
                if self.rect.centerx < SCREEN_WIDTH+self.rect.width//2:
                    self.rect.centerx += self.speed
                else:
                    self.live = False
    
  • 一次最多发射三颗子弹

    • 在事件检测中修改
    elif event.key==pygame.K_SPACE:
        # print("发射子弹")
        #如果我方坦克活着
        if MainGame.P1 and MainGame.P1.live:
            if len(MainGame.bullet_hero_list)<3:
                #创建一个子弹对象,添加到我方子弹列表中
                bullet = MainGame.P1.shot()
                MainGame.bullet_hero_list.append(bullet)
            else:
            print("一次最多可以发射三颗子弹")
    

敌方坦克发射子弹

  • 敌方坦克类中定义发射子弹的计算器count,主逻辑中增加存储敌方的子弹列表

  • 敌方坦克类中重写shot方法,count==200时,再发射子弹

    #敌方坦克类中定义发射子弹的计算器
    self.count = 1
    #主逻辑中增加存储敌方的子弹列表
    bullet_enemy_list = []
    
    #敌方坦克类中重写shot方法,count==200时,再发射子弹
        def shot(self):
            self.count+=1
            if self.count==200:
                b = super(EnemyTank, self).shot()
                MainGame.bullet_hero_list.append(b)
                self.count=1
    
  • 游戏类中,新增加载敌方子弹的方法

        def load_bullet_enemy(self):
            """加载敌方子弹"""
            #取出每一颗子弹
            for b in MainGame.bullet_enemy_list:
                if b.live:
                    b.display_bullet()
                else:
                    MainGame.bullet_enemy_list.remove(b)
    #游戏循环中,调用self.load_bullet_enemy()
    #在在家敌方坦克中调用shot方法
    #敌方坦克发射子弹-->创建子弹对象,添加到主逻辑子弹列表-->游戏循环中遍历子弹列表,贴子弹图片,和子弹移动
    

子弹消灭坦克

  • 碰撞检测,冲突检测

    #我方子弹是否打中敌方坦克
    #将创建的子弹和每一辆敌方坦克进行碰撞检测
        def hit_enemy_tank(self):
            #将创建的子弹和每一辆敌方坦克进行碰撞检测
            for e_tank in MainGame.enemy_tank_list:
                # 进行一对一的冲突检测
                if pygame.sprite.collide_rect(self,e_tank):
                    #如果相撞,则修改子弹和坦克的生存属性
                    self.live = False
                    e_tank.live = False
    #在加载我方坦克子弹中,完成碰撞方法的调用
        def load_bullet_hero(self):
            """加载我方坦克发射的子弹"""
            #取出每一颗子弹
            for b in MainGame.bullet_hero_list:
                #判断子弹是否活着
                if b.live:
                    #贴子弹图片
                    b.display_bullet()
                    #调用子弹移动的方法
                    b.move()
                    b.hit_enemy_tank() #调用子弹碰撞敌方坦克的方法
    
                    
    #敌方子弹是否打中我方坦克
        def hit_hero_tank(self):
            if pygame.sprite.collide_rect(self,MainGame.P1):
                # 如果相撞,则修改子弹和我方坦克的生存属性
                self.live = False
                MainGame.P1.live = False
    #在加载敌方坦克子弹中,完成碰撞方法的调用
        def load_bullet_enemy(self):
            """加载敌方子弹"""
            #取出每一颗子弹
            for b in MainGame.bullet_enemy_list:
                if b.live:
                    b.display_bullet()
                    b.move()
                    if MainGame.P1 and MainGame.P1.live:
                        b.hit_hero_tank() #调用子弹碰撞我方坦克方法
                else:
                    MainGame.bullet_enemy_list.remove(b)
    

爆炸效果

  • 属性:图片,位置,生存状态

  • 方法:贴图

    #在主逻辑类中,新增爆炸效果列表
    bomb_list = []
    class Bomb:
        def __init__(self,tank):
            self.images = [
                pygame.image.load("tank_img/blast0.gif"),
                pygame.image.load("tank_img/blast1.gif"),
                pygame.image.load("tank_img/blast2.gif"),
                pygame.image.load("tank_img/blast3.gif"),
                pygame.image.load("tank_img/blast4.gif")
                # pygame.image.load("tank_img/blast5.gif"),
                # pygame.image.load("tank_img/blast6.gif"),
                # pygame.image.load("tank_img/blast7.gif")
            ]
            #根据索引取爆炸图片
            self.image_index = 0
            self.image = self.images[self.image_index]
            self.rect = tank.rect
            self.live = True
    
        def display_bomb(self):
            """贴爆炸效果图"""
            if self.image_index<len(self.images):
                self.image = self.images[self.image_index]
                MainGame.window.blit(self.image,self.rect)
                self.image_index+=1
            else:
                self.live = False
                self.image_index = 0
    
    #子弹类中,打中敌方坦克创建爆炸对象,加入爆炸效果列表
    def hit_enemy_tank(self):
        """。。。。"""
        #创建爆炸效果对象,添加到爆炸效果列表中
        bomb = Bomb(e_tank)
        MainGame.bomb_list.append(bomb)
        
    #子弹类中,打中我方坦克创建爆炸对象,加入爆炸效果列表    
        def hit_hero_tank(self):
            """...."""
            # 创建爆炸效果对象,添加到爆炸效果列表中
            bomb = Bomb(MainGame.P1)
            MainGame.bomb_list.append(bomb)
    
    • 主逻辑类中,新增加载爆炸效果的方法

          def load_bomb(self):
              """加载爆炸效果的方法"""
              for bomb in MainGame.bomb_list:
                  if bomb.live:
                      bomb.display_bomb()
                  else:
                      MainGame.bomb_list.remove(bomb)
      #在游戏循环中调用  self.load_bomb()                
      

实现障碍物类

  • 属性:图片,位置,血量

  • 方法:贴图

    class Wall:
        def __init__(self,x,y):
            self.image = pygame.image.load("tank_img/walls.gif")
            self.rect = self.image.get_rect()
            self.rect.x = x
            self.rect.y = y
            self.hp = 5
        def display_wall(self):
            MainGame.window.blit(self.image,self.rect)
    
  • 在主逻辑类中新增障碍物列表

    #记录墙壁列表
        wall_list = []
    
  • 主逻辑类中,新增创建障碍物create_wall和加载障碍物load_wall方法

        def create_wall(self):
            """创建墙壁方法 墙壁60*60  窗口900"""
            for i in range(9):
                wall = Wall(i*100,300)
                MainGame.wall_list.append(wall)
    
        def load_wall(self):
            """加载墙壁方法"""
            for wall in MainGame.wall_list:
                if wall.hp>0:
                    wall.display_wall()
                else:
                    MainGame.wall_list.remove(wall)
    #在游戏开始调用:self.create_wall()
    #在游戏循环中调用:self.load_wall()
    
  • 子弹和墙壁碰撞(子弹类中新增与墙壁的碰撞方法)

    #子弹类中新增与墙壁的碰撞方法    
        def hit_wall(self):
            for wall in MainGame.wall_list:
                if pygame.sprite.collide_rect(self, wall):
                    self.live = False
                    #墙壁生命值减少
                    wall.hp -= 1
    
  • 在加载我方子弹方法中,调用子弹撞墙的方法

        def load_bullet_hero(self):
            """加载我方坦克发射的子弹"""
            b.hit_wall() #调用撞墙方法
    
  • 在加载敌方子弹方法中,调用子弹撞墙的方法

    def load_bullet_enemy(self):
    	"""加载敌方子弹"""
    	b.hit_wall() #调用撞墙方法
        
    #此时我方和敌方坦克可以打障碍物
    #此时坦克可以穿墙
    
  • 坦克无法穿墙

    • 在基本坦克类中,新增记录旧坐标的属性oldx,oldy,在移动之前先记录旧坐标

          def move(self):
              #记录旧坐标
              self.oldx = self.rect.x
              self.oldy = self.rect.y
      
    • 在基本坦克类中,新增坐标还原的方法

你可能感兴趣的:(Python基础语法,python)