Game Development 39 min read

Python Pygame Tank Battle Game Implementation

This article presents a complete Python 3 + Pygame implementation of a classic tank battle game, detailing the visual layout, full source code with sprite classes for tanks, bullets, obstacles, and game logic, and explains how to run and customize the game.

Python Programming Learning Circle
Python Programming Learning Circle
Python Programming Learning Circle
Python Pygame Tank Battle Game Implementation

In this tutorial the author, a nostalgic 80s gamer, shares a full Python 3 + Pygame implementation of a "Tank Battle" game, providing around 1000 lines of source code that can be run directly.

Display Effect

The article includes screenshots demonstrating the game's visual appearance, showing the tank sprites, battlefield layout, and in‑game action.

Code

The complete source code defines multiple sprite classes for the game elements, including walls, ice, river, trees, bricks, bullets, player tanks, enemy tanks, the home base, and the overall game controller. It also handles input, collision detection, bullet firing, enemy AI movement, and game state management.

import random
import sys

import pygame

# Screen dimensions
WIDTH = 630
HEIGHT = 630
# Border size
BORDER_LEN = 3
# Font path
FONTPATH = 'resources/font/font.ttf'

class Iron(pygame.sprite.Sprite):
    """Iron wall class"""
    group = pygame.sprite.Group()
    def __init__(self, position):
        super().__init__()
        self.image = pygame.image.load('resources/images/scene/iron.png')
        self.rect = self.image.get_rect()
        self.rect.topleft = position
        self.group.add(self)
    @classmethod
    def show(cls, screen):
        for temp in cls.group:
            screen.blit(temp.image, temp.rect)

class Ice(pygame.sprite.Sprite):
    """Ice class"""
    group = pygame.sprite.Group()
    def __init__(self, position):
        super().__init__()
        image = pygame.Surface((24, 24))
        for i in range(2):
            for j in range(2):
                image.blit(pygame.image.load('resources/images/scene/ice.png'), (12 * i, 12 * j))
        self.image = image
        self.rect = self.image.get_rect()
        self.rect.topleft = position
        self.group.add(self)
    @classmethod
    def show(cls, screen):
        for temp in cls.group:
            screen.blit(temp.image, temp.rect)

class River(pygame.sprite.Sprite):
    """River class"""
    group = pygame.sprite.Group()
    def __init__(self, position):
        super().__init__()
        image = pygame.Surface((24, 24))
        for i in range(2):
            for j in range(2):
                image.blit(pygame.image.load('resources/images/scene/river1.png'), (12 * i, 12 * j))
        self.image = image
        self.rect = self.image.get_rect()
        self.rect.topleft = position
        self.group.add(self)
    @classmethod
    def show(cls, screen):
        for temp in cls.group:
            screen.blit(temp.image, temp.rect)

class Tree(pygame.sprite.Sprite):
    """Tree class"""
    group = pygame.sprite.Group()
    def __init__(self, position):
        super().__init__()
        image = pygame.Surface((24, 24))
        for i in range(2):
            for j in range(2):
                image.blit(pygame.image.load('resources/images/scene/tree.png'), (12 * i, 12 * j))
        self.image = image
        self.rect = self.image.get_rect()
        self.rect.topleft = position
        self.group.add(self)
    @classmethod
    def show(cls, screen):
        for temp in cls.group:
            screen.blit(temp.image, temp.rect)

class Brick(pygame.sprite.Sprite):
    """Brick wall class"""
    group = pygame.sprite.Group()
    def __init__(self, position):
        super().__init__()
        self.image = pygame.image.load('resources/images/scene/brick.png')
        self.rect = self.image.get_rect()
        self.rect.topleft = position
        self.group.add(self)
    @classmethod
    def show(cls, screen):
        for temp in cls.group:
            screen.blit(temp.image, temp.rect)

class Bullet(pygame.sprite.Sprite):
    """Bullet class"""
    group = pygame.sprite.Group()
    group_enemy = pygame.sprite.Group()
    def __init__(self, _type, direction, position):
        super().__init__()
        if direction == "up":
            image_path = "resources/images/bullet/bullet_up.png"
        elif direction == "down":
            image_path = "resources/images/bullet/bullet_down.png"
        elif direction == "left":
            image_path = "resources/images/bullet/bullet_left.png"
        elif direction == "right":
            image_path = "resources/images/bullet/bullet_right.png"
        self.image = pygame.image.load(image_path)
        self.rect = self.image.get_rect()
        self.rect.center = position
        self.direction = direction
        self.speed = 8
        if _type == "player":
            self.group.add(self)
        else:
            self.group_enemy.add(self)
    @classmethod
    def auto_move(cls):
        for temp in cls.group:
            if temp.rect.x < BORDER_LEN or temp.rect.x > WIDTH - BORDER_LEN or temp.rect.y < BORDER_LEN or temp.rect.y > HEIGHT - BORDER_LEN:
                cls.group.remove(temp)
                print("子弹超出边界,移除子弹")
                continue
            if temp.direction == "up":
                temp.rect = temp.rect.move((0, -temp.speed))
            elif temp.direction == "down":
                temp.rect = temp.rect.move((0, temp.speed))
            elif temp.direction == "left":
                temp.rect = temp.rect.move((-temp.speed, 0))
            elif temp.direction == "right":
                temp.rect = temp.rect.move((temp.speed, 0))
        for temp in cls.group_enemy:
            if temp.rect.x < BORDER_LEN or temp.rect.x > WIDTH - BORDER_LEN or temp.rect.y < BORDER_LEN or temp.rect.y > HEIGHT - BORDER_LEN:
                cls.group_enemy.remove(temp)
                print("子弹超出边界,移除子弹")
                continue
            if temp.direction == "up":
                temp.rect = temp.rect.move((0, -temp.speed))
            elif temp.direction == "down":
                temp.rect = temp.rect.move((0, temp.speed))
            elif temp.direction == "left":
                temp.rect = temp.rect.move((-temp.speed, 0))
            elif temp.direction == "right":
                temp.rect = temp.rect.move((temp.speed, 0))
        pygame.sprite.groupcollide(cls.group, Brick.group, True, True)
        pygame.sprite.groupcollide(cls.group_enemy, Brick.group, True, True)
        for bullet in cls.group:
            if pygame.sprite.spritecollide(bullet, Iron.group, False, None):
                cls.group.remove(bullet)
        for bullet in cls.group_enemy:
            if pygame.sprite.spritecollide(bullet, Iron.group, False, None):
                cls.group_enemy.remove(bullet)
    @classmethod
    def show(cls, screen):
        for temp in cls.group:
            screen.blit(temp.image, temp.rect)
        for temp in cls.group_enemy:
            screen.blit(temp.image, temp.rect)
    @classmethod
    def move_and_show(cls, screen):
        cls.auto_move()
        cls.show(screen)

class PlayerTank(pygame.sprite.Sprite):
    """Player tank class"""
    player_group = []
    group = pygame.sprite.Group()
    def __init__(self, player, top_left):
        super().__init__()
        image_path = "resources/images/playerTank/tank_T1_0.png" if player == "player1" else "resources/images/playerTank/tank_T2_0.png"
        self.tank_all_image = pygame.image.load(image_path).convert_alpha()
        self.image = self.tank_all_image.subsurface((0, 0), (48, 48))
        self.rect = self.image.get_rect()
        self.rect.topleft = top_left
        self.origin_position = top_left
        self.step_length = 8
        self.direction = "up"
        self.move_cache_time = 4
        self.move_cache_count = 0
        self.switch_count = 0
        self.switch_time = 2
        self.switch_image_index = False
        self.image_postion_index = 0
        self.is_bullet_cooling = False
        self.bullet_cooling_count = 0
        self.bullet_cooling_time = 30
        self.life_num = 3
        self.is_show_flag = True
        self.__class__.player_group.append(self)
        self.group.add(self)
    def update_direction(self):
        if self.direction == 'up':
            self.image = self.tank_all_image.subsurface((0, 0), (48, 48))
            self.image_postion_index = 0
        elif self.direction == 'down':
            self.image = self.tank_all_image.subsurface((0, 48), (48, 48))
            self.image_postion_index = 48
        elif self.direction == 'left':
            self.image = self.tank_all_image.subsurface((0, 96), (48, 48))
            self.image_postion_index = 96
        elif self.direction == 'right':
            self.image = self.tank_all_image.subsurface((0, 144), (48, 48))
            self.image_postion_index = 144
    def move(self, direction, group_list):
        """Move tank based on direction and handle collisions"""
        if self.direction != direction:
            self.direction = direction
            self.update_direction()
            return
        self.move_cache_count += 1
        if self.move_cache_count < self.move_cache_time:
            return
        else:
            self.move_cache_count = 0
        rect_ori = self.rect
        if direction == "up":
            self.rect = self.rect.move((0, -self.step_length))
        elif direction == "down":
            self.rect = self.rect.move((0, self.step_length))
        elif direction == "left":
            self.rect = self.rect.move((-self.step_length, 0))
        elif direction == "right":
            self.rect = self.rect.move((self.step_length, 0))
        for group in group_list:
            if pygame.sprite.spritecollide(self, group, False, None):
                self.rect = rect_ori
        if self.rect.top < BORDER_LEN:
            self.rect.top = BORDER_LEN
        elif self.rect.bottom > HEIGHT - BORDER_LEN:
            self.rect.bottom = HEIGHT - BORDER_LEN
        elif self.rect.left < BORDER_LEN:
            self.rect.left = BORDER_LEN
        elif self.rect.right > WIDTH - BORDER_LEN:
            self.rect.right = WIDTH - BORDER_LEN
        self.switch_count += 1
        if self.switch_count > self.switch_time:
            self.switch_count = 0
            self.switch_image_index = not self.switch_image_index
            self.image = self.tank_all_image.subsurface((48 * int(self.switch_image_index), self.image_postion_index), (48, 48))
    def fire(self):
        """Fire a bullet if not cooling"""
        if not self.is_bullet_cooling:
            if self.direction == "up":
                position = (self.rect.centerx, self.rect.y)
            elif self.direction == "down":
                position = (self.rect.centerx, self.rect.y + 48)
            elif self.direction == "left":
                position = (self.rect.x, self.rect.centery)
            elif self.direction == "right":
                position = (self.rect.x + 48, self.rect.centery)
            Bullet("player", self.direction, position)
            print("我方坦克发射子弹")
    @classmethod
    def move_player_tank(cls, is_dual_mode, group_list):
        """Handle keyboard input to move player tanks"""
        key_pressed = pygame.key.get_pressed()
        if key_pressed[pygame.K_w]:
            cls.player_group[0].move("up", group_list)
        elif key_pressed[pygame.K_s]:
            cls.player_group[0].move("down", group_list)
        elif key_pressed[pygame.K_a]:
            cls.player_group[0].move("left", group_list)
        elif key_pressed[pygame.K_d]:
            cls.player_group[0].move("right", group_list)
        elif key_pressed[pygame.K_SPACE]:
            cls.player_group[0].fire()
        if is_dual_mode:
            if key_pressed[pygame.K_UP]:
                cls.player_group[1].move("up", group_list)
            elif key_pressed[pygame.K_DOWN]:
                cls.player_group[1].move("down", group_list)
            elif key_pressed[pygame.K_LEFT]:
                cls.player_group[1].move("left", group_list)
            elif key_pressed[pygame.K_RIGHT]:
                cls.player_group[1].move("right", group_list)
            elif key_pressed[pygame.K_KP0]:
                cls.player_group[1].fire()
    def bullet_cooling(self):
        """Manage bullet cooldown timer"""
        self.bullet_cooling_count += 1
        if self.bullet_cooling_count > self.bullet_cooling_time:
            self.is_bullet_cooling = False
            self.bullet_cooling_count = 0
            print("冷却完毕...")
        else:
            self.is_bullet_cooling = True
    def judge_bomb(self):
        """Check if hit by enemy bullet"""
        if pygame.sprite.spritecollide(self, Bullet.group_enemy, True, None):
            self.life_num -= 1
            if self.life_num == 0:
                self.is_show_flag = False
            self.rect.topleft = self.origin_position
    @classmethod
    def show(cls, screen, is_dual_mode):
        """Render player tanks"""
        if cls.player_group:
            if cls.player_group[0].is_show_flag:
                screen.blit(cls.player_group[0].image, cls.player_group[0].rect)
                cls.player_group[0].bullet_cooling()
                cls.player_group[0].judge_bomb()
            if is_dual_mode and cls.player_group[1].is_show_flag:
                screen.blit(cls.player_group[1].image, cls.player_group[1].rect)
                cls.player_group[1].bullet_cooling()
                cls.player_group[1].judge_bomb()

class PlayerHome(pygame.sprite.Sprite):
    """Player home base"""
    home = None
    def __init__(self, position):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load('resources/images/home/home1.png')
        self.rect = self.image.get_rect()
        self.rect.left, self.rect.top = position
        self.__class__.home = self
    @classmethod
    def show(cls, screen):
        screen.blit(cls.home.image, cls.home.rect)

class EnemyTank(pygame.sprite.Sprite):
    """Enemy tank class"""
    group = pygame.sprite.Group()
    def __init__(self, position):
        super().__init__()
        self.direction = random.choice(["up", "down", "left", "right"])
        self.tank_all_image = pygame.image.load('resources/images/enemyTank/enemy_1_0.png')
        self.image = None
        self.update_direction()
        self.rect = self.image.get_rect()
        self.rect.topleft = position
        self.speed = random.choice([2, 4])
        self.is_bullet_cooling = True
        self.bullet_cooling_count = 0
        self.bullet_cooling_time = 5
        self.group.add(self)
    def update_direction(self):
        if self.direction == 'up':
            self.image = self.tank_all_image.subsurface((0, 0), (48, 48))
        elif self.direction == 'down':
            self.image = self.tank_all_image.subsurface((0, 48), (48, 48))
        elif self.direction == 'left':
            self.image = self.tank_all_image.subsurface((0, 96), (48, 48))
        elif self.direction == 'right':
            self.image = self.tank_all_image.subsurface((0, 144), (48, 48))
    @classmethod
    def show(cls, screen):
        for temp in cls.group:
            screen.blit(temp.image, temp.rect)
    def move(self, group_list):
        """Automatic enemy movement with collision handling"""
        rect_ori = self.rect
        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))
        for group in group_list:
            if pygame.sprite.spritecollide(self, group, False, None):
                self.rect = rect_ori
                self.direction = random.choice(["up", "down", "left", "right"])
                self.update_direction()
        self.group.remove(self)
        if pygame.sprite.spritecollide(self, self.group, False, None):
            self.rect = rect_ori
            self.direction = random.choice(["up", "down", "left", "right"])
            self.update_direction()
        self.group.add(self)
        if pygame.sprite.spritecollide(self, PlayerTank.group, False, None):
            self.rect = rect_ori
            self.direction = random.choice(["up", "down", "left", "right"])
            self.update_direction()
        if pygame.sprite.spritecollide(self, Bullet.group, True, None):
            self.group.remove(self)
        if self.rect.top < BORDER_LEN:
            self.rect.top = BORDER_LEN
            self.direction = random.choice(["up", "down", "left", "right"])
            self.update_direction()
        elif self.rect.bottom > HEIGHT - BORDER_LEN:
            self.rect.bottom = HEIGHT - BORDER_LEN
            self.direction = random.choice(["up", "down", "left", "right"])
            self.update_direction()
        elif self.rect.left < BORDER_LEN:
            self.rect.left = BORDER_LEN
            self.direction = random.choice(["up", "down", "left", "right"])
            self.update_direction()
        elif self.rect.right > WIDTH - BORDER_LEN:
            self.rect.right = WIDTH - BORDER_LEN
            self.direction = random.choice(["up", "down", "left", "right"])
            self.update_direction()
    @classmethod
    def auto_move(cls, group_list):
        for temp in cls.group:
            temp.move(group_list)
    @classmethod
    def auto_move_and_show(cls, screen, group_list):
        cls.auto_move(group_list)
        cls.show(screen)
    def judge_cooling_and_fire(self):
        """Fire bullet after cooldown"""
        self.bullet_cooling_count += 1
        if self.bullet_cooling_count > self.bullet_cooling_time:
            self.bullet_cooling_count = 0
            if random.randint(1, 10) == 6:
                self.is_bullet_cooling = False
        if not self.is_bullet_cooling:
            if self.direction == "up":
                position = (self.rect.centerx, self.rect.y)
            elif self.direction == "down":
                position = (self.rect.centerx, self.rect.y + 48)
            elif self.direction == "left":
                position = (self.rect.x, self.rect.centery)
            elif self.direction == "right":
                position = (self.rect.x + 48, self.rect.centery)
            Bullet("enemy", self.direction, position)
            self.is_bullet_cooling = True
    @classmethod
    def fire(cls):
        """Make all enemy tanks attempt to fire"""
        for temp in cls.group:
            temp.judge_cooling_and_fire()

class Game(object):
    """Main game controller"""
    def __init__(self):
        """Initialize pygame and screen"""
        pygame.init()
        self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
    def game_start_interface(self):
        """Show start screen for player count selection"""
        background_img = pygame.image.load('resources/images/others/background.png')
        logo_img = pygame.image.load('resources/images/others/logo.png')
        logo_img = pygame.transform.scale(logo_img, (446, 70))
        logo_rect = logo_img.get_rect()
        logo_rect.centerx, logo_rect.centery = WIDTH / 2, HEIGHT // 4
        font = pygame.font.Font(FONTPATH, 60)
        font_color_white = (255, 255, 255)
        one_player_text = font.render('1 PLAYER', True, font_color_white)
        one_player_rect = one_player_text.get_rect()
        one_player_rect.left, one_player_rect.top = WIDTH / 2 - 50, HEIGHT / 2 - 60
        two_players_text = font.render('2 PLAYERS', True, font_color_white)
        two_players_rect = two_players_text.get_rect()
        two_players_rect.left, two_players_rect.top = WIDTH / 2 - 50, HEIGHT / 2
        select_player_num_tank = pygame.image.load('resources/images/playerTank/tank_T1_0.png').convert_alpha().subsurface((0, 144), (48, 48))
        select_player_num_tank_rect = select_player_num_tank.get_rect()
        game_tip = font.render('press <Enter> to start', True, font_color_white)
        game_tip_rect = game_tip.get_rect()
        game_tip_rect.centerx, game_tip_rect.top = WIDTH / 2, HEIGHT / 1.4
        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 in (pygame.K_UP, pygame.K_DOWN, pygame.K_w, pygame.K_s):
                        is_dual_mode = not is_dual_mode
            self.screen.blit(background_img, (0, 0))
            self.screen.blit(logo_img, logo_rect)
            self.screen.blit(one_player_text, one_player_rect)
            self.screen.blit(two_players_text, two_players_rect)
            if is_dual_mode:
                select_player_num_tank_rect.right, select_player_num_tank_rect.top = two_players_rect.left - 10, two_players_rect.top
            else:
                select_player_num_tank_rect.right, select_player_num_tank_rect.top = one_player_rect.left - 10, one_player_rect.top
            self.screen.blit(select_player_num_tank, select_player_num_tank_rect)
            self.screen.blit(game_tip, game_tip_rect)
            pygame.display.update()
            clock.tick(60)
    def game_end_interface(self, is_win):
        """Show win/lose screen and handle restart/quit"""
        background_img = pygame.image.load('resources/images/others/background.png')
        game_over_img = pygame.image.load('resources/images/others/gameover.png')
        game_over_img = pygame.transform.scale(game_over_img, (150, 75))
        game_over_img_rect = game_over_img.get_rect()
        game_over_img_rect.midtop = WIDTH / 2, HEIGHT / 8
        color_white = (255, 255, 255)
        font = pygame.font.Font(FONTPATH, 60)
        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 = WIDTH / 2, HEIGHT / 3
        tank_cursor = pygame.image.load('resources/images/playerTank/tank_T1_0.png').convert_alpha().subsurface((0, 144), (48, 48))
        tank_rect = tank_cursor.get_rect()
        restart_render_white = font.render('RESTART', True, color_white)
        restart_rect = restart_render_white.get_rect()
        restart_rect.left, restart_rect.top = WIDTH / 2.4, HEIGHT / 2
        quit_render_white = font.render('QUIT', True, color_white)
        quit_rect = quit_render_white.get_rect()
        quit_rect.left, quit_rect.top = WIDTH / 2.4, 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 in (pygame.K_UP, pygame.K_DOWN, pygame.K_w, pygame.K_s):
                        is_quit_game = not is_quit_game
            self.screen.blit(background_img, (0, 0))
            self.screen.blit(game_over_img, game_over_img_rect)
            self.screen.blit(font_render, font_rect)
            if not is_quit_game:
                tank_rect.right, tank_rect.top = restart_rect.left - 10, restart_rect.top
                self.screen.blit(tank_cursor, tank_rect)
                self.screen.blit(quit_render_white, quit_rect)
                self.screen.blit(restart_render_white, restart_rect)
            else:
                tank_rect.right, tank_rect.top = quit_rect.left - 10, quit_rect.top
                self.screen.blit(tank_cursor, tank_rect)
                self.screen.blit(quit_render_white, quit_rect)
                self.screen.blit(restart_render_white, restart_rect)
            pygame.display.update()
            clock.tick(60)
    def parse_game_level_file(self):
        """Parse level configuration and create game objects"""
        grid_size = 24
        with open("./levels/3.lvl", errors='ignore') as f:
            num_row = -1
            for line in f.readlines():
                line = line.strip('\n')
                if line[0] in ["S", "B", "I", "R", "C", "T"]:
                    num_row += 1
                    for num_col, elem in enumerate(line.split(' ')):
                        position = BORDER_LEN + num_col * grid_size, BORDER_LEN + num_row * grid_size
                        if elem == 'B':
                            Brick(position)
                        elif elem == 'I':
                            Iron(position)
                        elif elem == 'R':
                            River(position)
                        elif elem == 'C':
                            Ice(position)
                        elif elem == 'T':
                            Tree(position)
                elif line.startswith('%HOMEPOS'):
                    home_position = line.split(':')[-1]
                    home_position = int(home_position.split(',')[0]), int(home_position.split(',')[1])
                    home_position = (BORDER_LEN + home_position[0] * grid_size, BORDER_LEN + home_position[1] * grid_size)
                    PlayerHome(home_position)
                elif line.startswith('%PLAYERTANKPOS'):
                    player_tank_positions = line.split(':')[-1]
                    player_tank_positions = [(int(pos.split(',')[0]), int(pos.split(',')[1])) for pos in player_tank_positions.split(' ')]
                    player_tank_positions = [[BORDER_LEN + pos[0] * grid_size, BORDER_LEN + pos[1] * grid_size] for pos in player_tank_positions]
                    PlayerTank('player1', player_tank_positions[0])
                    PlayerTank('player2', player_tank_positions[1])
                elif line.startswith('%ENEMYTANKPOS'):
                    position = line.split(':')[-1]
                    position = [[int(pos.split(',')[0]), int(pos.split(',')[1])] for pos in position.split(' ')]
                    position = [(BORDER_LEN + pos[0] * grid_size, BORDER_LEN + pos[1] * grid_size) for pos in position]
                    for pos in position:
                        EnemyTank(pos)
    def game_run_level(self, is_dual_mode):
        """Run the main game loop for a level"""
        background_img = pygame.image.load('resources/images/others/background.png')
        self.parse_game_level_file()
        clock = pygame.time.Clock()
        is_win = False
        is_running = True
        while is_running:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()
            PlayerTank.move_player_tank(is_dual_mode, [Brick.group, River.group, Ice.group, Iron.group])
            EnemyTank.fire()
            if pygame.sprite.spritecollide(PlayerHome.home, Bullet.group, True, None) or pygame.sprite.spritecollide(PlayerHome.home, Bullet.group_enemy, True, None):
                is_running = False
                is_win = False
            if len(EnemyTank.group) == 0:
                is_running = False
                is_win = True
            if (not is_dual_mode and PlayerTank.player_group[0].life_num == 0) or (is_dual_mode and PlayerTank.player_group[0].life_num == 0 and PlayerTank.player_group[1].life_num == 0):
                is_running = False
                is_win = False
            self.screen.blit(background_img, (0, 0))
            Brick.show(self.screen)
            Iron.show(self.screen)
            River.show(self.screen)
            Ice.show(self.screen)
            Tree.show(self.screen)
            PlayerHome.show(self.screen)
            PlayerTank.show(self.screen, is_dual_mode)
            Bullet.move_and_show(self.screen)
            EnemyTank.auto_move_and_show(self.screen, [Brick.group, River.group, Ice.group, Iron.group])
            pygame.display.update()
            clock.tick(60)
        return is_win
    def clean(self):
        """Clear all sprite groups for a fresh start"""
        EnemyTank.group.empty()
        PlayerTank.group.empty()
        PlayerTank.player_group.clear()
        Brick.group.empty()
        Ice.group.empty()
        River.group.empty()
        Iron.group.empty()
        Tree.group.empty()
        Bullet.group.empty()
        Bullet.group_enemy.empty()
    def run(self):
        """Main entry point to start the game"""
        is_dual_mode = self.game_start_interface()
        is_win = self.game_run_level(is_dual_mode)
        while True:
            if self.game_end_interface(is_win):
                break
            self.clean()
            pygame.display.set_mode((WIDTH, HEIGHT))
            is_dual_mode = self.game_start_interface()
            is_win = self.game_run_level(is_dual_mode)

if __name__ == '__main__':
    """Start the game"""
    game = Game()
    game.run()
PythonGame developmentcollision detectionPygameSpriteTank Battle
Python Programming Learning Circle
Written by

Python Programming Learning Circle

A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.