专栏首页AI科技大本营的专栏用Python实现坦克大战游戏 | 干货贴

用Python实现坦克大战游戏 | 干货贴

作者 | 李秋键

出品 | AI科技大本营(rgznai100)

《坦克大战》是1985年日本南梦宫Namco游戏公司在任天堂FC平台上,推出的一款多方位平面射击游戏。游戏以坦克战斗及保卫基地为主题,属于策略型联机类。同时也是FC平台上少有的内建关卡编辑器的几个游戏之一,玩家可自己创建独特的关卡,并通过获取一些道具使坦克和基地得到强化。而今天我们就将利用python还原以下坦克大战的制作。

实验前的准备

首先我们使用的Python版本是3.6.5所用到的模块如下:

  • Pygame模块用来创建游戏整体框架、精灵等基本架构;
  • OS模块用来加载本地文件(包括音乐,背景、图片等素材)。

精灵类程序

其中精灵类设置作为基本程序框架用来主函数的调用,其中包括子弹类程序、食物类、家类、砖墙树木等障碍物类、坦克类。具体程序布局如下:

其中子弹类程序,首先需要建立bullet.py程序,建立类包括子弹位置、方向、图片加载、子弹速度等基本信息。具体代码如下:

'''子弹'''
class Bullet(pygame.sprite.Sprite):
  def __init__(self, bullet_image_paths, screensize, direction, position, border_len, is_stronger=False, speed=8, **kwargs):
    pygame.sprite.Sprite.__init__(self)
    self.bullet_image_paths = bullet_image_paths
    self.width, self.height = screensize
    self.direction = direction
    self.position = position
    self.image = pygame.image.load(self.bullet_image_paths.get(direction))
    self.rect = self.image.get_rect()
    self.rect.center = position
    # 地图边缘宽度
    self.border_len = border_len
    # 是否为加强版子弹(加强版可碎铁墙)
    self.is_stronger = is_stronger
    # 子弹速度
    self.speed = speed
  '''移动子弹, 若子弹越界, 则返回True, 否则为False'''
  def move(self):
    if self.direction == 'up':
      self.rect = self.rect.move(0, -self.speed)
    elif self.direction == 'down':
      self.rect = self.rect.move(0, self.speed)
    elif self.direction == 'left':
      self.rect = self.rect.move(-self.speed, 0)
    elif self.direction == 'right':
      self.rect = self.rect.move(self.speed, 0)
    if (self.rect.top < self.border_len) or (self.rect.bottom > self.height) or (self.rect.left < self.border_len) or (self.rect.right > self.width):
      return True
    return False

食物奖励类,建立food.py作为坦克吃到食物时增加生命等基本奖励:

'''食物类. 用于获得奖励'''
class Foods(pygame.sprite.Sprite):
  def __init__(self, food_image_paths, screensize, **kwargs):
    pygame.sprite.Sprite.__init__(self)
    self.name = random.choice(list(food_image_paths.keys()))
    self.image = pygame.image.load(food_image_paths.get(self.name))
    self.rect = self.image.get_rect()
    self.rect.left, self.rect.top = random.randint(100, screensize[0]-100), random.randint(100, screensize[1]-100)
    self.exist_time = 1000
  def update(self):
    self.exist_time -= 1
    return True if self.exist_time < 0 else False

坦克家类,建立home.py存储家基本信息(包括是否存活、图片加载、位置尺寸等)。

'''大本营类'''
class Home(pygame.sprite.Sprite):
  def __init__(self, position, imagepaths, **kwargs):
    pygame.sprite.Sprite.__init__(self)
    self.imagepaths = imagepaths
    self.image = pygame.image.load(self.imagepaths[0])
    self.rect = self.image.get_rect()
    self.rect.left, self.rect.top = position
    self.alive = True
  '''被摧毁'''
  def setDead(self):
    self.image = pygame.image.load(self.imagepaths[1])
    self.alive = False
  '''画到屏幕上'''
  def draw(self, screen):
    screen.blit(self.image, self.rect)

砖墙等障碍物类,建立scenes.py其中也是主要位置尺寸的布局:

'''砖墙'''
class Brick(pygame.sprite.Sprite):
  def __init__(self, position, imagepath, **kwargs):
    pygame.sprite.Sprite.__init__(self)
    self.image = pygame.image.load(imagepath)
    self.rect = self.image.get_rect()
    self.rect.left, self.rect.top = position
'''铁墙'''
class Iron(pygame.sprite.Sprite):
  def __init__(self, position, imagepath, **kwargs):
    pygame.sprite.Sprite.__init__(self)
    self.image = pygame.image.load(imagepath)
    self.rect = self.image.get_rect()
    self.rect.left, self.rect.top = position
'''冰'''
class Ice(pygame.sprite.Sprite):
  def __init__(self, position, imagepath, **kwargs):
    pygame.sprite.Sprite.__init__(self)
    self.image = pygame.Surface((24, 24))
    for i in range(2):
      for j in range(2):
        self.image.blit(pygame.image.load(imagepath), (12*i, 12*j))
    self.rect = self.image.get_rect()
    self.rect.left, self.rect.top = position
'''河流'''
class River(pygame.sprite.Sprite):
  def __init__(self, position, imagepath, **kwargs):
    pygame.sprite.Sprite.__init__(self)
    self.image = pygame.Surface((24, 24))
    for i in range(2):
      for j in range(2):
        self.image.blit(pygame.image.load(imagepath), (12*i, 12*j))
    self.rect = self.image.get_rect()
    self.rect.left, self.rect.top = position
'''树'''
class Tree(pygame.sprite.Sprite):
  def __init__(self, position, imagepath, **kwargs):
    pygame.sprite.Sprite.__init__(self)
    self.image = pygame.Surface((24, 24))
    for i in range(2):
      for j in range(2):
        self.image.blit(pygame.image.load(imagepath), (12*i, 12*j))
    self.rect = self.image.get_rect()
    self.rect.left, self.rect.top = position

坦克类,建立tanks.py包括坦克数量名称、初始位置等信息:

'''玩家坦克类'''
class PlayerTank(pygame.sprite.Sprite):
  def __init__(self, name, player_tank_image_paths, position, border_len, screensize, direction='up', bullet_image_paths=None, protected_mask_path=None, boom_image_path=None, **kwargs):
    pygame.sprite.Sprite.__init__(self)
    # 玩家1/玩家2
    self.name = name
    # 坦克图片路径
    self.player_tank_image_paths = player_tank_image_paths.get(name)
    # 地图边缘宽度
    self.border_len = border_len
    # 屏幕大小
    self.screensize = screensize
    # 初始坦克方向
    self.init_direction = direction
    # 初始位置
    self.init_position = position
    # 子弹图片
    self.bullet_image_paths = bullet_image_paths
    # 保护罩图片路径
    self.protected_mask = pygame.image.load(protected_mask_path)
    self.protected_mask_flash_time = 25
    self.protected_mask_flash_count = 0
    self.protected_mask_pointer = False
    # 坦克爆炸图
    self.boom_image = pygame.image.load(boom_image_path)
    self.boom_last_time = 5
    self.booming_flag = False
    self.boom_count = 0
    # 坦克生命数量
    self.num_lifes = 3
    # 重置
    self.reset()
  '''移动'''
  def move(self, direction, scene_elems, player_tanks_group, enemy_tanks_group, home):
    # 爆炸时无法移动
    if self.booming_flag:
      return
    # 方向不一致先改变方向
    if self.direction != direction:
      self.setDirection(direction)
      self.switch_count = self.switch_time
      self.move_cache_count = self.move_cache_time
    # 移动(使用缓冲)
    self.move_cache_count += 1
    if self.move_cache_count < self.move_cache_time:
      return
    self.move_cache_count = 0
    if self.direction == 'up':
      speed = (0, -self.speed)
    elif self.direction == 'down':
      speed = (0, self.speed)
    elif self.direction == 'left':
      speed = (-self.speed, 0)
    elif self.direction == 'right':
      speed = (self.speed, 0)
    rect_ori = self.rect
    self.rect = self.rect.move(speed)
    # --碰到场景元素
    for key, value in scene_elems.items():
      if key in ['brick_group', 'iron_group', 'river_group']:
        if pygame.sprite.spritecollide(self, value, False, None):
          self.rect = rect_ori
      elif key in ['ice_group']:
        if pygame.sprite.spritecollide(self, value, False, None):
          self.rect = self.rect.move(speed)
    # --碰到其他玩家坦克
    if pygame.sprite.spritecollide(self, player_tanks_group, False, None):
      self.rect = rect_ori
    # --碰到敌方坦克
    if pygame.sprite.spritecollide(self, enemy_tanks_group, False, None):
      self.rect = rect_ori
    # --碰到玩家大本营
    if pygame.sprite.collide_rect(self, home):
      self.rect = rect_ori
    # --碰到边界
    if self.rect.left < self.border_len:
      self.rect.left = self.border_len
    elif self.rect.right > self.screensize[0]-self.border_len:
      self.rect.right = self.screensize[0] - self.border_len
    elif self.rect.top < self.border_len:
      self.rect.top = self.border_len
    elif self.rect.bottom > self.screensize[1]-self.border_len:
      self.rect.bottom = self.screensize[1] - self.border_len
    # 为了坦克轮动特效切换图片
    self.switch_count += 1
    if self.switch_count > self.switch_time:
      self.switch_count = 0
      self.switch_pointer = not self.switch_pointer
      self.image = self.tank_direction_image.subsurface((48*int(self.switch_pointer), 0), (48, 48))

游戏界面设置

游戏界面设置包括:开始界面设置、结束界面设置和关卡切换界面设置:

其中游戏开始界面包括玩家数的选择和图片音乐的加载:

'''游戏开始界面'''
def gameStartInterface(screen, cfg):
  background_img = pygame.image.load(cfg.OTHER_IMAGE_PATHS.get('background'))
  color_white = (255, 255, 255)
  color_red = (255, 0, 0)
  font = pygame.font.Font(cfg.FONTPATH, cfg.WIDTH//12)
  logo_img = pygame.image.load(cfg.OTHER_IMAGE_PATHS.get('logo'))
  logo_img = pygame.transform.scale(logo_img, (446, 70))
  logo_rect = logo_img.get_rect()
  logo_rect.centerx, logo_rect.centery = cfg.WIDTH/2, cfg.HEIGHT//4
  tank_cursor = pygame.image.load(cfg.PLAYER_TANK_IMAGE_PATHS.get('player1')[0]).convert_alpha().subsurface((0, 144), (48, 48))
  tank_rect = tank_cursor.get_rect()
  # 玩家数量选择
  player_render_white = font.render('1 PLAYER', True, color_white)
  player_render_red = font.render('1 PLAYER', True, color_red)
  player_rect = player_render_white.get_rect()
  player_rect.left, player_rect.top = cfg.WIDTH/2.8, cfg.HEIGHT/2.5
  players_render_white = font.render('2 PLAYERS', True, color_white)
  players_render_red = font.render('2 PLAYERS', True, color_red)
  players_rect = players_render_white.get_rect()
  players_rect.left, players_rect.top = cfg.WIDTH/2.8, cfg.HEIGHT/2
  # 游戏提示
  game_tip = font.render('press <Enter> to start', True, color_white)
  game_tip_rect = game_tip.get_rect()
  game_tip_rect.centerx, game_tip_rect.top = cfg.WIDTH/2, cfg.HEIGHT/1.4
  game_tip_flash_time = 25
  game_tip_flash_count = 0
  game_tip_show_flag = True
  # 主循环
  clock = pygame.time.Clock()
  is_dual_mode = False
  while True:
    for event in pygame.event.get():
      if event.type == pygame.QUIT:
        pygame.quit()
        sys.exit()
      elif event.type == pygame.KEYDOWN:
        if event.key == pygame.K_RETURN:
          return is_dual_mode
        elif event.key == pygame.K_UP or event.key == pygame.K_DOWN or event.key == pygame.K_w or event.key == pygame.K_s:
          is_dual_mode = not is_dual_mode
    screen.blit(background_img, (0, 0))
    screen.blit(logo_img, logo_rect)
    game_tip_flash_count += 1
    if game_tip_flash_count > game_tip_flash_time:
      game_tip_show_flag = not game_tip_show_flag
      game_tip_flash_count = 0
    if game_tip_show_flag:
      screen.blit(game_tip, game_tip_rect)
    if not is_dual_mode:
      tank_rect.right, tank_rect.top = player_rect.left-10, player_rect.top
      screen.blit(tank_cursor, tank_rect)
      screen.blit(player_render_red, player_rect)
      screen.blit(players_render_white, players_rect)
    else:
      tank_rect.right, tank_rect.top = players_rect.left-10, players_rect.top
      screen.blit(tank_cursor, tank_rect)
      screen.blit(player_render_white, player_rect)
      screen.blit(players_render_red, players_rect)
    pygame.display.update()
    clock.tick(60)

游戏结束界面包括游戏胜利与失败情况判断和是否退出游戏或重新开始的设置:

'''游戏结束界面'''
def gameEndIterface(screen, cfg, is_win=True):
  background_img = pygame.image.load(cfg.OTHER_IMAGE_PATHS.get('background'))
  color_white = (255, 255, 255)
  color_red = (255, 0, 0)
  font = pygame.font.Font(cfg.FONTPATH, cfg.WIDTH//12)
  # 游戏失败图
  gameover_img = pygame.image.load(cfg.OTHER_IMAGE_PATHS.get('gameover'))
  gameover_img = pygame.transform.scale(gameover_img, (150, 75))
  gameover_img_rect = gameover_img.get_rect()
  gameover_img_rect.midtop = cfg.WIDTH/2, cfg.HEIGHT/8
  gameover_flash_time = 25
  gameover_flash_count = 0
  gameover_show_flag = True
  # 游戏胜利与否的提示
  if is_win:
    font_render = font.render('Congratulations, You win!', True, color_white)
  else:
    font_render = font.render('Sorry, You fail!', True, color_white)
  font_rect = font_render.get_rect()
  font_rect.centerx, font_rect.centery = cfg.WIDTH/2, cfg.HEIGHT/3
  # 用于选择退出或重新开始
  tank_cursor = pygame.image.load(cfg.PLAYER_TANK_IMAGE_PATHS.get('player1')[0]).convert_alpha().subsurface((0, 144), (48, 48))
  tank_rect = tank_cursor.get_rect()
  restart_render_white = font.render('RESTART', True, color_white)
  restart_render_red = font.render('RESTART', True, color_red)
  restart_rect = restart_render_white.get_rect()
  restart_rect.left, restart_rect.top = cfg.WIDTH/2.4, cfg.HEIGHT/2
  quit_render_white = font.render('QUIT', True, color_white)
  quit_render_red = font.render('QUIT', True, color_red)
  quit_rect = quit_render_white.get_rect()
  quit_rect.left, quit_rect.top = cfg.WIDTH/2.4, cfg.HEIGHT/1.6
  is_quit_game = False
  # 主循环
  clock = pygame.time.Clock()
  while True:
    for event in pygame.event.get():
      if event.type == pygame.QUIT:
        pygame.quit()
        sys.exit()
      elif event.type == pygame.KEYDOWN:
        if event.key == pygame.K_RETURN:
          return is_quit_game
        elif event.key == pygame.K_UP or event.key == pygame.K_DOWN or event.key == pygame.K_w or event.key == pygame.K_s:
          is_quit_game = not is_quit_game
    screen.blit(background_img, (0, 0))
    gameover_flash_count += 1
    if gameover_flash_count > gameover_flash_time:
      gameover_show_flag = not gameover_show_flag
      gameover_flash_count = 0
    if gameover_show_flag:
      screen.blit(gameover_img, gameover_img_rect)
    screen.blit(font_render, font_rect)
    if not is_quit_game:
      tank_rect.right, tank_rect.top = restart_rect.left-10, restart_rect.top
      screen.blit(tank_cursor, tank_rect)
      screen.blit(restart_render_red, restart_rect)
      screen.blit(quit_render_white, quit_rect)
    else:
      tank_rect.right, tank_rect.top = quit_rect.left-10, quit_rect.top
      screen.blit(tank_cursor, tank_rect)
      screen.blit(restart_render_white, restart_rect)
      screen.blit(quit_render_red, quit_rect)
    pygame.display.update()
    clock.tick(60)

游戏界面切换主要是利用进度条加载:

'''关卡切换界面'''
def switchLevelIterface(screen, cfg, level_next=1):
  background_img = pygame.image.load(cfg.OTHER_IMAGE_PATHS.get('background'))
  color_white = (255, 255, 255)
  color_gray = (192, 192, 192)
  font = pygame.font.Font(cfg.FONTPATH, cfg.WIDTH//20)
  logo_img = pygame.image.load(cfg.OTHER_IMAGE_PATHS.get('logo'))
  logo_img = pygame.transform.scale(logo_img, (446, 70))
  logo_rect = logo_img.get_rect()
  logo_rect.centerx, logo_rect.centery = cfg.WIDTH/2, cfg.HEIGHT//4
  # 游戏加载提示
  font_render = font.render('Loading game data, You will enter Level-%s' % level_next, True, color_white)
  font_rect = font_render.get_rect()
  font_rect.centerx, font_rect.centery = cfg.WIDTH/2, cfg.HEIGHT/2
  # 游戏加载进度条
  gamebar = pygame.image.load(cfg.OTHER_IMAGE_PATHS.get('gamebar')).convert_alpha()
  gamebar_rect = gamebar.get_rect()
  gamebar_rect.centerx, gamebar_rect.centery = cfg.WIDTH/2, cfg.HEIGHT/1.4
  tank_cursor = pygame.image.load(cfg.PLAYER_TANK_IMAGE_PATHS.get('player1')[0]).convert_alpha().subsurface((0, 144), (48, 48))
  tank_rect = tank_cursor.get_rect()
  tank_rect.left = gamebar_rect.left
  tank_rect.centery = gamebar_rect.centery
  # 加载所需时间
  load_time_left = gamebar_rect.right - tank_rect.right + 8
  # 主循环
  clock = pygame.time.Clock()
  while True:
    for event in pygame.event.get():
      if event.type == pygame.QUIT:
        pygame.quit()
        sys.exit()
    if load_time_left <= 0:
      return
    screen.blit(background_img, (0, 0))
    screen.blit(logo_img, logo_rect)
    screen.blit(font_render, font_rect)
    screen.blit(gamebar, gamebar_rect)
    screen.blit(tank_cursor, tank_rect)
    pygame.draw.rect(screen, color_gray, (gamebar_rect.left+8, gamebar_rect.top+8, tank_rect.left-gamebar_rect.left-8, tank_rect.bottom-gamebar_rect.top-16))
    tank_rect.left += 1
    load_time_left -= 1
    pygame.display.update()
    clock.tick(60)

完整代码:

https://pan.baidu.com/s/1BUh9M73AAGkZeDN0IEKdKA

提取码:09bl

作者:李秋键

CSDN博客专家,CSDN达人课作者。硕士在读于中国矿业大学,开发有taptap竞赛获奖等。

本文分享自微信公众号 - AI科技大本营(rgznai100),作者:李秋键

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-10-10

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 一站式解决:隐马尔可夫模型(HMM)全过程推导及实现

    。一共有4个箱子,2种球,结合前面的箱子的详细数据,可以得到从每一个箱子取到各种颜色球的可能性,用一个表格表示:

    AI科技大本营
  • Python 还能实现哪些 AI 游戏?附上代码一起来一把!

    人工智能作为当前热门在我们生活中得到了广泛应用,尤其是在智能游戏方面,有的已经达到了可以和职业选手匹敌的效果。而DQN算法作为智能游戏的经典选择算法,其主要是通...

    AI科技大本营
  • Python 还能实现图片去雾?FFA 去雾算法、暗通道去雾算法用起来! | 附代码

    Pytorch模块用来模型训练和网络层建立;其底层和Torch框架一样,但是使用Python重新写了很多内容,不仅更加灵活,支持动态图,而且提供了Python接...

    AI科技大本营
  • python实现飞机大战项目

    网上下载了腾全民飞机大战的贴图,把原来的贴图改头换面了一下,其中的P图修改过程历尽艰辛啊,兼职了美工……

    砸漏
  • 用Python实现坦克大战游戏 | 干货贴

    《坦克大战》是1985年日本南梦宫Namco游戏公司在任天堂FC平台上,推出的一款多方位平面射击游戏。游戏以坦克战斗及保卫基地为主题,属于策略型联机类。同时也是...

    小白学视觉
  • Python 游戏编程之实现飞机大战(含源代码)

    第一次接触打飞机的时候作者本人是身心愉悦的,因为周边的朋友都在打飞机, 每次都会下意识彼此较量一下,看谁打得更好。打飞机也是需要有一定的技巧的,熟练的朋友一把能...

    吾非同
  • pygame系列_箭刺Elephant游戏_源码下载

    http://www.pygame.org/docs/tut/chimp/ChimpLineByLine.html

    Hongten
  • 用Python实现谷歌的小恐龙游戏:p

    让我们来依次定义一下这些游戏元素类。对于云,路面以及仙人掌来说,定义起来很简单,我们只需要加载对应的游戏元素图片:

    Crossin先生
  • Python之pygame学习精灵碰撞做一个躲避球游戏(13)

    获取鼠标返回的坐标,用这个坐标来画圆的时候,设置好不能超出边框,结果圆居然能出去????

    萌海无涯
  • 在Python游戏中模拟重力

    我们的现实生活中充满了运动和生命。物理让我们的世界变得如此繁忙和生动。 同时我们要知道,物理阐释了物质在空间中移动的方式。 不过呢,因为我们的游戏世界本不存在物...

    五月Rambo

扫码关注云+社区

领取腾讯云代金券