import pygame
import random
import sys

初始化pygame

pygame.init()

游戏常量

SCREEN_WIDTH, SCREEN_HEIGHT = 800, 600
GRID_SIZE = 80
LANES = 5
COLUMNS = 9
FPS = 60

颜色定义

WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
YELLOW = (255, 255, 0)
GRAY = (128, 128, 128)

创建游戏窗口

screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("植物大战僵尸")
clock = pygame.time.Clock()

加载资源

def load_image(name, size=None):
try:
image = pygame.image.load(f"assets/{name}.png").convert_alpha()
if size:
image = pygame.transform.scale(image, size)
return image
except:
# 如果找不到图片,创建一个彩色方块作为替代
surf = pygame.Surface((size or (50, 50)))
colors = {
'sunflower': YELLOW,
'peashooter': GREEN,
'peashot': (150, 255, 150),
'zombie': RED,
'sun': YELLOW,
'background': (200, 220, 200),
'card': (180, 255, 180),
'button': (255, 200, 100),
'shovel': (150, 150, 150)
}
color = colors.get(name.split('.')[0], (100, 100, 100))
surf.fill(color)
return surf

游戏资源

resources = {
'background': load_image('background', (SCREEN_WIDTH, SCREEN_HEIGHT)),
'sunflower': load_image('sunflower', (60, 60)),
'peashooter': load_image('peashooter', (60, 60)),
'peashot': load_image('peashot', (20, 20)),
'zombie': load_image('zombie', (60, 80)),
'sun': load_image('sun', (40, 40)),
'card_sunflower': load_image('card', (80, 100)),
'card_peashooter': load_image('card', (80, 100)),
'shovel': load_image('shovel', (40, 40)),
'button': load_image('button', (120, 40))
}

游戏字体

pygame.font.init()
font = pygame.font.SysFont("SimHei", 24)
large_font = pygame.font.SysFont("SimHei", 36)

游戏类

class GameObject:
def init(self, x, y, image):
self.x = x
self.y = y
self.image = image
self.width = image.get_width()
self.height = image.get_height()
self.rect = pygame.Rect(x, y, self.width, self.height)

def update_rect(self):
    self.rect = pygame.Rect(self.x, self.y, self.width, self.height)

def draw(self, surface):
    surface.blit(self.image, (self.x, self.y))

class Plant(GameObject):
def init(self, x, y, image, health, cost):
super().init(x, y, image)
self.health = health
self.cost = cost
self.attack_cooldown = 0
self.sun_production_cooldown = 0

def update(self):
    if self.attack_cooldown > 0:
        self.attack_cooldown -= 1
    if self.sun_production_cooldown > 0:
        self.sun_production_cooldown -= 1

class Sunflower(Plant):
def init(self, x, y):
super().init(x, y, resources['sunflower'], 100, 50)
self.sun_production_cooldown = 300

def update(self):
    super().update()
    if self.sun_production_cooldown == 0:
        self.sun_production_cooldown = 300
        return Sun(self.x, self.y)
    return None

class Peashooter(Plant):
def init(self, x, y):
super().init(x, y, resources['peashooter'], 100, 100)
self.attack_cooldown = 60

def update(self):
    super().update()
    if self.attack_cooldown == 0:
        self.attack_cooldown = 60
        return Pea(self.x + 40, self.y + 20)
    return None

class Sun(GameObject):
def init(self, x, y):
super().init(x, y, resources['sun'])
self.value = 25
self.target_y = y + random.randint(20, 80)
self.fall_speed = 1
self.collected = False
self.collect_time = 0

def update(self):
    if self.y < self.target_y and not self.collected:
        self.y += self.fall_speed
    self.update_rect()

def collect(self):
    self.collected = True
    self.collect_time = 30

def is_collected(self):
    if self.collected:
        self.collect_time -= 1
        if self.collect_time <= 0:
            return True
    return False

class Pea(GameObject):
def init(self, x, y):
super().init(x, y, resources['peashot'])
self.damage = 20
self.speed = 5

def update(self):
    self.x += self.speed
    self.update_rect()
    return self.x > SCREEN_WIDTH

class Zombie(GameObject):
def init(self, lane):
x = SCREEN_WIDTH
y = 100 + lane * GRID_SIZE
super().init(x, y, resources['zombie'])
self.health = 200
self.speed = 0.5
self.lane = lane
self.eating = False

def update(self, plants):
    self.eating = False
    for plant in plants:
        if plant.y // GRID_SIZE == self.lane and self.x < plant.x + plant.width and self.x + self.width > plant.x:
            self.eating = True
            plant.health -= 1
            return
    
    if not self.eating:
        self.x -= self.speed
    self.update_rect()

def is_dead(self):
    return self.health <= 0

class Card(GameObject):
def init(self, x, y, plant_type, cost, recharge_time):
super().init(x, y, resources[f'card_{plant_type}'])
self.plant_type = plant_type
self.cost = cost
self.recharge_time = recharge_time
self.current_recharge = 0
self.available = True

def update(self):
    if self.current_recharge > 0:
        self.current_recharge -= 1
        if self.current_recharge == 0:
            self.available = True

def use(self):
    if self.available:
        self.available = False
        self.current_recharge = self.recharge_time
        return True
    return False

def draw(self, surface):
    super().draw(surface)
    # 绘制卡片上的文字
    cost_text = font.render(f"{self.cost}", True, BLACK)
    surface.blit(cost_text, (self.x + 10, self.y + 10))
    
    # 如果卡片在冷却中,绘制冷却覆盖
    if not self.available:
        recharge_percent = 1 - (self.current_recharge / self.recharge_time)
        recharge_height = int(self.height * recharge_percent)
        pygame.draw.rect(surface, (100, 100, 100, 150), 
                        (self.x, self.y + self.height - recharge_height, 
                         self.width, recharge_height))

class Shovel(GameObject):
def init(self, x, y):
super().init(x, y, resources['shovel'])
self.selected = False

def draw(self, surface):
    super().draw(surface)
    if self.selected:
        pygame.draw.rect(surface, GREEN, (self.x, self.y, self.width, self.height), 2)

游戏状态类

class GameState:
def init(self):
self.sun = 150
self.plants = []
self.zombies = []
self.sun_objects = []
self.peas = []
self.cards = [
Card(50, 20, 'sunflower', 50, 100),
Card(150, 20, 'peashooter', 100, 150)
]
self.shovel = Shovel(250, 30)
self.selected_card = None
self.selected_shovel = False
self.score = 0
self.wave = 1
self.zombie_timer = 100
self.game_over = False
self.victory = False
self.plant_grid = [[None for _ in range(COLUMNS)] for _ in range(LANES)]

def update(self):
    if self.game_over:
        return
    
    # 生成阳光
    if random.randint(1, 300) == 1:
        lane = random.randint(0, LANES - 1)
        x = random.randint(100, SCREEN_WIDTH - 100)
        self.sun_objects.append(Sun(x, 50))
    
    # 更新植物
    new_plants = []
    for plant in self.plants:
        plant.update()
        
        # 处理植物产生阳光或豌豆
        if isinstance(plant, Sunflower):
            sun = plant.update()
            if sun:
                self.sun_objects.append(sun)
        elif isinstance(plant, Peashooter):
            pea = plant.update()
            if pea:
                self.peas.append(pea)
        
        # 检查植物是否死亡
        if plant.health > 0:
            new_plants.append(plant)
        else:
            # 植物死亡,从网格中移除
            grid_x = plant.x // GRID_SIZE
            grid_y = plant.y // GRID_SIZE - 1  # 减去顶部栏
            if 0 <= grid_y < LANES and 0 <= grid_x < COLUMNS:
                self.plant_grid[grid_y][grid_x] = None
    
    self.plants = new_plants
    
    # 更新阳光
    for sun in self.sun_objects:
        sun.update()
    
    self.sun_objects = [sun for sun in self.sun_objects if not sun.is_collected()]
    
    # 更新豌豆
    new_peas = []
    for pea in self.peas:
        if not pea.update():
            new_peas.append(pea)
    
    self.peas = new_peas
    
    # 生成僵尸
    self.zombie_timer -= 1
    if self.zombie_timer <= 0:
        lane = random.randint(0, LANES - 1)
        self.zombies.append(Zombie(lane))
        self.zombie_timer = random.randint(80, 150)
    
    # 更新僵尸
    new_zombies = []
    for zombie in self.zombies:
        zombie.update(self.plants)
        
        # 检查僵尸是否到达最左边
        if zombie.x < 50:
            self.game_over = True
        
        # 检查僵尸是否死亡
        if not zombie.is_dead():
            new_zombies.append(zombie)
        else:
            self.score += 100
    
    self.zombies = new_zombies
    
    # 检查胜利条件
    if self.score >= 1000 and not self.zombies:
        self.victory = True
    
    # 更新卡片
    for card in self.cards:
        card.update()
    
    # 检查豌豆与僵尸的碰撞
    for pea in self.peas[:]:
        hit = False
        for zombie in self.zombies:
            if pea.rect.colliderect(zombie.rect):
                zombie.health -= pea.damage
                self.peas.remove(pea)
                hit = True
                break
        if hit:
            break

def draw(self, surface):
    # 绘制背景
    surface.blit(resources['background'], (0, 0))
    
    # 绘制网格线
    for i in range(1, LANES):
        pygame.draw.line(surface, GRAY, (0, 100 + i * GRID_SIZE), (SCREEN_WIDTH, 100 + i * GRID_SIZE), 2)
    for i in range(1, COLUMNS):
        pygame.draw.line(surface, GRAY, (i * GRID_SIZE, 100), (i * GRID_SIZE, SCREEN_HEIGHT), 2)
    
    # 绘制阳光
    for sun in self.sun_objects:
        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)
    
    # 绘制卡片
    for card in self.cards:
        card.draw(surface)
    
    # 绘制铲子
    self.shovel.draw(surface)
    
    # 绘制阳光数量
    sun_text = font.render(f"阳光: {self.sun}", True, BLACK)
    surface.blit(sun_text, (400, 30))
    
    # 绘制分数
    score_text = font.render(f"分数: {self.score}", True, BLACK)
    surface.blit(score_text, (550, 30))
    
    # 绘制波次
    wave_text = font.render(f"波次: {self.wave}", True, BLACK)
    surface.blit(wave_text, (700, 30))
    
    # 如果选择了卡片,绘制预览
    if self.selected_card is not None:
        mouse_x, mouse_y = pygame.mouse.get_pos()
        grid_x = (mouse_x // GRID_SIZE) * GRID_SIZE
        grid_y = ((mouse_y - 100) // GRID_SIZE) * GRID_SIZE + 100
        
        if 100 <= grid_y < 100 + LANES * GRID_SIZE:
            plant_type = self.selected_card.plant_type
            if plant_type == 'sunflower':
                preview = resources['sunflower']
            elif plant_type == 'peashooter':
                preview = resources['peashooter']
            
            # 半透明预览
            preview.set_alpha(150)
            surface.blit(preview, (grid_x + 10, grid_y + 10))
    
    # 如果选择了铲子,绘制预览
    if self.selected_shovel:
        mouse_x, mouse_y = pygame.mouse.get_pos()
        surface.blit(resources['shovel'], (mouse_x - 20, mouse_y - 20))
    
    # 游戏结束画面
    if self.game_over:
        overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT))
        overlay.fill(BLACK)
        overlay.set_alpha(150)
        surface.blit(overlay, (0, 0))
        
        game_over_text = large_font.render("游戏结束", True, WHITE)
        final_score_text = font.render(f"最终分数: {self.score}", True, WHITE)
        restart_text = font.render("按 R 键重新开始", True, WHITE)
        
        surface.blit(game_over_text, (SCREEN_WIDTH//2 - game_over_text.get_width()//2, SCREEN_HEIGHT//2 - 50))
        surface.blit(final_score_text, (SCREEN_WIDTH//2 - final_score_text.get_width()//2, SCREEN_HEIGHT//2))
        surface.blit(restart_text, (SCREEN_WIDTH//2 - restart_text.get_width()//2, SCREEN_HEIGHT//2 + 50))
    
    # 胜利画面
    if self.victory:
        overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT))
        overlay.fill(YELLOW)
        overlay.set_alpha(100)
        surface.blit(overlay, (0, 0))
        
        victory_text = large_font.render("胜利!", True, GREEN)
        final_score_text = font.render(f"最终分数: {self.score}", True, BLACK)
        next_wave_text = font.render("按 N 键进入下一波", True, BLACK)
        
        surface.blit(victory_text, (SCREEN_WIDTH//2 - victory_text.get_width()//2, SCREEN_HEIGHT//2 - 50))
        surface.blit(final_score_text, (SCREEN_WIDTH//2 - final_score_text.get_width()//2, SCREEN_HEIGHT//2))
        surface.blit(next_wave_text, (SCREEN_WIDTH//2 - next_wave_text.get_width()//2, SCREEN_HEIGHT//2 + 50))

def handle_click(self, pos):
    if self.game_over or self.victory:
        return
    
    x, y = pos
    
    # 检查是否点击了卡片
    for card in self.cards:
        if card.rect.collidepoint(pos) and card.available and self.sun >= card.cost:
            self.selected_card = card
            self.selected_shovel = False
            return
    
    # 检查是否点击了铲子
    if self.shovel.rect.collidepoint(pos):
        self.selected_shovel = not self.selected_shovel
        self.selected_card = None
        return
    
    # 检查是否点击了阳光
    for sun in self.sun_objects[:]:
        if sun.rect.collidepoint(pos) and not sun.collected:
            sun.collect()
            self.sun += sun.value
            return
    
    # 如果选择了卡片,尝试种植植物
    if self.selected_card is not None:
        grid_x = x // GRID_SIZE
        grid_y = (y - 100) // GRID_SIZE
        
        if 0 <= grid_y < LANES and 0 <= grid_x < COLUMNS and self.plant_grid[grid_y][grid_x] is None:
            plant_type = self.selected_card.plant_type
            plant_x = grid_x * GRID_SIZE + 10
            plant_y = grid_y * GRID_SIZE + 110
            
            if plant_type == 'sunflower' and self.sun >= 50:
                self.plants.append(Sunflower(plant_x, plant_y))
                self.plant_grid[grid_y][grid_x] = 'sunflower'
                self.sun -= 50
                self.selected_card.use()
            elif plant_type == 'peashooter' and self.sun >= 100:
                self.plants.append(Peashooter(plant_x, plant_y))
                self.plant_grid[grid_y][grid_x] = 'peashooter'
                self.sun -= 100
                self.selected_card.use()
        
        self.selected_card = None
    
    # 如果选择了铲子,尝试移除植物
    elif self.selected_shovel:
        grid_x = x // GRID_SIZE
        grid_y = (y - 100) // GRID_SIZE
        
        if 0 <= grid_y < LANES and 0 <= grid_x < COLUMNS and self.plant_grid[grid_y][grid_x] is not None:
            # 找到要移除的植物
            for i, plant in enumerate(self.plants):
                if plant.x // GRID_SIZE == grid_x and (plant.y - 100) // GRID_SIZE == grid_y:
                    self.plants.pop(i)
                    self.plant_grid[grid_y][grid_x] = None
                    break
        
        self.selected_shovel = False

def restart(self):
    self.__init__()

主游戏循环

def main():
game_state = GameState()

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        
        if event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 1:  # 左键点击
                game_state.handle_click(pygame.mouse.get_pos())
        
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_r and game_state.game_over:
                game_state.restart()
            if event.key == pygame.K_n and game_state.victory:
                game_state.victory = False
                game_state.wave += 1
                game_state.zombie_timer = 50
    
    game_state.update()
    game_state.draw(screen)
    
    pygame.display.flip()
    clock.tick(FPS)

pygame.quit()
sys.exit()

if name == "main":
main()#3002

posted on 2025-06-17 07:54  雨水啊  阅读(32)  评论(0)    收藏  举报