You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

522 lines
20 KiB

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