【Python小游戏】植物大战僵尸的实现与源码分享

avatar
作者
猴君
阅读量:1

文章目录

    • Python版植物大战僵尸
    • 环境要求
    • 方法
    • 源码分享
      • 初始化页面(部分)
      • 地图搭建(部分)
      • 定义植物类 (部分)
      • 定义僵尸类(部分)
      • 游戏运行入口
    • 游戏源码获取

Python版植物大战僵尸

  • 已有的植物:向日葵,豌豆射手,坚果墙,寒冰射手,樱桃炸弹,双发射手,三线射手,大嘴花,小喷菇,土豆雷,地刺,胆小菇,倭瓜,火爆辣椒,阳光菇,寒冰菇,魅惑菇,火炬树桩,睡莲,杨桃,咖啡豆,海蘑菇,高坚果,缠绕水草,毁灭菇,墓碑吞噬者,大喷菇,大蒜,南瓜头
  • 已有的僵尸:普通僵尸,旗帜僵尸,路障僵尸,铁桶僵尸,读报僵尸,橄榄球僵尸,鸭子救生圈僵尸,铁门僵尸,撑杆跳僵尸,冰车僵尸,潜水僵尸
  • 支持选择植物卡片
  • 支持白昼模式,夜晚模式,泳池模式,浓雾模式(暂时没有加入雾),传送带模式和坚果保龄球模式
  • 支持背景音乐播放
    • 支持调节音量
  • 支持音效
    • 支持与背景音乐一起调节音量
  • 支持全屏模式
    • F键进入全屏模式,按U键恢复至窗口模式
  • 支持用小铲子移除植物
  • 支持分波生成僵尸
  • 支持“关卡进程”进度条显示
  • 夜晚模式支持墓碑以及从墓碑生成僵尸
  • 含有泳池的模式支持在最后一波时从泳池中自动冒出僵尸

环境要求

  • Python3 (建议 >= 3.10,最好使用最新版)
  • Python-Pygame (建议 >= 2.0,最好使用最新版)

方法

  • 使用鼠标收集阳光,种植植物

源码分享

在这里插入图片描述

初始化页面(部分)

import os import pygame as pg  # 用户数据及日志存储路径 if os.name == "nt": # Windows系统存储路径     USERDATA_PATH = os.path.expandvars(os.path.join("%APPDATA%", "pypvz", "userdata.json"))     USERLOG_PATH = os.path.expandvars(os.path.join("%APPDATA%", "pypvz", "run.log")) else:   # 非Windows系统存储路径     USERDATA_PATH = os.path.expanduser(os.path.join("~", ".config", "pypvz", "userdata.json"))     USERLOG_PATH = os.path.expanduser(os.path.join("~", ".config", "pypvz", "run.log"))  # 游戏图片资源路径 PATH_IMG_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "resources", "graphics") # 游戏音乐文件夹路径 PATH_MUSIC_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "resources","music") # 窗口图标 ORIGINAL_LOGO = os.path.join(os.path.dirname(os.path.dirname(__file__)), "pypvz-exec-logo.png") # 字体路径 FONT_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), "resources", "DroidSansFallback.ttf")  # 窗口标题 ORIGINAL_CAPTION = "pypvz"  # 游戏模式 GAME_MODE = "mode" MODE_ADVENTURE = "adventure" MODE_LITTLEGAME = "littleGame"  # 窗口大小 SCREEN_WIDTH = 800 SCREEN_HEIGHT = 600 SCREEN_SIZE = (SCREEN_WIDTH, SCREEN_HEIGHT)   # 选卡数量 # 最大数量 CARD_MAX_NUM = 10   # 这里以后可以增加解锁功能,从最初的6格逐渐解锁到10格 # 最小数量 CARD_LIST_NUM = CARD_MAX_NUM  # 方格数据 # 一般 GRID_X_LEN = 9 GRID_Y_LEN = 5 GRID_X_SIZE = 80 GRID_Y_SIZE = 100 # 带有泳池 GRID_POOL_X_LEN = GRID_X_LEN GRID_POOL_Y_LEN = 6 GRID_POOL_X_SIZE = GRID_X_SIZE GRID_POOL_Y_SIZE = 85 # 屋顶 GRID_ROOF_X_LEN = GRID_X_LEN GRID_ROOF_Y_LEN = GRID_Y_LEN GRID_ROOF_X_SIZE = GRID_X_SIZE GRID_ROOF_Y_SIZE = 85  # 颜色 WHITE        = (255, 255, 255) NAVYBLUE     = ( 60,  60, 100) SKY_BLUE     = ( 39, 145, 251) BLACK        = (  0,   0,   0) LIGHTYELLOW  = (234, 233, 171) RED          = (255,   0,   0) PURPLE       = (255,   0, 255) GOLD         = (255, 215,   0) GREEN        = (  0, 255,   0) YELLOWGREEN  = ( 55, 200,   0) LIGHTGRAY    = (107, 108, 145) PARCHMENT_YELLOW = (207, 146, 83)  # 退出游戏按钮 EXIT = "exit" HELP = "help" # 游戏界面可选的菜单 LITTLE_MENU = "littleMenu" BIG_MENU = "bigMenu" RESTART_BUTTON = "restartButton" MAINMENU_BUTTON = "mainMenuButton" LITTLEGAME_BUTTON = "littleGameButton" OPTION_BUTTON = "optionButton" SOUND_VOLUME_BUTTON = "volumeButton" UNIVERSAL_BUTTON = "universalButton" # 金银向日葵奖杯 TROPHY_SUNFLOWER = "sunflowerTrophy" # 小铲子 SHOVEL = "shovel" SHOVEL_BOX = "shovelBox" # 一大波僵尸来袭图片 HUGE_WAVE_APPROCHING = "Approching" # 关卡进程图片 LEVEL_PROGRESS_BAR = "LevelProgressBar" LEVEL_PROGRESS_ZOMBIE_HEAD = "LevelProgressZombieHead" LEVEL_PROGRESS_FLAG = "LevelProgressFlag" 

地图搭建(部分)

class Map():     def __init__(self, background_type:int):         self.background_type = background_type         # 注意:从0开始编号         if self.background_type in c.POOL_EQUIPPED_BACKGROUNDS:             self.width = c.GRID_POOL_X_LEN             self.height = c.GRID_POOL_Y_LEN             self.grid_height_size = c.GRID_POOL_Y_SIZE             self.map =  [   [self.initMapGrid(c.MAP_WATER) if 2 <= y <= 3                                 else self.initMapGrid(c.MAP_GRASS)                             for x in range(self.width)                             ]                         for y in range(self.height)                         ]         elif self.background_type in c.ON_ROOF_BACKGROUNDS:             self.width = c.GRID_ROOF_X_LEN             self.height = c.GRID_ROOF_Y_LEN             self.grid_height_size = c.GRID_ROOF_Y_SIZE             self.map =  [   [self.initMapGrid(c.MAP_TILE)                             for x in range(self.width)                             ]                         for y in range(self.height)                         ]         elif self.background_type == c.BACKGROUND_SINGLE:             self.width = c.GRID_X_LEN             self.height = c.GRID_Y_LEN             self.grid_height_size = c.GRID_Y_SIZE             self.map =  [   [self.initMapGrid(c.MAP_GRASS) if y ==2                                 else self.initMapGrid(c.MAP_UNAVAILABLE)                             for x in range(self.width)                             ]                         for y in range(self.height)                         ]         elif self.background_type == c.BACKGROUND_TRIPLE:             self.width = c.GRID_X_LEN             self.height = c.GRID_Y_LEN             self.grid_height_size = c.GRID_Y_SIZE             self.map =  [   [self.initMapGrid(c.MAP_GRASS) if 1 <= y <= 3                                 else self.initMapGrid(c.MAP_UNAVAILABLE)                             for x in range(self.width)                             ]                         for y in range(self.height)                         ]         else:             self.width = c.GRID_X_LEN             self.height = c.GRID_Y_LEN             self.grid_height_size = c.GRID_Y_SIZE             self.map =  [   [self.initMapGrid(c.MAP_GRASS)                             for x in range(self.width)                             ]                         for y in range(self.height)                         ]      def isValid(self, map_x:int, map_y:int) -> bool:         if ((0 <= map_x < self.width)         and (0 <= map_y < self.height)):             return True         return False      # 地图单元格状态     # 注意是可变对象,不能直接引用     # 由于同一格显然不可能种两个相同的植物,所以用集合     def initMapGrid(self, plot_type:str) -> set:         return {c.MAP_PLANT:set(), c.MAP_SLEEP:False, c.MAP_PLOT_TYPE:plot_type}      # 判断位置是否可用     # 暂时没有写紫卡植物的判断方法     # 由于紫卡植物需要移除以前的植物,所以可用另外定义一个函数     def isAvailable(self, map_x:int, map_y:int, plant_name:str) -> bool:         # 咖啡豆和墓碑吞噬者的判别最为特殊         if plant_name == c.COFFEEBEAN:             if (self.map[map_y][map_x][c.MAP_SLEEP]             and (plant_name not in self.map[map_y][map_x][c.MAP_PLANT])):                 return True             else:                 return False         if plant_name == c.GRAVEBUSTER:             if (c.GRAVE in self.map[map_y][map_x][c.MAP_PLANT]             and (plant_name not in self.map[map_y][map_x][c.MAP_PLANT])):                 return True             else:                 return False         # 被非植物障碍占据的格子对于一般植物不可种植         if any((i in c.NON_PLANT_OBJECTS) for i in self.map[map_y][map_x][c.MAP_PLANT]):             return False         if self.map[map_y][map_x][c.MAP_PLOT_TYPE] == c.MAP_GRASS:  # 草地             # 首先需要判断植物是否是水生植物,水生植物不能种植在陆地上             if plant_name not in c.WATER_PLANTS:                 if not self.map[map_y][map_x][c.MAP_PLANT]: # 没有植物肯定可以种植                     return True                 elif (all((i in {"花盆(未实现)", c.PUMPKINHEAD}) for i in self.map[map_y][map_x][c.MAP_PLANT])                 and (plant_name not in self.map[map_y][map_x][c.MAP_PLANT])): # 例外植物:集合中填花盆和南瓜头,只要这里没有这种植物就能种植                     return True                 elif ((plant_name == c.PUMPKINHEAD)                 and (c.PUMPKINHEAD not in self.map[map_y][map_x][c.MAP_PLANT])):   # 没有南瓜头就能种南瓜头                     return True                 else:                     return False             else:                 return False         elif self.map[map_y][map_x][c.MAP_PLOT_TYPE] == c.MAP_TILE: # 屋顶             # 首先需要判断植物是否是水生植物,水生植物不能种植在陆地上             if plant_name not in c.WATER_PLANTS:                 if "花盆(未实现)" in self.map[map_y][map_x][c.MAP_PLANT]:                     if (all((i in {"花盆(未实现)", c.PUMPKINHEAD}) for i in self.map[map_y][map_x][c.MAP_PLANT])                     and (plant_name not in self.map[map_y][map_x][c.MAP_PLANT])): # 例外植物:集合中填花盆和南瓜头,只要这里没有这种植物就能种植                         if plant_name in {c.SPIKEWEED}: # 不能在花盆上种植的植物                             return False                         else:                             return True                     elif ((plant_name == c.PUMPKINHEAD)                     and (c.PUMPKINHEAD not in self.map[map_y][map_x][c.MAP_PLANT])):    # 有花盆且没有南瓜头就能种南瓜头                         return True                     else:                         return False                 elif plant_name == "花盆(未实现)": # 这一格本来没有花盆而且新来的植物是花盆,可以种                     return True                 else:                     return False             else:                 return False         elif self.map[map_y][map_x][c.MAP_PLOT_TYPE] == c.MAP_WATER:   # 水里             if plant_name in c.WATER_PLANTS:   # 是水生植物                 if not self.map[map_y][map_x][c.MAP_PLANT]: # 只有无植物时才能在水里种植水生植物                     return True                 else:                     return False             else:   # 非水生植物,依赖睡莲                 if c.LILYPAD in self.map[map_y][map_x][c.MAP_PLANT]:                     if (all((i in {c.LILYPAD, c.PUMPKINHEAD}) for i in self.map[map_y][map_x][c.MAP_PLANT])                     and (plant_name not in self.map[map_y][map_x][c.MAP_PLANT])):                         if plant_name in {c.SPIKEWEED, c.POTATOMINE, "花盆(未实现)"}: # 不能在睡莲上种植的植物                             return False                         else:                             return True                     elif ((plant_name == c.PUMPKINHEAD)                     and (c.PUMPKINHEAD not in self.map[map_y][map_x][c.MAP_PLANT])):   # 在睡莲上且没有南瓜头就能种南瓜头                         return True                     else:                         return False                 else:                     return False         else:   # 不可种植区域             return False          def getMapIndex(self, x:int, y:int) -> tuple[int, int]:         if self.background_type in c.POOL_EQUIPPED_BACKGROUNDS:             x -= c.MAP_POOL_OFFSET_X             y -= c.MAP_POOL_OFFSET_Y             return (x // c.GRID_POOL_X_SIZE, y // c.GRID_POOL_Y_SIZE)         elif self.background_type in c.ON_ROOF_BACKGROUNDS:             x -= c.MAP_ROOF_OFFSET_X             y -= c.MAP_ROOF_OFFSET_X             grid_x = x // c.GRID_ROOF_X_SIZE             if grid_x >= 5:                 grid_y = y // c.GRID_ROOF_Y_SIZE             else:                 grid_y = (y - 20*(6 - grid_x)) // 85             return (grid_x, grid_y)         else:             x -= c.MAP_OFFSET_X             y -= c.MAP_OFFSET_Y             return (x // c.GRID_X_SIZE, y // c.GRID_Y_SIZE)          def getMapGridPos(self, map_x:int, map_y:int) -> tuple[int, int]:         if self.background_type in c.POOL_EQUIPPED_BACKGROUNDS:             return (map_x * c.GRID_POOL_X_SIZE + c.GRID_POOL_X_SIZE//2 + c.MAP_POOL_OFFSET_X,                     map_y * c.GRID_POOL_Y_SIZE + c.GRID_POOL_Y_SIZE//5 * 3 + c.MAP_POOL_OFFSET_Y)         elif self.background_type in c.ON_ROOF_BACKGROUNDS:             return (map_x * c.GRID_ROOF_X_SIZE + c.GRID_ROOF_X_SIZE//2 + c.MAP_ROOF_OFFSET_X,                     map_y * c.GRID_ROOF_Y_SIZE + 20 * max(0, (6 - map_y)) + c.GRID_ROOF_Y_SIZE//5 * 3 + c.MAP_POOL_OFFSET_Y)         else:             return (map_x * c.GRID_X_SIZE + c.GRID_X_SIZE//2 + c.MAP_OFFSET_X,                     map_y * c.GRID_Y_SIZE + c.GRID_Y_SIZE//5 * 3 + c.MAP_OFFSET_Y)          def setMapGridType(self, map_x:int, map_y:int, plot_type:str):         self.map[map_y][map_x][c.MAP_PLOT_TYPE] = plot_type      def addMapPlant(self, map_x:int, map_y:int, plant_name:int, sleep:bool=False):         self.map[map_y][map_x][c.MAP_PLANT].add(plant_name)         self.map[map_y][map_x][c.MAP_SLEEP] = sleep          def removeMapPlant(self, map_x:int, map_y:int, plant_name:str):         self.map[map_y][map_x][c.MAP_PLANT].discard(plant_name)      def getRandomMapIndex(self) -> tuple[int, int]:         map_x = random.randint(0, self.width-1)         map_y = random.randint(0, self.height-1)         return (map_x, map_y)      def checkPlantToSeed(self, x:int, y:int, plant_name:str) -> tuple[int, int]:         pos = None         map_x, map_y = self.getMapIndex(x, y)         if self.isValid(map_x, map_y) and self.isAvailable(map_x, map_y, plant_name):             pos = self.getMapGridPos(map_x, map_y)         return pos 

定义植物类 (部分)

在这里插入图片描述

# 豌豆及孢子类普通子弹 class Bullet(pg.sprite.Sprite):     def __init__(   self, x:int, start_y:int, dest_y:int, name:str, damage:int,                     effect:str=None, passed_torchwood_x:int=None,                     damage_type:str=c.ZOMBIE_DEAFULT_DAMAGE):         pg.sprite.Sprite.__init__(self)          self.name = name         self.frames = []         self.frame_index = 0         self.load_images()         self.frame_num = len(self.frames)         self.image = self.frames[self.frame_index]         self.mask = pg.mask.from_surface(self.image)         self.rect = self.image.get_rect()         self.rect.x = x         self.rect.y = start_y         self.dest_y = dest_y         self.y_vel = 15 if (dest_y > start_y) else -15         self.x_vel = 10         self.damage = damage         self.damage_type = damage_type         self.effect = effect         self.state = c.FLY         self.current_time = 0         self.animate_timer = 0         self.animate_interval = 70         self.passed_torchwood_x = passed_torchwood_x  # 记录最近通过的火炬树横坐标,如果没有缺省为None      def loadFrames(self, frames, name):         frame_list = tool.GFX[name]         if name in c.PLANT_RECT:             data = c.PLANT_RECT[name]             x, y, width, height = data["x"], data["y"], data["width"], data["height"]         else:             x, y = 0, 0             rect = frame_list[0].get_rect()             width, height = rect.w, rect.h          for frame in frame_list:             frames.append(tool.get_image(frame, x, y, width, height))      def load_images(self):         self.fly_frames = []         self.explode_frames = []          fly_name = self.name         if self.name in c.BULLET_INDEPENDENT_BOOM_IMG:             explode_name = f"{self.name}Explode"         else:             explode_name = "PeaNormalExplode"          self.loadFrames(self.fly_frames, fly_name)         self.loadFrames(self.explode_frames, explode_name)          self.frames = self.fly_frames      def update(self, game_info):         self.current_time = game_info[c.CURRENT_TIME]         if self.state == c.FLY:             if self.rect.y != self.dest_y:                 self.rect.y += self.y_vel                 if self.y_vel * (self.dest_y - self.rect.y) < 0:                     self.rect.y = self.dest_y             self.rect.x += self.x_vel             if self.rect.x >= c.SCREEN_WIDTH + 20:                 self.kill()         elif self.state == c.EXPLODE:             if (self.current_time - self.explode_timer) > 250:                 self.kill()         if self.current_time - self.animate_timer >= self.animate_interval:             self.frame_index += 1             self.animate_timer = self.current_time             if self.frame_index >= self.frame_num:                 self.frame_index = 0             self.image = self.frames[self.frame_index]      def setExplode(self):         self.state = c.EXPLODE         self.explode_timer = self.current_time         self.frames = self.explode_frames         self.frame_num = len(self.frames)         self.image = self.frames[0]         self.mask = pg.mask.from_surface(self.image)          # 播放子弹爆炸音效         if self.name == c.BULLET_FIREBALL:             c.SOUND_FIREPEA_EXPLODE.play()         else:             c.SOUND_BULLET_EXPLODE.play()      def draw(self, surface):         surface.blit(self.image, self.rect)  # 大喷菇的烟雾 # 仅有动画效果,不参与攻击运算 class Fume(pg.sprite.Sprite):     def __init__(self, x, y):         pg.sprite.Sprite.__init__(self)         self.name = c.FUME         self.timer = 0         self.frame_index = 0         self.load_images()         self.frame_num = len(self.frames)         self.image = self.frames[self.frame_index]         self.mask = pg.mask.from_surface(self.image)         self.rect = self.image.get_rect()         self.rect.x = x         self.rect.y = y      def load_images(self):         self.fly_frames = []          fly_name = self.name          self.loadFrames(self.fly_frames, fly_name)          self.frames = self.fly_frames      def draw(self, surface):         surface.blit(self.image, self.rect)      def update(self, game_info):         self.current_time = game_info[c.CURRENT_TIME]         if self.current_time - self.timer >= 100:             self.frame_index += 1             if self.frame_index >= self.frame_num:                 self.frame_index = self.frame_num - 1                 self.kill()             self.timer = self.current_time         self.image = self.frames[self.frame_index]      def loadFrames(self, frames, name):         frame_list = tool.GFX[name]         x, y = 0, 0         rect = frame_list[0].get_rect()         width, height = rect.w, rect.h          for frame in frame_list:             frames.append(tool.get_image(frame, x, y, width, height)) 

定义僵尸类(部分)

在这里插入图片描述

class Zombie(pg.sprite.Sprite):     def __init__(   self, x, y, name, head_group=None,                     helmet_health=0,                helmet_type2_health=0,                     body_health=c.NORMAL_HEALTH,    losthead_health=c.LOSTHEAD_HEALTH,                     damage=c.ZOMBIE_ATTACK_DAMAGE,  can_swim=False):         pg.sprite.Sprite.__init__(self)          self.name = name         self.frames = []         self.frame_index = 0         self.loadImages()         self.frame_num = len(self.frames)          self.image = self.frames[self.frame_index]         self.rect = self.image.get_rect()         self.mask = pg.mask.from_surface(self.image)         self.rect.x = x         self.rect.bottom = y         # 大蒜换行移动像素值,< 0时向上,= 0时不变,> 0时向上         self.target_y_change = 0         self.original_y = y         self.to_change_group = False          self.helmet_health = helmet_health         self.helmet_type2_health = helmet_type2_health         self.health = body_health + losthead_health         self.losthead_health = losthead_health         self.damage = damage         self.dead = False         self.losthead = False         self.can_swim = can_swim         self.swimming = False         self.helmet = (self.helmet_health > 0)         self.helmet_type2 = (self.helmet_type2_health > 0)         self.head_group = head_group          self.walk_timer = 0         self.animate_timer = 0         self.attack_timer = 0         self.state = c.WALK         self.animate_interval = 150         self.walk_animate_interval = 180         self.attack_animate_interval = 100         self.losthead_animate_interval = 180         self.die_animate_interval = 50         self.boomDie_animate_interval = 100         self.ice_slow_ratio = 1         self.ice_slow_timer = 0         self.hit_timer = 0         self.speed = 1         self.freeze_timer = 0         self.losthead_timer = 0         self.is_hypno = False  # the zombie is hypo and attack other zombies when it ate a HypnoShroom      def loadFrames(self, frames, name, colorkey=c.BLACK):         frame_list = tool.GFX[name]         rect = frame_list[0].get_rect()         width, height = rect.w, rect.h         if name in c.ZOMBIE_RECT:             data = c.ZOMBIE_RECT[name]             x, width = data["x"], data["width"]         else:             x = 0         for frame in frame_list:             frames.append(tool.get_image(frame, x, 0, width, height, colorkey))      def update(self, game_info):         self.current_time = game_info[c.CURRENT_TIME]         self.handleState()         self.updateIceSlow()         self.animation()      def handleState(self):         if self.state == c.WALK:             self.walking()         elif self.state == c.ATTACK:             self.attacking()         elif self.state == c.DIE:             self.dying()         elif self.state == c.FREEZE:             self.freezing()      # 濒死状态用函数     def checkToDie(self, framesKind):         if self.health <= 0:             self.setDie()             return True         elif self.health <= self.losthead_health:             if not self.losthead:                 self.changeFrames(framesKind)                 self.setLostHead()                 return True             else:                 self.health -= (self.current_time - self.losthead_timer) / 40                 self.losthead_timer = self.current_time                 return False         else:             return False      def walking(self):         if self.checkToDie(self.losthead_walk_frames):             return          # 能游泳的僵尸         if self.can_swim:             # 在水池范围内             # 在右侧岸左             if self.rect.right <= c.MAP_POOL_FRONT_X:                 # 在左侧岸右,左侧岸位置为预估                 if self.rect.right - 25 >= c.MAP_POOL_OFFSET_X:                     # 还未进入游泳状态                     if not self.swimming:                         self.swimming = True                         self.changeFrames(self.swim_frames)                         # 播放入水音效                         c.SOUND_ZOMBIE_ENTERING_WATER.play()                         # 同样没有兼容双防具                         if self.helmet:                             if self.helmet_health <= 0:                                 self.helmet = False                             else:                                 self.changeFrames(self.helmet_swim_frames)                         if self.helmet_type2:                             if self.helmet_type2_health <= 0:                                 self.helmet_type2 = False                             else:                                 self.changeFrames(self.helmet_swim_frames)                     # 已经进入游泳状态                     else:                         if self.helmet:                             if self.helmet_health <= 0:                                 self.changeFrames(self.swim_frames)                                 self.helmet = False                         if self.helmet_type2:                             if self.helmet_type2_health <= 0:                                 self.changeFrames(self.swim_frames)                                 self.helmet_type2 = False                 # 水生僵尸已经接近家门口并且上岸                 else:                     if self.swimming:                         self.changeFrames(self.walk_frames)                         self.swimming = False                         # 同样没有兼容双防具                         if self.helmet:                             if self.helmet_health <= 0:                                 self.helmet = False                             else:                                 self.changeFrames(self.helmet_walk_frames)                         if self.helmet_type2:                             if self.helmet_type2_health <= 0:                                 self.helmet_type2 = False                             else:                                 self.changeFrames(self.helmet_walk_frames)                     if self.helmet:                         if self.helmet_health <= 0:                             self.helmet = False                             self.changeFrames(self.walk_frames)                     if self.helmet_type2:                         if self.helmet_type2_health <= 0:                             self.helmet_type2 = False                             self.changeFrames(self.walk_frames)             elif self.is_hypno and self.rect.right > c.MAP_POOL_FRONT_X + 55:   # 常数拟合暂时缺乏检验                 if self.swimming:                     self.changeFrames(self.walk_frames)                 if self.helmet:                     if self.helmet_health <= 0:                         self.changeFrames(self.walk_frames)                         self.helmet = False                     elif self.swimming: # 游泳状态需要改为步行                         self.changeFrames(self.helmet_walk_frames)                 if self.helmet_type2:                     if self.helmet_type2_health <= 0:                         self.changeFrames(self.walk_frames)                         self.helmet_type2 = False                     elif self.swimming: # 游泳状态需要改为步行                         self.changeFrames(self.helmet_walk_frames)                 self.swimming = False             # 尚未进入水池             else:                 if self.helmet_health <= 0 and self.helmet:                     self.changeFrames(self.walk_frames)                     self.helmet = False                 if self.helmet_type2_health <= 0 and self.helmet_type2:                     self.changeFrames(self.walk_frames)                     self.helmet_type2 = False         # 不能游泳的一般僵尸         else:             if self.helmet_health <= 0 and self.helmet:                 self.changeFrames(self.walk_frames)                 self.helmet = False             if self.helmet_type2_health <= 0 and self.helmet_type2:                 self.changeFrames(self.walk_frames)                 self.helmet_type2 = False          if (self.current_time - self.walk_timer) > (c.ZOMBIE_WALK_INTERVAL * self.getTimeRatio()):             self.handleGarlicYChange()             self.walk_timer = self.current_time             if self.is_hypno:                 self.rect.x += 1             else:                 self.rect.x -= 1 

游戏运行入口

在这里插入图片描述

 #!/usr/bin/env python import logging import traceback import os import pygame as pg from logging.handlers import RotatingFileHandler # 由于在后续本地模块中存在对pygame的调用,在此处必须完成pygame的初始化 os.environ["SDL_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR"]="0" # 设置临时环境变量以避免Linux下禁用x11合成器 pg.init()  from source import tool from source import constants as c from source.state import mainmenu, screen, level  if __name__ == "__main__":     # 日志设置     if not os.path.exists(os.path.dirname(c.USERLOG_PATH)):         os.makedirs(os.path.dirname(c.USERLOG_PATH))     logger = logging.getLogger("main")     formatter = logging.Formatter("%(asctime)s - %(levelname)s: %(message)s")     fileHandler = RotatingFileHandler(c.USERLOG_PATH, "a", 1_000_000, 0, "utf-8")     # 设置日志文件权限,Unix为644,Windows为可读写;Python的os.chmod与Unix chmod相同,但要显式说明8进制     os.chmod(c.USERLOG_PATH, 0o644)     fileHandler.setFormatter(formatter)     streamHandler = logging.StreamHandler()     streamHandler.setFormatter(formatter)     logger.addHandler(fileHandler)     logger.addHandler(streamHandler)      try:         # 控制状态机运行         game = tool.Control()         state_dict = {  c.MAIN_MENU:    mainmenu.Menu(),                         c.GAME_VICTORY: screen.GameVictoryScreen(),                         c.GAME_LOSE:    screen.GameLoseScreen(),                         c.LEVEL:        level.Level(),                         c.AWARD_SCREEN: screen.AwardScreen(),                         c.HELP_SCREEN:  screen.HelpScreen(),                         }         game.setup_states(state_dict, c.MAIN_MENU)         game.run()     except:         print() # 将日志输出与上文内容分隔开,增加可读性         logger.error(f"\n{traceback.format_exc()}")   

游戏源码获取

关注vx公众号【苏凉闲谈社】回复“777””即可免费领取游戏源码,同时还为大家准备了相关图书资料、视频资料以及其他python小游戏源码等等。

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!