植物大战僵尸
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


浙公网安备 33010602011771号