page contents

Python 植物大战僵尸代码实现:图片加载和显示切换

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


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

功能实现如下:

支持的植物类型:太阳花,豌豆射手,寒冰射手,坚果,樱桃炸弹。

新增加植物:双重豌豆射手,三重豌豆射手。

支持的僵尸类型:普通僵尸,棋子僵尸,路障僵尸,铁桶僵尸。

使用json文件保存关卡信息,设置僵尸出现的时间和位置。

新增加除草机。

下面是游戏的截图:

图1

attachments-2020-01-xY8BeuFy5e1fbdf85cbdc.png

图2

attachments-2020-01-fiFddijt5e1fbe0cf3b81.png

图三

attachments-2020-01-fSNoQugE5e1fbe21631a5.png

图片显示切换

从图1和图2可以看到,僵尸的行走和攻击时的图片显示会有不同,这篇文章讲下如何进行图片显示的切换。

以上面的路障僵尸为例,一共有下面几种图片类型。

  • 带着路障行走

  • 带着路障攻击

  • 不带路障行走(即变成普通僵尸的行走)

  • 不带路障攻击(即变成普通僵尸的攻击)

  • 没有头的行走

  • 没有头的攻击

  • 死亡

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

attachments-2020-01-GPFxE0nO5e1fbe359cc8c.png


图片加载

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

attachments-2020-01-UgBQ0JDs5e1fbe4c061a2.png


在 source\tool.py 中 load_all_gfx 函数遍历resources\graphics 目录和子目录。

代码中做了一个简单的区分:

  • 如果在resources\graphics\subfolder\ 目录中是图片,那就是单独的一个图片,比如resources\graphics\Screen 目录中的界面图片

  • 如果在resources\graphics\subfolder\ 目录中是子目录,那这个子目录或子子目录中的所有图片都属于一个图片类型,比如resources\graphics\Zombies\ConeheadZombie\ConeheadZombieAttack 目录下就是路障僵尸带着路障攻击的动作图片, 如图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 resources\graphics
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 resources\graphics\Zombies
11 for name3 in os.listdir(dir2):
12 dir3 = os.path.join(dir2, name3)
13 # e.g. subfolders or pics under the folder resources\graphics\Zombies\ConeheadZombie
14 if os.path.isdir(dir3):
15 # e.g. it's the folder resources\graphics\Zombies\ConeheadZombie\ConeheadZombieAttack
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 resources\graphics\Plants\Peashooter
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 resources\graphics\Screen
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


图片显示切换

在 source\component\zombie.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

https://download.csdn.net/download/marble_xu/11639414

  • 发表于 2020-01-16 09:37
  • 阅读 ( 858 )
  • 分类:Python开发

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
Pack
Pack

1135 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 1312 文章
  3. Pack 1135 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章