效果图

项目结构

实现思路
下面是代码的实现思路:
导入必要的库和模块: 首先导入 Python 的
os和time库以及pygame库,用于处理游戏的各种操作。同时,导入植物大战僵尸游戏中使用的各种植物和僵尸类。初始化游戏和加载资源: 初始化
Pygame库,并设置游戏的背景尺寸。随后,加载所需的图像资源,包括游戏背景、植物、僵尸等。定义游戏元素和变量: 设置一些全局变量,如阳光值、植物组、子弹组、僵尸组和阳光组。定义特殊事件,用于游戏中生成新的植物、子弹、僵尸和阳光。
编写游戏主循环: 在
main()函数中实现游戏的主循环。主循环负责更新植物、子弹和僵尸的位置和状态,绘制这些元素在屏幕上,并响应用户的鼠标和键盘事件,如选择和放置植物、收集阳光等。此外,更新游戏界面并检查游戏结束条件。处理游戏事件: 使用
pygame.event.get()函数捕获并处理游戏事件。根据用户的点击事件,判断是否点击了植物种子的图标,并据此设置选定的植物类型;若点击游戏区域,则根据已选择的植物类型放置植物。更新游戏状态和界面: 在主循环中,根据游戏进程更新状态,如调整阳光值、增加僵尸数量等,并刷新游戏界面,如重绘阳光值、植物和僵尸等。
检查游戏结束条件: 游戏继续进行直到检查到结束条件,如僵尸达到终点标志游戏失败,或者僵尸数量超过特定值则玩家胜利。
启动游戏: 在
if __name__ == '__main__':代码块中调用main()函数,以启动游戏。
pvz/main.py
import osimport timeimport pygamefrom pvz.plant.Peashooter import Peashooterfrom pvz.plant.SunFlower import SunFlowerfrom pvz.plant.WallNut import WallNutfrom pvz.plant.Sun import Sunfrom pvz.plant.Sun2 import Sun2from pvz.plant.JXC import JXCfrom pvz.plant.Bullet import Bulletfrom pvz.plant.BulletJXC import BulletJXCfrom pvz.zombie.Zombie import Zombiefrom pvz.zombie.ZombieLz import ZombieLz# 初始化pygame库pygame.init()# 设置游戏背景尺寸,所有的资源图片都是基于这个尺寸制作的,不建议修改background_size = (820, 560)# 创建游戏窗口并设置标题screen = pygame.display.set_mode(background_size)pygame.display.set_caption("植物大战僵尸")# 获取当前工作目录base_path = os.getcwd()# 加载背景图片bg_img_obj = pygame.image.load(os.path.join(base_path, 'images/a3.png')).convert_alpha()# 加载植物图片sunFlowerImg = pygame.image.load(os.path.join(base_path, 'images/SunFlower/SunFlower_00.png')).convert_alpha()wallNutImg = pygame.image.load(os.path.join(base_path, 'images/WallNut/wall_nut_00.png')).convert_alpha()peaShooterImg = pygame.image.load(os.path.join(base_path, 'images/Peashooter/Peashooter00.png')).convert_alpha()jxcImg = pygame.image.load(os.path.join(base_path, 'images/jxc/JXC00.png')).convert_alpha()# 加载阳光储蓄罐和种子图片sun_back_img = pygame.image.load(os.path.join(base_path, 'images/SeedBank01.png')).convert_alpha()sunflower_seed = pygame.image.load(os.path.join(base_path, 'images/SunFlower_kp.png'))wall_nut_seed = pygame.image.load(os.path.join(base_path, 'images/Wallnut_kp.png'))peashooter_seed = pygame.image.load(os.path.join(base_path, 'images/Peashooter_kp.png'))jxc_seed = pygame.image.load(os.path.join(base_path, 'images/jxc_kp.png'))# 初始化阳光值为100text = "1000"# 设置阳光值字体和颜色sun_font = pygame.font.SysFont("黑体", 25)sun_num_surface = sun_font.render(str(text), True, (0, 0, 0))# 创建植物组、子弹组、僵尸组和阳光组spriteGroup = pygame.sprite.Group()bulletGroup = pygame.sprite.Group()zombieGroup = pygame.sprite.Group()sun_sprite = pygame.sprite.Group()# 定义游戏时钟和特殊事件clock = pygame.time.Clock()GEN_SUN_EVENT = pygame.USEREVENT + 1 # 生成阳光事件pygame.time.set_timer(GEN_SUN_EVENT, 2000) # 每2秒生成一次阳光GEN_BULLET_EVENT = pygame.USEREVENT + 2 # 生成子弹事件pygame.time.set_timer(GEN_BULLET_EVENT, 2000) # 每2秒生成一次子弹GEN_ZOMBIE_EVENT = pygame.USEREVENT + 3 # 生成僵尸事件pygame.time.set_timer(GEN_ZOMBIE_EVENT, 10000) # 每10秒生成一次僵尸GEN_SUN2_EVENT = pygame.USEREVENT + 4 # 生成双倍阳光事件pygame.time.set_timer(GEN_SUN2_EVENT, 20000) # 每20秒生成一次双倍阳光# 初始化选择的植物类型和僵尸数量choose = 0zombie_num = 0def main():"""游戏主函数,包含游戏主循环"""global zombie_num # 僵尸数量全局变量global choose # 选择的植物类型全局变量global text # 阳光值全局变量global sun_num_surface # 阳光值显示表面全局变量running = True # 游戏是否运行标志index = 0 # 用于植物、子弹和僵尸的更新和绘制的索引while running:# 控制游戏帧率clock.tick(20)# 检查子弹和僵尸的碰撞,如果碰撞则减少僵尸的能量并移除子弹for bullet in bulletGroup:for zombie in zombieGroup:if pygame.sprite.collide_mask(bullet, zombie):if isinstance(bullet, BulletJXC): # 如果是坚果的子弹,则减少2点能量zombie.energy -= 2bulletGroup.remove(bullet)else: # 否则减少1点能量zombie.energy -= 1bulletGroup.remove(bullet)# 检查植物和僵尸的碰撞,如果碰撞则设置僵尸的GO标志为True,并将僵尸添加到植物的zombies列表中for sprite in spriteGroup:for zombie in zombieGroup:if pygame.sprite.collide_mask(sprite, zombie):zombie.GO = Truesprite.zombies.add(zombie)# 如果植物是坚果,则检查僵尸是否在攻击范围内,如果是则设置植物的攻击标志为True,并生成子弹if isinstance(sprite, JXC):if abs(zombie.rect.top - sprite.rect[1]) <= 40 and zombie.rect.left < 760:sprite.attack = Trueif sprite.att == 11:bullet_jxc = BulletJXC(sprite.rect, background_size, zombie.rect[0])bulletGroup.add(bullet_jxc)break# 在屏幕上绘制背景、阳光储蓄罐、阳光值和种子图片screen.blit(bg_img_obj, (0, 0))screen.blit(sun_back_img, (20, 0.5))screen.blit(sun_num_surface, (35, 50))screen.blit(sunflower_seed, (80, 5))screen.blit(peashooter_seed, (121, 5))screen.blit(wall_nut_seed, (162, 5))screen.blit(jxc_seed, (203, 5))# 更新和绘制植物、子弹、僵尸和阳光spriteGroup.update(index)spriteGroup.draw(screen)bulletGroup.update(index)bulletGroup.draw(screen)zombieGroup.update(index)zombieGroup.draw(screen)sun_sprite.update(index)sun_sprite.draw(screen)# 获取鼠标位置,并在鼠标位置上绘制选择的植物预览图(x, y) = pygame.mouse.get_pos()if choose == 1:screen.blit(sunFlowerImg, (x - sunFlowerImg.get_rect().width // 2, y - sunFlowerImg.get_rect().height // 2))if choose == 2:screen.blit(peaShooterImg,(x - peaShooterImg.get_rect().width // 2, y - peaShooterImg.get_rect().height // 2))if choose == 3:screen.blit(wallNutImg, (x - wallNutImg.get_rect().width // 2, y - wallNutImg.get_rect().height // 2))if choose == 4:screen.blit(jxcImg, (x - jxcImg.get_rect().width // 2, y - jxcImg.get_rect().height // 2))# 增加索引值index += 1# 处理pygame事件for event in pygame.event.get():# 处理生成双倍阳光事件if event.type == GEN_SUN2_EVENT:sun2 = Sun2()sun_sprite.add(sun2)# 处理生成僵尸事件if event.type == GEN_ZOMBIE_EVENT:zombie_num += 1zombie = Zombie()zombie_lz = ZombieLz()if 0 < zombie_num <= 15:zombieGroup.add(zombie)if zombie_num > 7:zombieGroup.add(zombie_lz)# 处理生成阳光事件if event.type == GEN_SUN_EVENT:for sprite in spriteGroup:if isinstance(sprite, SunFlower):now = time.time()if now - sprite.last_time >= 10: # 如果距离上次生成阳光的时间大于等于10秒,则生成阳光sun = Sun(sprite.rect)sun_sprite.add(sun)sprite.last_time = now# 处理生成子弹事件if event.type == GEN_BULLET_EVENT:for sprite in spriteGroup:for zombie in zombieGroup:if isinstance(sprite, Peashooter) \and 0 < sprite.rect[1] - zombie.rect[1] < 50 \and zombie.rect[0] < 760:bullet = Bullet(sprite.rect, background_size)bulletGroup.add(bullet)break# 处理退出游戏事件if event.type == pygame.QUIT:running = False# 处理鼠标点击事件if event.type == pygame.MOUSEBUTTONDOWN:pressed_key = pygame.mouse.get_pressed()if pressed_key[0]:pos = pygame.mouse.get_pos()x, y = pos# 如果点击了种子按钮,则设置选择的植物类型if 80 <= x < 121 and 5 <= y <= 63 and int(text) >= 50:choose = 1elif 121 <= x < 162 and 5 <= y <= 63 and int(text) >= 100:choose = 2elif 162 <= x < 203 and 5 <= y <= 63 and int(text) >= 50:choose = 3elif 203 <= x < 244 and 5 <= y <= 63 and int(text) >= 100:choose = 4# 如果点击了游戏区域,则根据选择的植物类型放置植物elif 36 < x < 800 and 70 < y < 550:if choose == 1:true_x = x // 90 * 85 + 35 # 计算植物的左上角坐标true_y = y // 100 * 95 - 15can_hold = True # 是否可以放置植物标志for sprite in spriteGroup:if sprite.rect.left == true_x and sprite.rect.top == true_y:can_hold = Falsebreakif not can_hold or true_y < 25:breaksunflower = SunFlower(time.time(), (true_x, true_y)) # 创建向日葵实例spriteGroup.add(sunflower) # 将向日葵添加到植物组中choose = 0 # 重置选择的植物类型text = int(text) # 将阳光值转换为整数text -= 50 # 减少50阳光my_font = pygame.font.SysFont("黑体", 25) # 设置字体sun_num_surface = my_font.render(str(text), True, (0, 0, 0)) # 更新阳光值显示表面if choose == 2:true_x = x // 90 * 85 + 32true_y = y // 100 * 95 - 18can_hold = Truefor sprite in spriteGroup:if sprite.rect.left == true_x and sprite.rect.top == true_y:can_hold = Falsebreakif not can_hold or true_y < 25:breakpeashooter = Peashooter((true_x, true_y)) # 创建豌豆射手实例spriteGroup.add(peashooter) # 将豌豆射手添加到植物组中choose = 0text = int(text)text -= 100 # 减少100阳光my_font = pygame.font.SysFont("黑体", 25)sun_num_surface = my_font.render(str(text), True, (0, 0, 0))if choose == 3:true_x = x // 90 * 85 + 35true_y = y // 100 * 95 - 15can_hold = Truefor sprite in spriteGroup:if sprite.rect.left == true_x and sprite.rect.top == true_y:can_hold = Falsebreakif not can_hold or true_y < 25:breakwall_nut = WallNut((true_x, true_y)) # 创建坚果实例spriteGroup.add(wall_nut) # 将坚果添加到植物组中choose = 0text = int(text)text -= 50 # 减少50阳光my_font = pygame.font.SysFont("黑体", 25)sun_num_surface = my_font.render(str(text), True, (0, 0, 0))if choose == 4:true_x = x // 90 * 85 + 22true_y = y // 100 * 95 - 35can_hold = Truefor sprite in spriteGroup:if sprite.rect.left == true_x and sprite.rect.top == true_y:can_hold = Falsebreakif not can_hold or true_y < 25:breakjxc = JXC((true_x, true_y)) # 创建坚果墙实例spriteGroup.add(jxc) # 将坚果墙添加到植物组中choose = 0text = int(text)text -= 100 # 减少100阳光my_font = pygame.font.SysFont("黑体", 25)sun_num_surface = my_font.render(str(text), True, (0, 0, 0))# 如果点击了阳光,则收集阳光并更新阳光值显示表面for sun in sun_sprite:if sun.rect.collidepoint(pos):sun_sprite.remove(sun)text = str(int(text) + 25)sun_font = pygame.font.SysFont("黑体", 25)sun_num_surface = sun_font.render(str(text), True, (0, 0, 0))# 检查僵尸是否到达终点或游戏是否胜利for zombie in zombieGroup:if zombie.rect.left == -120: # 如果僵尸到达终点,则游戏失败print("你的脑子被僵尸吃了")running = Falseif zombie_num > 20: # 如果僵尸数量大于20,则游戏胜利print("胜利")running = False# 更新游戏界面pygame.display.update()if __name__ == '__main__':main()


