点击查看代码
import pygame
import sys
import random
import math
# 初始化pygame
pygame.init()
# 游戏常量
SCREEN_WIDTH = 900
SCREEN_HEIGHT = 600
GRID_SIZE = 80
GRID_ROWS = 5
GRID_COLS = 9
LAWN_LEFT = 100
LAWN_TOP = 100
FPS = 60
# 颜色定义
GREEN = (50, 180, 50)
DARK_GREEN = (30, 120, 30)
BROWN = (150, 100, 50)
LIGHT_BROWN = (200, 150, 100)
YELLOW = (255, 255, 0)
RED = (255, 50, 50)
BLUE = (50, 150, 255)
PURPLE = (180, 50, 180)
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
SKY_BLUE = (135, 206, 235)
GRID_BG = (100, 200, 100, 150)
# 创建游戏窗口
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("植物大战僵尸简化版")
clock = pygame.time.Clock()
# 字体
font = pygame.font.SysFont(None, 28)
big_font = pygame.font.SysFont(None, 48)
class Sun:
def __init__(self, x, y):
self.x = x
self.y = y
self.target_y = y + 100
self.speed = 1
self.collected = False
self.timer = 300 # 太阳存在时间
def update(self):
if self.y < self.target_y:
self.y += self.speed
self.timer -= 1
if self.timer <= 0:
return True
return False
def draw(self, surface):
pygame.draw.circle(surface, YELLOW, (self.x, self.y), 20)
pygame.draw.circle(surface, (255, 200, 0), (self.x, self.y), 15)
def is_clicked(self, pos):
distance = math.sqrt((pos[0] - self.x) ** 2 + (pos[1] - self.y) ** 2)
return distance <= 20
class Plant:
def __init__(self, row, col, plant_type):
self.row = row
self.col = col
self.x = LAWN_LEFT + col * GRID_SIZE + GRID_SIZE // 2
self.y = LAWN_TOP + row * GRID_SIZE + GRID_SIZE // 2
self.health = 100
self.plant_type = plant_type # "sunflower" or "peashooter"
self.shoot_timer = 0
self.sun_timer = 0
def update(self):
if self.plant_type == "peashooter":
self.shoot_timer += 1
if self.shoot_timer >= 60: # 每60帧发射一次
self.shoot_timer = 0
return True
elif self.plant_type == "sunflower":
self.sun_timer += 1
if self.sun_timer >= 180: # 每180帧产生一个太阳
self.sun_timer = 0
return True
return False
def draw(self, surface):
if self.plant_type == "sunflower":
# 绘制向日葵
pygame.draw.circle(surface, (255, 200, 0), (self.x, self.y), 20) # 花盘
for i in range(8):
angle = i * (2 * math.pi / 8)
petal_x = self.x + 30 * math.cos(angle)
petal_y = self.y + 30 * math.sin(angle)
pygame.draw.circle(surface, YELLOW, (petal_x, petal_y), 15)
pygame.draw.circle(surface, (200, 150, 0), (self.x, self.y), 10)
# 绘制茎和叶
pygame.draw.line(surface, GREEN, (self.x, self.y), (self.x, self.y + 30), 5)
pygame.draw.ellipse(surface, GREEN, (self.x - 15, self.y + 10, 30, 15))
elif self.plant_type == "peashooter":
# 绘制豌豆射手
pygame.draw.circle(surface, GREEN, (self.x, self.y), 20) # 头部
pygame.draw.circle(surface, DARK_GREEN, (self.x + 15, self.y), 8) # 嘴巴
pygame.draw.rect(surface, GREEN, (self.x - 15, self.y, 30, 40)) # 身体
pygame.draw.rect(surface, DARK_GREEN, (self.x - 10, self.y + 40, 20, 20)) # 底部
# 绘制血条
bar_width = 40
bar_height = 5
pygame.draw.rect(surface, RED, (self.x - bar_width//2, self.y - 40, bar_width, bar_height))
pygame.draw.rect(surface, (50, 200, 50), (self.x - bar_width//2, self.y - 40, bar_width * self.health // 100, bar_height))
class Pea:
def __init__(self, x, y):
self.x = x
self.y = y
self.speed = 5
self.damage = 25
def update(self):
self.x += self.speed
return self.x > SCREEN_WIDTH
def draw(self, surface):
pygame.draw.circle(surface, GREEN, (self.x, self.y), 8)
pygame.draw.circle(surface, DARK_GREEN, (self.x, self.y), 5)
class Zombie:
def __init__(self, row):
self.row = row
self.x = SCREEN_WIDTH
self.y = LAWN_TOP + row * GRID_SIZE + GRID_SIZE // 2
self.speed = 0.5
self.health = 100
self.damage = 0.5
self.attack_timer = 0
self.attacking = False
def update(self, plants):
if not self.attacking:
self.x -= self.speed
# 检查是否与植物碰撞
for plant in plants:
if plant.row == self.row and abs(self.x - plant.x) < 40:
self.attacking = True
self.attack_timer += 1
if self.attack_timer >= 30: # 每30帧攻击一次
plant.health -= self.damage * 30
self.attack_timer = 0
break
else:
self.attacking = False
return self.x < 0 or self.health <= 0
def draw(self, surface):
# 绘制僵尸身体
pygame.draw.rect(surface, (100, 100, 150), (self.x - 25, self.y - 40, 50, 60))
pygame.draw.circle(surface, (150, 150, 200), (self.x, self.y - 45), 20)
# 绘制僵尸眼睛
pygame.draw.circle(surface, BLACK, (self.x - 5, self.y - 50), 5)
pygame.draw.circle(surface, BLACK, (self.x + 10, self.y - 50), 5)
# 绘制僵尸嘴巴
pygame.draw.rect(surface, (200, 100, 100), (self.x - 15, self.y - 30, 30, 10))
# 绘制血条
bar_width = 50
bar_height = 5
pygame.draw.rect(surface, RED, (self.x - bar_width//2, self.y - 70, bar_width, bar_height))
pygame.draw.rect(surface, (50, 200, 50), (self.x - bar_width//2, self.y - 70, bar_width * self.health // 100, bar_height))
class Game:
def __init__(self):
self.plants = []
self.zombies = []
self.peas = []
self.suns = []
self.sun_count = 100
self.selected_plant = None
self.zombie_spawn_timer = 0
self.game_over = False
self.wave = 1
def spawn_zombie(self):
row = random.randint(0, GRID_ROWS - 1)
self.zombies.append(Zombie(row))
def spawn_sun(self):
x = random.randint(LAWN_LEFT, LAWN_LEFT + GRID_SIZE * GRID_COLS)
self.suns.append(Sun(x, LAWN_TOP))
def handle_events(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_pos = pygame.mouse.get_pos()
# 收集太阳
for sun in self.suns[:]:
if sun.is_clicked(mouse_pos):
self.sun_count += 25
self.suns.remove(sun)
break
# 选择植物
if 20 <= mouse_pos[0] <= 70:
if 20 <= mouse_pos[1] <= 70 and self.sun_count >= 50:
self.selected_plant = "sunflower"
elif 90 <= mouse_pos[1] <= 140 and self.sun_count >= 100:
self.selected_plant = "peashooter"
# 放置植物
if self.selected_plant:
grid_x = (mouse_pos[0] - LAWN_LEFT) // GRID_SIZE
grid_y = (mouse_pos[1] - LAWN_TOP) // GRID_SIZE
if 0 <= grid_x < GRID_COLS and 0 <= grid_y < GRID_ROWS:
# 检查该位置是否已有植物
position_empty = True
for plant in self.plants:
if plant.col == grid_x and plant.row == grid_y:
position_empty = False
break
if position_empty:
if self.selected_plant == "sunflower":
self.plants.append(Plant(grid_y, grid_x, "sunflower"))
self.sun_count -= 50
elif self.selected_plant == "peashooter":
self.plants.append(Plant(grid_y, grid_x, "peashooter"))
self.sun_count -= 100
self.selected_plant = None
def update(self):
if self.game_over:
return
# 更新太阳
for sun in self.suns[:]:
if sun.update():
self.suns.remove(sun)
# 随机生成太阳
if random.randint(0, 100) < 1 and len(self.suns) < 5:
self.spawn_sun()
# 更新植物
for plant in self.plants[:]:
if plant.health <= 0:
self.plants.remove(plant)
continue
if plant.update():
if plant.plant_type == "sunflower":
self.suns.append(Sun(plant.x, plant.y - 50))
elif plant.plant_type == "peashooter":
self.peas.append(Pea(plant.x + 20, plant.y))
# 更新豌豆
for pea in self.peas[:]:
if pea.update():
self.peas.remove(pea)
continue
# 检查豌豆是否击中僵尸
for zombie in self.zombies[:]:
if zombie.row == (pea.y - LAWN_TOP) // GRID_SIZE and abs(pea.x - zombie.x) < 30:
zombie.health -= pea.damage
if zombie.health <= 0:
self.zombies.remove(zombie)
if pea in self.peas:
self.peas.remove(pea)
break
# 更新僵尸
for zombie in self.zombies[:]:
if zombie.update(self.plants):
if zombie.x < 0: # 僵尸到达左边界
self.game_over = True
elif zombie in self.zombies: # 僵尸死亡
self.zombies.remove(zombie)
# 生成僵尸
self.zombie_spawn_timer += 1
spawn_rate = max(100 - self.wave * 10, 30)
if self.zombie_spawn_timer >= spawn_rate and len(self.zombies) < 5 + self.wave:
self.spawn_zombie()
self.zombie_spawn_timer = 0
# 检查是否进入下一波
if len(self.zombies) == 0 and self.zombie_spawn_timer > 300:
self.wave += 1
self.zombie_spawn_timer = 0
def draw(self, surface):
# 绘制背景
surface.fill(SKY_BLUE)
# 绘制标题
title = big_font.render("植物大战僵尸 - 简化版", True, GREEN)
surface.blit(title, (SCREEN_WIDTH // 2 - title.get_width() // 2, 20))
# 绘制草坪
pygame.draw.rect(surface, GREEN, (LAWN_LEFT - 10, LAWN_TOP - 10,
GRID_SIZE * GRID_COLS + 20,
GRID_SIZE * GRID_ROWS + 20))
# 绘制网格
for row in range(GRID_ROWS):
for col in range(GRID_COLS):
rect = pygame.Rect(LAWN_LEFT + col * GRID_SIZE,
LAWN_TOP + row * GRID_SIZE,
GRID_SIZE, GRID_SIZE)
pygame.draw.rect(surface, GRID_BG, rect)
pygame.draw.rect(surface, DARK_GREEN, rect, 1)
# 绘制植物选择区域
pygame.draw.rect(surface, LIGHT_BROWN, (10, 10, 300, 160))
pygame.draw.rect(surface, BROWN, (10, 10, 300, 160), 3)
# 绘制向日葵选择按钮
pygame.draw.rect(surface, YELLOW if self.selected_plant == "sunflower" else (200, 200, 100),
(20, 20, 50, 50))
pygame.draw.rect(surface, BROWN, (20, 20, 50, 50), 2)
pygame.draw.circle(surface, YELLOW, (45, 45), 20)
sun_text = font.render("50", True, BLACK)
surface.blit(sun_text, (75, 35))
# 绘制豌豆射手选择按钮
pygame.draw.rect(surface, GREEN if self.selected_plant == "peashooter" else (100, 200, 100),
(20, 90, 50, 50))
pygame.draw.rect(surface, BROWN, (20, 90, 50, 50), 2)
pygame.draw.circle(surface, GREEN, (45, 115), 15)
pea_text = font.render("100", True, BLACK)
surface.blit(pea_text, (75, 105))
# 绘制阳光计数
pygame.draw.circle(surface, YELLOW, (200, 40), 25)
sun_count_text = font.render(f": {self.sun_count}", True, BLACK)
surface.blit(sun_count_text, (220, 30))
# 绘制波数
wave_text = font.render(f"波数: {self.wave}", True, BLACK)
surface.blit(wave_text, (200, 80))
# 绘制太阳
for sun in self.suns:
sun.draw(surface)
# 绘制植物
for plant in self.plants:
plant.draw(surface)
# 绘制豌豆
for pea in self.peas:
pea.draw(surface)
# 绘制僵尸
for zombie in self.zombies:
zombie.draw(surface)
# 游戏结束
if self.game_over:
overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT))
overlay.set_alpha(150)
overlay.fill(BLACK)
surface.blit(overlay, (0, 0))
game_over_text = big_font.render("游戏结束!", True, RED)
surface.blit(game_over_text, (SCREEN_WIDTH // 2 - game_over_text.get_width() // 2,
SCREEN_HEIGHT // 2 - 50))
restart_text = font.render("按R键重新开始", True, WHITE)
surface.blit(restart_text, (SCREEN_WIDTH // 2 - restart_text.get_width() // 2,
SCREEN_HEIGHT // 2 + 20))
keys = pygame.key.get_pressed()
if keys[pygame.K_r]:
self.__init__()
# 创建游戏实例
game = Game()
# 游戏主循环
while True:
game.handle_events()
game.update()
game.draw(screen)
pygame.display.flip()
clock.tick(FPS)