zoukankan      html  css  js  c++  java
  • Python 植物大战僵尸代码实现: 图片加载和显示切换

    游戏介绍
    以前很火的植物大战僵尸游戏, 本想在网上找个python版本游戏学习下,无奈没有发现比较完整的,那就自己来写一个把。图片资源是从github上下载的,因为图片资源有限,只能实现几种植物和僵尸。
    功能实现如下:

    支持的植物类型:太阳花,豌豆射手,寒冰射手,坚果,樱桃炸弹。新增加植物:双重豌豆射手,三重豌豆射手。
    支持的僵尸类型:普通僵尸,棋子僵尸,路障僵尸,铁桶僵尸。
    使用json文件保存关卡信息,设置僵尸出现的时间和位置。
    新增加除草机。
    下面是游戏的截图:
    图1

    demo2

                                                                                                  图2

     

    图片显示切换


    从图1和图2可以看到,僵尸的行走和攻击时的图片显示会有不同,这篇文章讲下如何进行图片显示的切换。
    以上面的路障僵尸为例,一共有下面几种图片类型。

    • 带着路障行走
    • 带着路障攻击
    • 不带路障行走(即变成普通僵尸的行走)
    • 不带路障攻击(即变成普通僵尸的攻击)
    • 没有头的行走
    • 没有头的攻击
    • 死亡

    图3是路障僵尸的这7种图片类型的示例

    图片加载

    植物大战僵尸的图片资源比较特别,一种图片类型的每一个动作是一个单独的图片,如图4是路障僵尸带着路障攻击的动作图片,一共有11个图片,所以加载图片的代码要做对应的修改。

    完整代码

    游戏实现代码的github链接 植物大战僵尸
    这边是csdn的下载链接 植物大战僵尸

    图片加载
    在 source ool.py 中 load_all_gfx 函数遍历resourcesgraphics 目录和子目录。
    代码中做了一个简单的区分:

    • 如果在resourcesgraphicssubfolder 目录中是图片,那就是单独的一个图片,比如resourcesgraphicsScreen 目录中的界面图片
    • 如果在resourcesgraphicssubfolder 目录中是子目录,那这个子目录或子子目录中的所有图片都属于一个图片类型,比如resourcesgraphicsombiesConeheadZombieConeheadZombieAttack 目录下就是路障僵尸带着路障攻击的动作图片, 如图4所示。
     1 def load_all_gfx(directory, colorkey=c.WHITE, accept=('.png', '.jpg', '.bmp', '.gif')):
     2     graphics = {}
     3     for name1 in os.listdir(directory):
     4         # subfolders under the folder resourcesgraphics
     5         dir1 = os.path.join(directory, name1)
     6         if os.path.isdir(dir1):
     7             for name2 in os.listdir(dir1):
     8                 dir2 = os.path.join(dir1, name2)
     9                 if os.path.isdir(dir2):
    10                 # e.g. subfolders under the folder resourcesgraphicsombies
    11                     for name3 in os.listdir(dir2):
    12                         dir3 = os.path.join(dir2, name3)
    13                         # e.g. subfolders or pics under the folder resourcesgraphicsombiesConeheadZombie
    14                         if os.path.isdir(dir3):
    15                             # e.g. it's the folder resourcesgraphicsombiesConeheadZombieConeheadZombieAttack
    16                             image_name, _ = os.path.splitext(name3)
    17                             graphics[image_name] = load_image_frames(dir3, image_name, colorkey, accept)
    18                         else:
    19                             # e.g. pics under the folder resourcesgraphicsPlantsPeashooter
    20                             image_name, _ = os.path.splitext(name2)
    21                             graphics[image_name] = load_image_frames(dir2, image_name, colorkey, accept)
    22                             break
    23                 else:
    24                 # e.g. pics under the folder resourcesgraphicsScreen
    25                     name, ext = os.path.splitext(name2)
    26                     if ext.lower() in accept:
    27                         img = pg.image.load(dir2)
    28                         if img.get_alpha():
    29                             img = img.convert_alpha()
    30                         else:
    31                             img = img.convert()
    32                             img.set_colorkey(colorkey)
    33                         graphics[name] = img
    34     return graphics
    35 
    36 GFX = load_all_gfx(os.path.join("resources","graphics"))

     load_image_frames 函数 将目录中的所有图片按照图片名称中的index值为key,保存在tmp 字典中。比如图片名称为"ConeheadZombieAttack_2",它的index值就为2。
    然后将图片按index值依次加入到 frame_list 中。

     1 def load_image_frames(directory, image_name, colorkey, accept):
     2     frame_list = []
     3     tmp = {}
     4     # image_name is "Peashooter", pic name is 'Peashooter_1', get the index 1
     5     index_start = len(image_name) + 1 
     6     frame_num = 0;
     7     for pic in os.listdir(directory):
     8         name, ext = os.path.splitext(pic)
     9         if ext.lower() in accept:
    10             index = int(name[index_start:])
    11             img = pg.image.load(os.path.join(directory, pic))
    12             if img.get_alpha():
    13                 img = img.convert_alpha()
    14             else:
    15                 img = img.convert()
    16                 img.set_colorkey(colorkey)
    17             tmp[index]= img
    18             frame_num += 1
    19 
    20     for i in range(frame_num):
    21         frame_list.append(tmp[i])
    22     return frame_list

    图片显示切换
    在 sourcecomponentzombie.py 中, Zombie 类是所有僵尸类的父类,初始化 函数调用loadImages函数加载所有支持的图片类型,设置Sprite 精灵类显示需要的成员变量 image和rect。
    loadFrames函数给具体的子类来调用,获取图片。

     1 class Zombie(pg.sprite.Sprite):
     2     def __init__(self, x, y, name, health, head_group=None, damage=1):
     3         pg.sprite.Sprite.__init__(self)
     4         
     5         self.name = name
     6         self.frames = []
     7         self.frame_index = 0
     8         self.loadImages()
     9         self.frame_num = len(self.frames)
    10 
    11         self.image = self.frames[self.frame_index]
    12         self.rect = self.image.get_rect()
    13         self.rect.centerx = x
    14         self.rect.bottom = y
    15         ...
    16 
    17     def loadFrames(self, frames, name, image_x):
    18         frame_list = tool.GFX[name]
    19         rect = frame_list[0].get_rect()
    20         width, height = rect.w, rect.h
    21         width -= image_x
    22 
    23         for frame in frame_list:
    24             frames.append(tool.get_image(frame, image_x, 0, width, height))

    基本的功能都在Zombie 父类中实现,如果子类有特殊需求,可以重定义同名函数。

    update 函数:每个tick 都会调用的入口函数,用来更新僵尸的位置,切换状态和更新图片显示。
    handleState 函数:根据僵尸当前的状态来执行不同的函数。
    animation 函数:每隔指定的 animate_interval 时间会显示图片类型的下一个动作。

     1     def update(self, game_info):
     2         self.current_time = game_info[c.CURRENT_TIME]
     3         self.handleState()
     4         self.animation()
     5     
     6     def handleState(self):
     7         if self.state == c.WALK:
     8             self.walking()
     9         elif self.state == c.ATTACK:
    10             self.attacking()
    11         elif self.state == c.DIE:
    12             self.dying()
    13     
    14     def animation(self):
    15         if (self.current_time - self.animate_timer) > self.animate_interval:
    16             self.frame_index += 1
    17             if self.frame_index >= self.frame_num:
    18                 if self.state == c.DIE:
    19                     self.kill()
    20                     return
    21                 self.frame_index = 0
    22             self.animate_timer = self.current_time
    23         
    24         self.image = self.frames[self.frame_index]

    下面四个函数是修改僵尸的当前状态和图片显示。

    • setWalk 函数:修改为行走状态,图片显示会根据不同值设置不同的图片类型。
    • setAttack 函数:修改为攻击状态,图片显示会根据不同值设置不同的图片类型。
    • setDie 函数:修改为死亡状态。
    • changeFrames 函数:修改图片类型后,需要重新设置成员变量frame_num,frame_index, image和rect的值。
     1  def setWalk(self):
     2         self.state = c.WALK
     3         self.animate_interval = 150
     4         
     5         if self.helmet:
     6             self.changeFrames(self.helmet_walk_frames)
     7         elif self.losHead:
     8             self.changeFrames(self.losthead_walk_frames)
     9         else:
    10             self.changeFrames(self.walk_frames)
    11 
    12     def setAttack(self, plant):
    13         self.plant = plant
    14         self.state = c.ATTACK
    15         self.animate_interval = 100
    16         
    17         if self.helmet:
    18             self.changeFrames(self.helmet_attack_frames)
    19         elif self.losHead:
    20             self.changeFrames(self.losthead_attack_frames)
    21         else:
    22             self.changeFrames(self.attack_frames)
    23     
    24     def setDie(self):
    25         self.state = c.DIE
    26         self.animate_interval = 200
    27         self.changeFrames(self.die_frames)
    28     
    29     def changeFrames(self, frames):
    30         '''change image frames and modify rect position'''
    31         self.frames = frames
    32         self.frame_num = len(self.frames)
    33         self.frame_index = 0
    34         
    35         bottom = self.rect.bottom
    36         centerx = self.rect.centerx
    37         self.image = self.frames[self.frame_index]
    38         self.rect = self.image.get_rect()
    39         self.rect.bottom = bottom
    40         self.rect.centerx = centerx

    路障僵尸类就比较简单,只需要实现 loadImages 函数,调用loadFrames函数加载该种僵尸支持的图片类型,这边主要的差异在于不同种类僵尸的图片类型的名称会有区别。

     1 class ConeHeadZombie(Zombie):
     2     def __init__(self, x, y, head_group):
     3         Zombie.__init__(self, x, y, c.CONEHEAD_ZOMBIE, c.CONEHEAD_HEALTH, head_group)
     4         self.helmet = True
     5 
     6     def loadImages(self):
     7         self.helmet_walk_frames = []
     8         self.helmet_attack_frames = []
     9         self.walk_frames = []
    10         self.attack_frames = []
    11         self.losthead_walk_frames = []
    12         self.losthead_attack_frames = []
    13         self.die_frames = []
    14         
    15         helmet_walk_name = self.name
    16         helmet_attack_name = self.name + 'Attack'
    17         walk_name = c.NORMAL_ZOMBIE
    18         attack_name = c.NORMAL_ZOMBIE + 'Attack'
    19         losthead_walk_name = c.NORMAL_ZOMBIE + 'LostHead'
    20         losthead_attack_name = c.NORMAL_ZOMBIE + 'LostHeadAttack'
    21         die_name = c.NORMAL_ZOMBIE + 'Die'
    22 
    23         frame_list = [self.helmet_walk_frames, self.helmet_attack_frames,
    24                       self.walk_frames, self.attack_frames, self.losthead_walk_frames,
    25                       self.losthead_attack_frames, self.die_frames]
    26         name_list = [helmet_walk_name, helmet_attack_name,
    27                      walk_name, attack_name, losthead_walk_name,
    28                      losthead_attack_name, die_name]
    29         
    30         for i, name in enumerate(name_list):
    31             self.loadFrames(frame_list[i], name, tool.ZOMBIE_RECT[name]['x'])
    32 
    33         self.frames = self.helmet_walk_frames

    学习视频关注讨论群:887934385 源码、及相关素材

    编译环境

    python3.7 + pygame1.9

  • 相关阅读:
    react学习总结(一)
    jQuery的attr()与prop()的区别
    Vue.js学习(常用指令)
    Node.js学习(篇章一)
    CSS3关于-webkit-tap-highlight-color属性
    position布局影响点击事件以及冒泡获取事件目标
    取消事件默认行为(移动端)
    rem与px之间的换算(移动端)
    Node.js(初识)
    ES6(变量的解构赋值)
  • 原文地址:https://www.cnblogs.com/pypypy/p/12158437.html
Copyright © 2011-2022 走看看