植物大战僵尸

import pygame
import random
import sys
import time

pygame.init()

SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("植物大战僵尸")

pygame.font.init()
font = pygame.font.SysFont(["SimHei", "WenQuanYi Micro Hei", "Heiti TC"], 24)
small_font = pygame.font.SysFont(["SimHei", "WenQuanYi Micro Hei", "Heiti TC"], 16)

WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
YELLOW = (255, 255, 0)
GRAY = (100, 100, 100)
LIGHT_GREEN = (144, 238, 144)

LANES = 5
LANE_HEIGHT = 80
LANE_Y_POSITIONS = [i * LANE_HEIGHT + 120 for i in range(LANES)]
CARD_WIDTH = 80
CARD_HEIGHT = 100
CARD_AREA_Y = 20
CARD_AREA_HEIGHT = 100
PLANT_COST = 50
SUN_COST = 25
SUN_INTERVAL = 5000
ZOMBIE_SPAWN_INTERVAL = [5000, 15000]
LAST_ZOMBIE_TIME = 0
LAST_SUN_TIME = 0
GAME_STATE = "START"

class GameObject(pygame.sprite.Sprite):
def init(self, x, y, width, height, image=None):
super().init()
self.x = x
self.y = y
self.width = width
self.height = height
self.image = pygame.Surface((width, height))
if image:
self.image = image
else:
self.image.fill(GREEN)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y

def update(self):
    self.rect.x = self.x
    self.rect.y = self.y

class Sun(GameObject):
def init(self, x, y):
super().init(x, y, 30, 30)
self.image.fill(YELLOW)
self.value = SUN_COST
self.fall_speed = 1
self.target_y = y + random.randint(50, 100)
self.collected = False
self.lifetime = 8000
self.spawn_time = pygame.time.get_ticks()

def update(self):
    current_time = pygame.time.get_ticks()
    if current_time - self.spawn_time > self.lifetime:
        self.kill()
        return

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

class Plant(GameObject):
def init(self, x, y, cost, health=100):
super().init(x, y, 50, 50)
self.cost = cost
self.health = health
self.attack_damage = 10
self.attack_speed = 1000
self.last_attack_time = 0
self.lane = self.get_lane()

def get_lane(self):
    for i, y_pos in enumerate(LANE_Y_POSITIONS):
        if self.y >= y_pos and self.y < y_pos + LANE_HEIGHT:
            return i
    return 0

def attack(self, zombies):
    current_time = pygame.time.get_ticks()
    if current_time - self.last_attack_time < self.attack_speed:
        return

    for zombie in zombies:
        if zombie.lane == self.lane and zombie.x < self.x + 70:
            zombie.health -= self.attack_damage
            self.last_attack_time = current_time
            if zombie.health <= 0:
                zombie.kill()
            break

class Peashooter(Plant):
def init(self, x, y):
super().init(x, y, PLANT_COST)
self.image.fill(GREEN)
self.attack_speed = 1500
self.attack_damage = 25

class Sunflower(Plant):
def init(self, x, y):
super().init(x, y, PLANT_COST)
self.image.fill(YELLOW)
self.sun_production_rate = 25000
self.last_production_time = 0

def produce_sun(self, suns):
    current_time = pygame.time.get_ticks()
    if current_time - self.last_production_time > self.sun_production_rate:
        suns.add(Sun(self.x + 10, self.y + 10))
        self.last_production_time = current_time

class Zombie(GameObject):
def init(self, lane):
x = SCREEN_WIDTH
y = LANE_Y_POSITIONS[lane]
super().init(x, y, 60, 80)
self.image.fill(RED)
self.health = 75
self.speed = 0.1
self.lane = lane
self.damage = 10

def update(self):
    self.x -= self.speed
    super().update()

class Card(GameObject):
def init(self, x, y, plant_type, cost, cooldown=5000):
super().init(x, y, CARD_WIDTH, CARD_HEIGHT)
self.plant_type = plant_type
self.cost = cost
self.cooldown = cooldown
self.last_used_time = 0
self.is_available = True
self.update_image()

def update_image(self):
    self.image.fill(LIGHT_GREEN)
    pygame.draw.rect(self.image, GRAY, (0, 0, self.width, self.height), 2)
    text = small_font.render(self.plant_type.__name__, True, BLACK)
    cost_text = small_font.render(f"Cost: {self.cost}", True, BLACK)
    self.image.blit(text, (5, 5))
    self.image.blit(cost_text, (5, 30))

def can_use(self, current_time, sun_count):
    if current_time - self.last_used_time < self.cooldown:
        self.is_available = False
        cooldown_percent = 1 - (current_time - self.last_used_time) / self.cooldown
        pygame.draw.rect(self.image, (100, 100, 100, 100), 
                         (0, 0, self.width, self.height * cooldown_percent))
        return False
    if sun_count < self.cost:
        return False
    self.is_available = True
    return True

def use(self, current_time):
    self.last_used_time = current_time
    self.update_image()

def draw_lanes():
for i, y in enumerate(LANE_Y_POSITIONS):
pygame.draw.rect(screen, (100, 200, 100), (0, y, SCREEN_WIDTH, LANE_HEIGHT), 1)
lane_text = small_font.render(f"车道 {i+1}", True, BLACK)
screen.blit(lane_text, (10, y + 10))

def draw_sun_counter(sun_count):
sun_text = font.render(f"阳光: {sun_count}", True, YELLOW)
screen.blit(sun_text, (10, 10))

def draw_game_state():
if GAME_STATE == "GAME_OVER":
game_over_text = font.render("游戏结束!", True, RED)
restart_text = font.render("按 R 重新开始", True, WHITE)
screen.blit(game_over_text, (SCREEN_WIDTH//2 - 80, SCREEN_HEIGHT//2 - 30))
screen.blit(restart_text, (SCREEN_WIDTH//2 - 80, SCREEN_HEIGHT//2 + 10))

def check_collision(obj1, obj2):
return (obj1.rect.x < obj2.rect.x + obj2.width and
obj1.rect.x + obj1.width > obj2.rect.x and
obj1.rect.y < obj2.rect.y + obj2.height and
obj1.rect.y + obj1.height > obj2.rect.y)

def create_cards():
cards = []
card_x = 10
for plant_type in [Peashooter, Sunflower]:
cards.append(Card(card_x, CARD_AREA_Y, plant_type, PLANT_COST))
card_x += CARD_WIDTH + 10
return cards

def get_plant_position(mouse_pos):
grid_x = (mouse_pos[0] // 50) * 50
for y_pos in LANE_Y_POSITIONS:
if mouse_pos[1] >= y_pos and mouse_pos[1] < y_pos + LANE_HEIGHT:
return grid_x, y_pos
return None, None

def main():
global GAME_STATE, LAST_ZOMBIE_TIME, LAST_SUN_TIME

all_sprites = pygame.sprite.Group()
plants = pygame.sprite.Group()
zombies = pygame.sprite.Group()
suns = pygame.sprite.Group()
cards = create_cards()
all_sprites.add(cards)

sun_count = 150
selected_card = None
running = True
clock = pygame.time.Clock()

while running:
    current_time = pygame.time.get_ticks()
    
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
            
        if GAME_STATE == "GAME_OVER":
            if event.type == pygame.KEYDOWN and event.key == pygame.K_r:
                main()
                return
            continue
            
        if event.type == pygame.MOUSEBUTTONDOWN:
            mouse_x, mouse_y = pygame.mouse.get_pos()
            
            for card in cards:
                if check_collision(card, GameObject(mouse_x, mouse_y, 1, 1)):
                    if card.can_use(current_time, sun_count):
                        selected_card = card
                        break
            
            for sun in suns:
                if check_collision(sun, GameObject(mouse_x, mouse_y, 1, 1)):
                    sun.collected = True
                    sun_count += sun.value
                    sun.kill()
            
            if selected_card and mouse_y > CARD_AREA_HEIGHT + CARD_AREA_Y:
                plant_x, plant_y = get_plant_position((mouse_x, mouse_y))
                if plant_x is not None:
                    plant_exists = False
                    for plant in plants:
                        if plant.x == plant_x and plant.y == plant_y:
                            plant_exists = True
                            break
                    
                    if not plant_exists:
                        new_plant = selected_card.plant_type(plant_x, plant_y)
                        plants.add(new_plant)
                        all_sprites.add(new_plant)
                        sun_count -= selected_card.cost
                        selected_card.use(current_time)
                        selected_card = None
    
    screen.fill((200, 255, 200))
    
    if GAME_STATE == "START":
        start_text = font.render("按任意键开始游戏", True, BLACK)
        screen.blit(start_text, (SCREEN_WIDTH//2 - 100, SCREEN_HEIGHT//2))
        pygame.display.flip()
        
        waiting = True
        while waiting:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()
                if event.type == pygame.KEYDOWN or event.type == pygame.MOUSEBUTTONDOWN:
                    waiting = False
                    GAME_STATE = "PLAYING"
        continue
    
    if current_time - LAST_SUN_TIME > SUN_INTERVAL and random.random() < 0.02:
        sun_lane = random.randint(0, LANES-1)
        sun_x = random.randint(100, SCREEN_WIDTH - 100)
        sun_y = LANE_Y_POSITIONS[sun_lane]
        suns.add(Sun(sun_x, sun_y))
        LAST_SUN_TIME = current_time
    
    if current_time - LAST_ZOMBIE_TIME > random.randint(*ZOMBIE_SPAWN_INTERVAL):
        zombie_lane = random.randint(0, LANES-1)
        new_zombie = Zombie(zombie_lane)
        zombies.add(new_zombie)
        all_sprites.add(new_zombie)
        LAST_ZOMBIE_TIME = current_time
    
    for plant in plants:
        if isinstance(plant, Sunflower):
            plant.produce_sun(suns)
        elif isinstance(plant, Peashooter):
            plant.attack(zombies)
    
    for zombie in zombies:
        if zombie.x < 50:
            GAME_STATE = "GAME_OVER"
    
    all_sprites.update()
    suns.update()
    
    draw_lanes()
    all_sprites.draw(screen)
    suns.draw(screen)
    draw_sun_counter(sun_count)
    draw_game_state()
    
    if selected_card and pygame.mouse.get_pos()[1] > CARD_AREA_HEIGHT + CARD_AREA_Y:
        plant_x, plant_y = get_plant_position(pygame.mouse.get_pos())
        if plant_x is not None:
            preview_plant = selected_card.plant_type(plant_x, plant_y)
            preview_plant.image.set_alpha(128)
            screen.blit(preview_plant.image, (plant_x, plant_y))
    
    pygame.display.flip()
    clock.tick(60)

pygame.quit()
sys.exit()

if name == "main":
main()
2024310143026

posted @ 2025-06-20 21:33  林leo  阅读(12)  评论(0)    收藏  举报