分结构教学内容
阶段 | 内容模块 | 可视化效果 |
前期准备 | 准备号对应的素材 | 显示植物、僵尸、子弹等 |
第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()
恭喜你已经完成了一个完整的闯关版植物大战僵尸游戏阿雏形!