import pygame import sys import json import os import random # pip3 install moviepy from moviepy.editor import VideoFileClip import threading pygame.init() # 设置窗口 WIDTH, HEIGHT = 800, 600 screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("坦克大战") # 播放背景音乐 pygame.mixer.music.load('Resources/bg.mp3') pygame.mixer.music.play(-1) # 加载资源 start_ui = pygame.image.load("Resources/startui.png") btn_00 = pygame.image.load("Resources/btn_00.png") btn_01 = pygame.image.load("Resources/btn_01.png") btn_10 = pygame.image.load("Resources/btn_10.png") btn_11 = pygame.image.load("Resources/btn_11.png") player_image = pygame.image.load("Resources/player.png") bullet_images = [ pygame.image.load("Resources/zd_1.png"), pygame.image.load("Resources/zd_2.png"), pygame.image.load("Resources/zd_3.png") ] enemy_images = [ pygame.transform.scale(pygame.image.load('Resources/tank_1.png'), (40, 40)), pygame.transform.scale(pygame.image.load('Resources/tank_2.png'), (40, 40)), pygame.transform.scale(pygame.image.load('Resources/tank_3.png'), (40, 40)) ] # 加载并调整图片大小 image_dict = { 1: pygame.transform.scale(pygame.image.load('Resources/1.png'), (40, 40)), 2: pygame.transform.scale(pygame.image.load('Resources/2.png'), (40, 40)), 3: pygame.transform.scale(pygame.image.load('Resources/3.png'), (40, 40)), 4: pygame.transform.scale(pygame.image.load('Resources/4.png'), (40, 40)), 5: pygame.transform.scale(pygame.image.load('Resources/5.png'), (40, 40)), 6: pygame.transform.scale(pygame.image.load('Resources/6.png'), (40, 40)), 7: pygame.transform.scale(pygame.image.load('Resources/7.png'), (40, 40)), 8: pygame.transform.scale(pygame.image.load('Resources/8.png'), (40, 40)), 9: pygame.transform.scale(pygame.image.load('Resources/9.png'), (40, 40)) } # 读取JSON文件 with open('map/map1.json', 'r') as f: map_data = json.load(f) # 格子大小 tile_size = 40 # 按钮位置 btn1_rect = btn_00.get_rect(center=(WIDTH // 2, HEIGHT // 2 - 0)) btn2_rect = btn_10.get_rect(center=(WIDTH // 2, HEIGHT // 2 + 80)) total_kills = 0 enemy_kills = {1: 0, 2: 0, 3: 0} class Bullet: def __init__(self, image, position, direction, speed=10): self.image = image self.rect = self.image.get_rect(center=position) self.direction = direction self.speed = speed self.angle = 0 self.update_angle() def update(self): if self.direction == 'UP': self.rect.y -= self.speed elif self.direction == 'DOWN': self.rect.y += self.speed elif self.direction == 'LEFT': self.rect.x -= self.speed elif self.direction == 'RIGHT': self.rect.x += self.speed self.update_angle() def update_angle(self): if self.direction == 'UP': self.angle = 0 elif self.direction == 'DOWN': self.angle = 180 elif self.direction == 'LEFT': self.angle = 90 elif self.direction == 'RIGHT': self.angle = -90 self.image = pygame.transform.rotate(bullet_images[0], self.angle) class EnemyTank: def __init__(self, image, position, tank_type): self.image = image self.rect = self.image.get_rect(center=position) self.direction = random.choice(['UP', 'DOWN', 'LEFT', 'RIGHT']) self.speed = 40 * (2 if tank_type == 1 else 1.5 if tank_type == 2 else 1.0) self.move_timer = 0 self.tank_type = tank_type self.bullet_timer = 0 self.health = 1 if tank_type == 1 else 3 if tank_type == 2 else 5 self.dead = False self.explode_frames = [pygame.image.load(f'Resources/effect/explode_{i}.png') for i in range(8)] self.current_explode_frame = 0 self.explode_timer = 0 def can_move(self, new_rect): if self.direction == 'UP': grid_x = new_rect.x // tile_size grid_y = new_rect.y // tile_size - 1 elif self.direction == 'DOWN': grid_x = new_rect.x // tile_size grid_y = new_rect.y // tile_size + 1 elif self.direction == 'LEFT': grid_x = new_rect.x // tile_size - 1 grid_y = new_rect.y // tile_size elif self.direction == 'RIGHT': grid_x = new_rect.x // tile_size + 1 grid_y = new_rect.y // tile_size if new_rect.x < 0 or (new_rect.x >= WIDTH - 40) or new_rect.y < 0 or (new_rect.y >= HEIGHT - 40): return False if map_data['layer_1'][grid_y][grid_x] == 2 or map_data['layer_1'][grid_y][grid_x] == 3 or map_data['layer_1'][grid_y][grid_x] == 4 or map_data['layer_1'][grid_y][grid_x] == 6: return False return True def move(self): new_rect = self.rect.copy() if self.direction == 'UP': new_rect.y -= self.speed / 60 elif self.direction == 'DOWN': new_rect.y += self.speed / 60 elif self.direction == 'LEFT': new_rect.x -= self.speed / 60 elif self.direction == 'RIGHT': new_rect.x += self.speed / 60 if self.can_move(new_rect): self.rect = new_rect self.move_timer = 0 else: self.move_timer += 1 if self.move_timer >= 60: # 每秒尝试改变方向 self.change_direction() def change_direction(self): self.direction = random.choice(['UP', 'DOWN', 'LEFT', 'RIGHT']) def update(self): if not self.dead: self.move() self.bullet_timer += 1 bullet_speed = 150 if self.tank_type == 1 else 100 if self.tank_type == 2 else 80 bullet_image = bullet_images[self.tank_type - 1] if (self.bullet_timer >= (180 if self.tank_type == 1 else 240 if self.tank_type == 2 else 300)): # 每3/4/5秒发射一颗子弹 bullet = Bullet(bullet_image, (self.rect.centerx, self.rect.centery), self.direction, speed=bullet_speed / 60) bullets.append(bullet) self.bullet_timer = 0 self.rotate() def rotate(self): if self.direction == 'UP': self.image = pygame.transform.rotate(enemy_images[self.tank_type - 1], 0) elif self.direction == 'DOWN': self.image = pygame.transform.rotate(enemy_images[self.tank_type - 1], 180) elif self.direction == 'LEFT': self.image = pygame.transform.rotate(enemy_images[self.tank_type - 1], 90) elif self.direction == 'RIGHT': self.image = pygame.transform.rotate(enemy_images[self.tank_type - 1], -90) def take_damage(self, damage): self.health -= damage if self.health <= 0: self.dead = True self.explode() def explode(self): threading.Thread(target=self.play_explosion_sound).start() def play_explosion_sound(self): pygame.mixer.Sound('Resources/dead.wav').play() def render_explosion(self): if self.dead: self.explode_timer += 1 if self.explode_timer >= 20: self.current_explode_frame += 1 self.explode_timer = 0 if self.current_explode_frame < len(self.explode_frames): screen.blit(self.explode_frames[self.current_explode_frame], self.rect.topleft) class Tank: def __init__(self, image, speed, position): self.image = image self.speed = speed self.rect = self.image.get_rect(center=position) self.direction = 'UP' self.move_timer = 0 self.bullet_timer = 0 self.bullets = [] self.can_fire = True self.health = 10 self.dead = False self.invincible = False self.invincible_timer = 0 self.invincibility_start_time = 0 def move(self, dx, dy): new_rect = self.rect.move(dx, dy) if self.can_move(new_rect): self.rect = new_rect grid_x = (new_rect.x + 40) // tile_size grid_y = (new_rect.y + 40) // tile_size if map_data['layer_1'][grid_y][grid_x] == 8: self.invincible = True self.invincibility_start_time = pygame.time.get_ticks() map_data['layer_1'][grid_y][grid_x] = 0 elif map_data['layer_1'][grid_y][grid_x] == 9: self.health = 10 map_data['layer_1'][grid_y][grid_x] = 0 def can_move(self, new_rect): grid_x = (new_rect.x + 40) // tile_size grid_y = (new_rect.y + 40) // tile_size if new_rect.x < 0 or (new_rect.x >= WIDTH - 40) or new_rect.y < 0 or (new_rect.y >= HEIGHT - 40): return False if map_data['layer_1'][grid_y][grid_x] == 2 or map_data['layer_1'][grid_y][grid_x] == 3 or map_data['layer_1'][grid_y][grid_x] == 4 or map_data['layer_1'][grid_y][grid_x] == 6: return False return True def rotate(self, direction): self.direction = direction if direction == 'UP': self.image = pygame.transform.rotate(player_image, 0) elif direction == 'DOWN': self.image = pygame.transform.rotate(player_image, 180) elif direction == 'LEFT': self.image = pygame.transform.rotate(player_image, 90) elif direction == 'RIGHT': self.image = pygame.transform.rotate(player_image, -90) def update(self): if not self.dead: if self.invincible: if pygame.time.get_ticks() - self.invincibility_start_time >= 5000: # 5秒后失去无敌状态 self.invincible = False self.move_timer += 1 if self.move_timer >= 1200: # 每2秒改变方向 self.move_timer = 0 self.change_direction() if self.direction == 'UP': self.move(0, -self.speed / 60) elif self.direction == 'DOWN': self.move(0, self.speed / 60) elif self.direction == 'LEFT': self.move(-self.speed / 60, 0) elif self.direction == 'RIGHT': self.move(self.speed / 60, 0) self.rotate(self.direction) def fire(self): if self.can_fire: bullet_image = bullet_images[random.randint(0, 2)] bullet = Bullet(bullet_image, (self.rect.centerx, self.rect.centery), self.direction) self.bullets.append(bullet) self.can_fire = False def reset_fire(self): self.can_fire = True def take_damage(self, damage): if not self.invincible: self.health -= damage if self.health <= 0: self.dead = True self.game_over(False) def game_over(self, victory): finish_background = pygame.image.load("Resources/finish.png") finish_background = pygame.transform.scale(finish_background, (WIDTH, HEIGHT)) while True: screen.blit(finish_background, (0, 0)) show_result(victory) pygame.display.flip() start_menu() def activate_invincibility(self): self.invincible = True self.invincible_timer = 0 def start_menu(): while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() mouse_pos = pygame.mouse.get_pos() if btn1_rect.collidepoint(mouse_pos): screen.blit(start_ui, (0, 0)) screen.blit(btn_01, btn1_rect.topleft) else: screen.blit(start_ui, (0, 0)) screen.blit(btn_00, btn1_rect.topleft) if btn2_rect.collidepoint(mouse_pos): screen.blit(btn_11, btn2_rect.topleft) else: screen.blit(btn_10, btn2_rect.topleft) if pygame.mouse.get_pressed()[0]: if btn1_rect.collidepoint(mouse_pos): battle_scene() elif btn2_rect.collidepoint(mouse_pos): pygame.quit() sys.exit() pygame.display.flip() def display_video(video_path, screen): video = VideoFileClip(video_path) for frame in video.iter_frames(fps=30, dtype='uint8'): surface = pygame.surfarray.make_surface(frame) surface = pygame.transform.rotate(surface, -90) screen.blit(surface, (0, 0)) pygame.display.flip() for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE: break def spawn_enemy(): rand = random.random() if rand < 0.5: return 1 # tank_1 elif rand < 0.8: return 2 # tank_2 else: return 3 # tank_3 def show_result(victory): global total_kills global enemy_kills font = pygame.font.SysFont(None, 74) if victory: title = font.render("WIN", True, (255, 255, 255)) else: title = font.render("GAME OVER", True, (255, 0, 0)) screen.blit(title, (WIDTH // 2 - title.get_width() // 2, HEIGHT // 2 - title.get_height() // 2 - 50)) tank_icons = [pygame.transform.scale(enemy_images[i], (40, 40)) for i in range(3)] for i in range(3): screen.blit(tank_icons[i], (WIDTH // 2 - 60 + i * 60, HEIGHT // 2 + 20)) count_text = font.render(f"{enemy_kills[i + 1]}", True, (255, 255, 255)) screen.blit(count_text, (WIDTH // 2 - 60 + i * 60, HEIGHT // 2 + 70)) def draw_kills(): font = pygame.font.SysFont(None, 36) kill_text = font.render(f"Kill: {total_kills}", True, (255, 255, 255)) screen.blit(kill_text, (10, 10)) def battle_scene(): video_path = 'Resources/story.mp4' display_video(video_path, screen) player_tank = Tank(player_image, 60, (8 * tile_size + tile_size // 2, 14 * tile_size + tile_size // 2)) enemy_count = 20 enemy_spawn_timer = 0 enemy_spawn_positions = [(20, 20), (WIDTH // 2, 20), (WIDTH - 60, 20)] enemies = [] global bullets bullets = [] while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() enemy_spawn_timer += 1 if enemy_count > 0 and enemy_spawn_timer >= 240: # 每4秒生成 position = enemy_spawn_positions[enemy_count % 3] tank_type = spawn_enemy() enemy_image = enemy_images[tank_type - 1] enemies.append(EnemyTank(enemy_image, position, tank_type)) enemy_count -= 1 enemy_spawn_timer = 0 keys = pygame.key.get_pressed() if keys[pygame.K_UP]: player_tank.rotate('UP') player_tank.move(0, -player_tank.speed / 60) if keys[pygame.K_DOWN]: player_tank.rotate('DOWN') player_tank.move(0, player_tank.speed / 60) if keys[pygame.K_LEFT]: player_tank.rotate('LEFT') player_tank.move(-player_tank.speed / 60, 0) if keys[pygame.K_RIGHT]: player_tank.rotate('RIGHT') player_tank.move(player_tank.speed / 60, 0) if keys[pygame.K_SPACE]: player_tank.fire() else: player_tank.reset_fire() screen.fill((0, 0, 0)) for layer in range(2): for row in range(15): for col in range(20): key = map_data['layer_' + str(layer)][row][col] if key in image_dict: screen.blit(image_dict[key], (col * tile_size, row * tile_size)) screen.blit(player_tank.image, player_tank.rect.topleft) for bullet in player_tank.bullets: bullet.update() screen.blit(bullet.image, bullet.rect.topleft) grid_x = bullet.rect.x // tile_size grid_y = bullet.rect.y // tile_size if grid_x >= 0 and grid_y >= 0: if grid_y < len(map_data['layer_1']) and grid_x < len(map_data['layer_1'][0]): check_enemy = False for enemy in enemies: if enemy.rect.colliderect(bullet.rect) and not enemy.dead: enemy.take_damage(1) if enemy.dead: global total_kills global enemy_kills total_kills += 1 enemy_kills[enemy.tank_type] += 1 player_tank.bullets.remove(bullet) check_enemy = True break if not check_enemy: tile_id = map_data['layer_1'][grid_y][grid_x] if tile_id == 2: map_data['layer_1'][grid_y][grid_x] = 0 player_tank.bullets.remove(bullet) elif tile_id == 3: map_data['layer_1'][grid_y][grid_x] = 2 player_tank.bullets.remove(bullet) elif tile_id == 4: player_tank.bullets.remove(bullet) elif tile_id == 6: map_data['layer_1'][grid_y][grid_x] = random.choice([8, 9]) player_tank.bullets.remove(bullet) elif tile_id == 7: player_tank.game_over(False) break for enemy in enemies: if not enemy.dead: enemy.update() screen.blit(enemy.image, enemy.rect.topleft) health_bar_length = enemy.rect.width health_bar_height = 5 health_bar_color = (255, 0, 0) health_ratio = enemy.health / (1 if enemy.tank_type == 1 else 3 if enemy.tank_type == 2 else 5) pygame.draw.rect(screen, health_bar_color, (enemy.rect.x, enemy.rect.y - health_bar_height, health_bar_length * health_ratio, health_bar_height)) else: enemy.render_explosion() for bullet in bullets[:]: bullet.update() screen.blit(bullet.image, bullet.rect.topleft) grid_x = bullet.rect.x // tile_size grid_y = bullet.rect.y // tile_size if grid_x >= 0 and grid_y >= 0: if grid_y < len(map_data['layer_1']) and grid_x < len(map_data['layer_1'][0]): if player_tank.rect.colliderect(bullet.rect): player_tank.take_damage(1) bullets.remove(bullet) else: tile_id = map_data['layer_1'][grid_y][grid_x] if tile_id == 2: map_data['layer_1'][grid_y][grid_x] = 1 bullets.remove(bullet) elif tile_id == 3: map_data['layer_1'][grid_y][grid_x] = 2 bullets.remove(bullet) elif tile_id == 4: bullets.remove(bullet) elif tile_id == 6: map_data['layer_1'][grid_y][grid_x] = random.choice([8, 9]) bullets.remove(bullet) elif tile_id == 7: player_tank.game_over(False) for row in range(15): for col in range(20): key = map_data['layer_2'][row][col] if key in image_dict: screen.blit(image_dict[key], (col * tile_size, row * tile_size)) # 绘制玩家坦克的生命值 player_health_bar_length = player_tank.rect.width player_health_bar_height = 5 player_health_bar_color = (255, 0, 0) player_health_ratio = player_tank.health / 10 # 默认生命值为10 pygame.draw.rect(screen, player_health_bar_color, (player_tank.rect.centerx - player_health_bar_length // 2, player_tank.rect.top - player_health_bar_height - 5, player_health_bar_length * player_health_ratio, player_health_bar_height)) draw_kills() pygame.display.flip() start_menu()