Pygame植物大战僵尸
Pygame植物大战僵尸

Pygame植物大战僵尸

分结构教学内容

阶段内容模块可视化效果
前期准备准备号对应的素材显示植物、僵尸、子弹等
第1阶段创建窗口、显示文字白底窗口 + 钱数文字
第2阶段加载地图背景格子地图铺满屏幕(交错灰白格)
第3阶段点击种植物(无逻辑)点击地图出现植物图片
第4阶段实现向日葵产钱逻辑钱数增加可见
第5阶段实现豌豆射手与子弹子弹射出动画
第6阶段添加僵尸移动与碰撞僵尸靠近植物动画
第7阶段加入积分、关卡提升显示得分与通关

前期准备工作:

①子弹:

②豌豆射手:

③向日葵:

④僵尸:

第1阶段:创建窗口、显示文字

功能目标:

①创建一个窗口(800×560)

②在窗口上方显示金钱文字和提示语

import pygame
# 初始化
WIDTH, HEIGHT = 800, 560
pygame.init()
window = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("植物大战僵尸 - 第1阶段:窗口与文字")
# 文本绘制函数
def draw_text(content, size, color, x, y):
    font = pygame.font.SysFont('kaiti', size)
    text = font.render(content, True, color)
    window.blit(text, (x, y))
# 主循环
running = True
while running:
    window.fill((255, 255, 255))  # 背景白色
    # 绘制顶部文字
    draw_text("当前金钱: 200", 24, (255, 0, 0), 500, 30)
    draw_text("提示:下一阶段将绘制地图格子背景", 20, (0, 0, 255), 10, 10)
    # 事件处理
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
    pygame.display.update()
    pygame.time.wait(10)
pygame.quit()

第2阶段:加载地图背景格子

功能目标:

①显示一个 10 列 × 6 行 的格子地图(从第2行开始)

②奇偶交错显示两种绿色(便于区分格子)

import pygame
# 基本设置
WIDTH, HEIGHT = 800, 560
TILE_SIZE = 80
pygame.init()
window = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("植物大战僵尸 - 第2阶段:地图背景格子")
# 地图绘制函数
def draw_map():
    for row in range(1, 7):  # y方向:从第1行(屏幕y=80)开始绘制6行
        for col in range(10):  # x方向:共10列
            rect = pygame.Rect(col * TILE_SIZE, row * TILE_SIZE, TILE_SIZE, TILE_SIZE)
            color = (192, 255, 192) if (row + col) % 2 == 0 else (160, 220, 160)
            pygame.draw.rect(window, color, rect)
# 主循环
running = True
while running:
    window.fill((255, 255, 255))  # 背景白色
    draw_map()  # 绘制地图格子
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
    pygame.display.update()
    pygame.time.wait(10)
pygame.quit()

第3阶段:点击种植物

功能目标:

①鼠标左键点击地图区域

②在点击位置显示植物图片(向日葵)

import pygame
# 常量设置
WIDTH, HEIGHT = 800, 560
TILE_SIZE = 80
pygame.init()
window = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("植物大战僵尸 - 第3阶段:点击种植物")
# 加载植物图片(确保你有 imgs/sunflower.png)
sunflower_img = pygame.image.load("imgs/sunflower.png")
# 存储植物坐标
plants = []
# 地图绘制函数
def draw_map():
    for row in range(1, 7):  # 从第1行开始(y=80)
        for col in range(10):
            rect = pygame.Rect(col * TILE_SIZE, row * TILE_SIZE, TILE_SIZE, TILE_SIZE)
            color = (192, 255, 192) if (row + col) % 2 == 0 else (160, 220, 160)
            pygame.draw.rect(window, color, rect)
# 植物绘制函数
def draw_plants():
    for pos in plants:
        window.blit(sunflower_img, pos)
# 主循环
running = True
while running:
    window.fill((255, 255, 255))
    draw_map()
    draw_plants()
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
            # 鼠标左键点击位置换算成左上角坐标(贴图对齐)
            x = event.pos[0] // TILE_SIZE * TILE_SIZE
            y = event.pos[1] // TILE_SIZE * TILE_SIZE
            if y >= TILE_SIZE:  # 避免顶部区域
                plants.append((x, y))
    pygame.display.update()
    pygame.time.wait(10)
pygame.quit()

第4阶段:实现向日葵产钱逻辑

功能目标:

①鼠标左键种植向日葵(50元)

②向日葵每隔一定时间产出5元

③上方显示当前金钱,能看到动态增加

import pygame
# 常量设置
WIDTH, HEIGHT = 800, 560
TILE_SIZE = 80
money = 200  # 初始金钱
pygame.init()
window = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("植物大战僵尸 - 第4阶段:向日葵产阳光")
# 加载向日葵图片(确保路径正确)
sunflower_img = pygame.image.load("imgs/sunflower.png")
# 向日葵类
class Sunflower:
    def __init__(self, x, y):
        self.rect = sunflower_img.get_rect(topleft=(x, y))
        self.hp = 100
        self.counter = 0
    def display(self):
        window.blit(sunflower_img, self.rect)
    def produce_money(self):
        global money
        self.counter += 1
        if self.counter >= 25:  # 每25帧产5元
            money += 5
            self.counter = 0
plants = []
# 地图绘制函数
def draw_map():
    for row in range(1, 7):
        for col in range(10):
            rect = pygame.Rect(col * TILE_SIZE, row * TILE_SIZE, TILE_SIZE, TILE_SIZE)
            color = (192, 255, 192) if (row + col) % 2 == 0 else (160, 220, 160)
            pygame.draw.rect(window, color, rect)
# 文本绘制
def draw_text(text, x, y, color=(255, 0, 0), size=24):
    font = pygame.font.SysFont("kaiti", size)
    surface = font.render(text, True, color)
    window.blit(surface, (x, y))
# 主循环
running = True
while running:
    window.fill((255, 255, 255))
    draw_map()
    for plant in plants:
        plant.display()
        plant.produce_money()
    draw_text(f"当前金钱: {money}", 500, 30)
    draw_text("提示:点击左键种向日葵,每株每段时间产5元", 10, 10, (0, 0, 200), 20)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
            x = event.pos[0] // TILE_SIZE * TILE_SIZE
            y = event.pos[1] // TILE_SIZE * TILE_SIZE
            if y >= TILE_SIZE and money >= 50:
                plants.append(Sunflower(x, y))
                money -= 50
    pygame.display.update()
    pygame.time.wait(10)
pygame.quit()

第5阶段:实现豌豆射手与子弹发射动画

功能目标:

①鼠标右键种豌豆射手(扣50元)

②如果前方有僵尸,同一行的豌豆射手会定期发射子弹

③子弹会向右飞行,有移动动画(暂不判断是否打中)

import pygame
import random
# 基本配置
WIDTH, HEIGHT = 800, 560
TILE_SIZE = 80
money = 200
pygame.init()
window = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("植物大战僵尸 - 第5阶段:豌豆射手发子弹")
# 加载素材
sunflower_img = pygame.image.load("imgs/sunflower.png")
peashooter_img = pygame.image.load("imgs/peashooter.png")
bullet_img = pygame.image.load("imgs/peabullet.png")
zombie_img = pygame.image.load("imgs/zombie.png")
# 植物类(基类)
class Plant:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.hp = 100
    def rect(self):
        return pygame.Rect(self.x, self.y, TILE_SIZE, TILE_SIZE)
# 向日葵类
class Sunflower(Plant):
    def __init__(self, x, y):
        super().__init__(x, y)
        self.counter = 0
    def display(self):
        window.blit(sunflower_img, (self.x, self.y))
    def produce_money(self):
        global money
        self.counter += 1
        if self.counter >= 25:
            money += 5
            self.counter = 0
# 豌豆射手类
class PeaShooter(Plant):
    def __init__(self, x, y):
        super().__init__(x, y)
        self.shot_timer = 0
    def display(self):
        window.blit(peashooter_img, (self.x, self.y))
    def shoot(self):
        self.shot_timer += 1
        if self.shot_timer >= 25:
            bullets.append(Bullet(self.x + 60, self.y + 20))
            self.shot_timer = 0
# 子弹类
class Bullet:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.speed = 10
        self.live = True
    def move(self):
        self.x += self.speed
        if self.x > WIDTH:
            self.live = False
    def display(self):
        window.blit(bullet_img, (self.x, self.y))
# 僵尸类
class Zombie:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.hp = 1000
        self.speed = 1
        self.live = True
    def move(self):
        self.x -= self.speed
    def display(self):
        window.blit(zombie_img, (self.x, self.y))
# 列表存储
plants = []
bullets = []
zombies = [Zombie(800 + i * 100, random.randint(1, 6) * TILE_SIZE) for i in range(3)]
# 地图绘制函数
def draw_map():
    for row in range(1, 7):
        for col in range(10):
            rect = pygame.Rect(col * TILE_SIZE, row * TILE_SIZE, TILE_SIZE, TILE_SIZE)
            color = (192, 255, 192) if (row + col) % 2 == 0 else (160, 220, 160)
            pygame.draw.rect(window, color, rect)
# 文本函数
def draw_text(text, x, y, color=(255, 0, 0), size=24):
    font = pygame.font.SysFont("kaiti", size)
    surface = font.render(text, True, color)
    window.blit(surface, (x, y))
# 主循环
running = True
while running:
    window.fill((255, 255, 255))
    draw_map()
    # 植物处理
    for plant in plants:
        plant.display()
        if isinstance(plant, Sunflower):
            plant.produce_money()
        elif isinstance(plant, PeaShooter):
            plant.shoot()
    # 子弹处理
    for bullet in bullets[:]:
        if bullet.live:
            bullet.move()
            bullet.display()
        else:
            bullets.remove(bullet)
    # 僵尸处理
    for zombie in zombies:
        zombie.move()
        zombie.display()
    # UI信息
    draw_text(f"当前金钱: {money}", 500, 30)
    draw_text("左键:向日葵;右键:豌豆射手(将自动射击)", 10, 10, (0, 0, 200), 20)
    # 事件处理
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.MOUSEBUTTONDOWN:
            x = event.pos[0] // TILE_SIZE * TILE_SIZE
            y = event.pos[1] // TILE_SIZE * TILE_SIZE
            if y >= TILE_SIZE:
                if event.button == 1 and money >= 50:
                    plants.append(Sunflower(x, y))
                    money -= 50
                elif event.button == 3 and money >= 50:
                    plants.append(PeaShooter(x, y))
                    money -= 50
    pygame.display.update()
    pygame.time.wait(10)
pygame.quit()

第6阶段:添加僵尸移动与碰撞攻击植物

功能目标:

①僵尸向左移动

②子弹击中僵尸后造成伤害

③僵尸撞到植物会停止移动并持续“啃咬”

④植物血量为0后死亡,地图可重新种植

import pygame
import random
# 游戏基本设置
WIDTH, HEIGHT = 800, 560
TILE_SIZE = 80
money = 200
pygame.init()
window = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("植物大战僵尸 - 第6阶段:僵尸碰撞与攻击")
# 加载图片资源
sunflower_img = pygame.image.load("imgs/sunflower.png")
peashooter_img = pygame.image.load("imgs/peashooter.png")
bullet_img = pygame.image.load("imgs/peabullet.png")
zombie_img = pygame.image.load("imgs/zombie.png")
# 类定义
class Plant:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.hp = 100
        self.rect = pygame.Rect(x, y, TILE_SIZE, TILE_SIZE)
class Sunflower(Plant):
    def __init__(self, x, y):
        super().__init__(x, y)
        self.counter = 0
    def display(self):
        window.blit(sunflower_img, (self.x, self.y))
    def produce_money(self):
        global money
        self.counter += 1
        if self.counter >= 25:
            money += 5
            self.counter = 0
class PeaShooter(Plant):
    def __init__(self, x, y):
        super().__init__(x, y)
        self.shot_timer = 0
    def display(self):
        window.blit(peashooter_img, (self.x, self.y))
    def shoot(self):
        self.shot_timer += 1
        if self.shot_timer >= 25:
            bullets.append(Bullet(self.x + 60, self.y + 20, self.y))
            self.shot_timer = 0
class Bullet:
    def __init__(self, x, y, row_y):
        self.x = x
        self.y = y
        self.row_y = row_y
        self.speed = 10
        self.damage = 50
        self.live = True
        self.rect = pygame.Rect(x, y, 24, 24)
    def move(self):
        self.x += self.speed
        self.rect.x = self.x
        if self.x > WIDTH:
            self.live = False
    def display(self):
        window.blit(bullet_img, (self.x, self.y))
    def check_hit(self):
        for zombie in zombies:
            if self.rect.colliderect(zombie.rect) and zombie.y == self.row_y:
                zombie.hp -= self.damage
                self.live = False
                if zombie.hp <= 0:
                    zombie.live = False
class Zombie:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.hp = 300
        self.speed = 1
        self.live = True
        self.stop = False
        self.rect = pygame.Rect(x, y, 80, 80)
    def display(self):
        window.blit(zombie_img, (self.x, self.y))
    def move(self):
        if not self.stop:
            self.x -= self.speed
            self.rect.x = self.x
    def check_plant(self):
        for plant in plants:
            if self.rect.colliderect(plant.rect) and plant.y == self.y:
                self.stop = True
                plant.hp -= 1
                if plant.hp <= 0:
                    plants.remove(plant)
                    self.stop = False
# 游戏数据结构
plants = []
bullets = []
zombies = [Zombie(800 + i * 150, random.randint(1, 6) * TILE_SIZE) for i in range(4)]
# 地图绘制
def draw_map():
    for row in range(1, 7):
        for col in range(10):
            rect = pygame.Rect(col * TILE_SIZE, row * TILE_SIZE, TILE_SIZE, TILE_SIZE)
            color = (192, 255, 192) if (row + col) % 2 == 0 else (160, 220, 160)
            pygame.draw.rect(window, color, rect)
# 文本函数
def draw_text(text, x, y, color=(255, 0, 0), size=24):
    font = pygame.font.SysFont("kaiti", size)
    surface = font.render(text, True, color)
    window.blit(surface, (x, y))
# 主循环
running = True
while running:
    window.fill((255, 255, 255))
    draw_map()
    # 植物处理
    for plant in plants[:]:
        plant.display()
        if isinstance(plant, Sunflower):
            plant.produce_money()
        elif isinstance(plant, PeaShooter):
            plant.shoot()
    # 子弹处理
    for bullet in bullets[:]:
        if bullet.live:
            bullet.move()
            bullet.check_hit()
            bullet.display()
        else:
            bullets.remove(bullet)
    # 僵尸处理
    for zombie in zombies[:]:
        if zombie.live:
            zombie.move()
            zombie.check_plant()
            zombie.display()
        else:
            zombies.remove(zombie)
    draw_text(f"当前金钱: {money}", 500, 30)
    draw_text("左键:向日葵;右键:豌豆射手(自动射击)", 10, 10, (0, 0, 200), 20)
    # 事件处理
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.MOUSEBUTTONDOWN:
            x = event.pos[0] // TILE_SIZE * TILE_SIZE
            y = event.pos[1] // TILE_SIZE * TILE_SIZE
            if y >= TILE_SIZE:
                if event.button == 1 and money >= 50:
                    plants.append(Sunflower(x, y))
                    money -= 50
                elif event.button == 3 and money >= 50:
                    plants.append(PeaShooter(x, y))
                    money -= 50
    pygame.display.update()
    pygame.time.wait(10)
pygame.quit()

第7阶段:加入积分、关卡提升与胜负机制

功能目标:

①击败僵尸 +20 分

②达到设定分数自动进入下一关,僵尸生成速度加快

③僵尸穿越左边界则游戏失败,显示“游戏结束”

import pygame
import random
# 游戏基本设置
WIDTH, HEIGHT = 800, 560
TILE_SIZE = 80
money = 200
pygame.init()
window = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("植物大战僵尸 - 第6阶段:僵尸碰撞与攻击")
# 加载图片资源
sunflower_img = pygame.image.load("imgs/sunflower.png")
peashooter_img = pygame.image.load("imgs/peashooter.png")
bullet_img = pygame.image.load("imgs/peabullet.png")
zombie_img = pygame.image.load("imgs/zombie.png")
# 类定义
class Plant:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.hp = 100
        self.rect = pygame.Rect(x, y, TILE_SIZE, TILE_SIZE)
class Sunflower(Plant):
    def __init__(self, x, y):
        super().__init__(x, y)
        self.counter = 0
    def display(self):
        window.blit(sunflower_img, (self.x, self.y))
    def produce_money(self):
        global money
        self.counter += 1
        if self.counter >= 25:
            money += 5
            self.counter = 0
class PeaShooter(Plant):
    def __init__(self, x, y):
        super().__init__(x, y)
        self.shot_timer = 0
    def display(self):
        window.blit(peashooter_img, (self.x, self.y))
    def shoot(self):
        self.shot_timer += 1
        if self.shot_timer >= 25:
            bullets.append(Bullet(self.x + 60, self.y + 20, self.y))
            self.shot_timer = 0
class Bullet:
    def __init__(self, x, y, row_y):
        self.x = x
        self.y = y
        self.row_y = row_y
        self.speed = 10
        self.damage = 50
        self.live = True
        self.rect = pygame.Rect(x, y, 24, 24)
    def move(self):
        self.x += self.speed
        self.rect.x = self.x
        if self.x > WIDTH:
            self.live = False
    def display(self):
        window.blit(bullet_img, (self.x, self.y))
    def check_hit(self):
        for zombie in zombies:
            if self.rect.colliderect(zombie.rect) and zombie.y == self.row_y:
                zombie.hp -= self.damage
                self.live = False
                if zombie.hp <= 0:
                    zombie.live = False
class Zombie:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.hp = 300
        self.speed = 1
        self.live = True
        self.stop = False
        self.rect = pygame.Rect(x, y, 80, 80)
    def display(self):
        window.blit(zombie_img, (self.x, self.y))
    def move(self):
        if not self.stop:
            self.x -= self.speed
            self.rect.x = self.x
    def check_plant(self):
        for plant in plants:
            if self.rect.colliderect(plant.rect) and plant.y == self.y:
                self.stop = True
                plant.hp -= 1
                if plant.hp <= 0:
                    plants.remove(plant)
                    self.stop = False
# 游戏数据结构
plants = []
bullets = []
zombies = [Zombie(800 + i * 150, random.randint(1, 6) * TILE_SIZE) for i in range(4)]
# 地图绘制
def draw_map():
    for row in range(1, 7):
        for col in range(10):
            rect = pygame.Rect(col * TILE_SIZE, row * TILE_SIZE, TILE_SIZE, TILE_SIZE)
            color = (192, 255, 192) if (row + col) % 2 == 0 else (160, 220, 160)
            pygame.draw.rect(window, color, rect)
# 文本函数
def draw_text(text, x, y, color=(255, 0, 0), size=24):
    font = pygame.font.SysFont("kaiti", size)
    surface = font.render(text, True, color)
    window.blit(surface, (x, y))
# 主循环
running = True
while running:
    window.fill((255, 255, 255))
    draw_map()
    # 植物处理
    for plant in plants[:]:
        plant.display()
        if isinstance(plant, Sunflower):
            plant.produce_money()
        elif isinstance(plant, PeaShooter):
            plant.shoot()
    # 子弹处理
    for bullet in bullets[:]:
        if bullet.live:
            bullet.move()
            bullet.check_hit()
            bullet.display()
        else:
            bullets.remove(bullet)
    # 僵尸处理
    for zombie in zombies[:]:
        if zombie.live:
            zombie.move()
            zombie.check_plant()
            zombie.display()
        else:
            zombies.remove(zombie)
    draw_text(f"当前金钱: {money}", 500, 30)
    draw_text("左键:向日葵;右键:豌豆射手(自动射击)", 10, 10, (0, 0, 200), 20)
    # 事件处理
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.MOUSEBUTTONDOWN:
            x = event.pos[0] // TILE_SIZE * TILE_SIZE
            y = event.pos[1] // TILE_SIZE * TILE_SIZE
            if y >= TILE_SIZE:
                if event.button == 1 and money >= 50:
                    plants.append(Sunflower(x, y))
                    money -= 50
                elif event.button == 3 and money >= 50:
                    plants.append(PeaShooter(x, y))
                    money -= 50
    pygame.display.update()
    pygame.time.wait(10)
pygame.quit()

恭喜你已经完成了一个完整的闯关版植物大战僵尸游戏雏形

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注