Python 植物大战僵尸代码实现: 图片加载和显示切换
游戏介绍
以前很火的植物大战僵尸游戏, 本想在网上找个python版本游戏学习下,无奈没有发现比较完整的,那就自己来写一个把。图片资源是从github上下载的,因为图片资源有限,只能实现几种植物和僵尸。
功能实现如下:
支持的植物类型:太阳花,豌豆射手,寒冰射手,坚果,樱桃炸弹。新增加植物:双重豌豆射手,三重豌豆射手。
支持的僵尸类型:普通僵尸,棋子僵尸,路障僵尸,铁桶僵尸。
使用json文件保存关卡信息,设置僵尸出现的时间和位置。
新增加除草机。
下面是游戏的截图:
图1
图2
图片显示切换
从图1和图2可以看到,僵尸的行走和攻击时的图片显示会有不同,这篇文章讲下如何进行图片显示的切换。
以上面的路障僵尸为例,一共有下面几种图片类型。
- 带着路障行走
- 带着路障攻击
- 不带路障行走(即变成普通僵尸的行走)
- 不带路障攻击(即变成普通僵尸的攻击)
- 没有头的行走
- 没有头的攻击
- 死亡
图3是路障僵尸的这7种图片类型的示例
图片加载
植物大战僵尸的图片资源比较特别,一种图片类型的每一个动作是一个单独的图片,如图4是路障僵尸带着路障攻击的动作图片,一共有11个图片,所以加载图片的代码要做对应的修改。
完整代码
游戏实现代码的github链接 植物大战僵尸
这边是csdn的下载链接 植物大战僵尸
图片加载
在 source\tool.py 中 load_all_gfx 函数遍历resources\graphics 目录和子目录。
代码中做了一个简单的区分:
- 如果在resources\graphics\subfolder\ 目录中是图片,那就是单独的一个图片,比如resources\graphics\Screen 目录中的界面图片
- 如果在resources\graphics\subfolder\ 目录中是子目录,那这个子目录或子子目录中的所有图片都属于一个图片类型,比如resources\graphics\Zombies\ConeheadZombie\ConeheadZombieAttack 目录下就是路障僵尸带着路障攻击的动作图片, 如图4所示。
def load_all_gfx(directory, colorkey=c.WHITE, accept=('.png', '.jpg', '.bmp', '.gif')):
graphics = {}
for name1 in os.listdir(directory):
# subfolders under the folder resources\graphics
dir1 = os.path.join(directory, name1)
if os.path.isdir(dir1):
for name2 in os.listdir(dir1):
dir2 = os.path.join(dir1, name2)
if os.path.isdir(dir2):
# e.g. subfolders under the folder resources\graphics\Zombies
for name3 in os.listdir(dir2):
dir3 = os.path.join(dir2, name3)
# e.g. subfolders or pics under the folder resources\graphics\Zombies\ConeheadZombie
if os.path.isdir(dir3):
# e.g. it's the folder resources\graphics\Zombies\ConeheadZombie\ConeheadZombieAttack
image_name, _ = os.path.splitext(name3)
graphics[image_name] = load_image_frames(dir3, image_name, colorkey, accept)
else:
# e.g. pics under the folder resources\graphics\Plants\Peashooter
image_name, _ = os.path.splitext(name2)
graphics[image_name] = load_image_frames(dir2, image_name, colorkey, accept)
break
else:
# e.g. pics under the folder resources\graphics\Screen
name, ext = os.path.splitext(name2)
if ext.lower() in accept:
img = pg.image.load(dir2)
if img.get_alpha():
img = img.convert_alpha()
else:
img = img.convert()
img.set_colorkey(colorkey)
graphics[name] = img
return graphics GFX = load_all_gfx(os.path.join("resources","graphics"))
load_image_frames 函数 将目录中的所有图片按照图片名称中的index值为key,保存在tmp 字典中。比如图片名称为"ConeheadZombieAttack_2",它的index值就为2。
然后将图片按index值依次加入到 frame_list 中。
def load_image_frames(directory, image_name, colorkey, accept):
frame_list = []
tmp = {}
# image_name is "Peashooter", pic name is 'Peashooter_1', get the index 1
index_start = len(image_name) + 1
frame_num = 0;
for pic in os.listdir(directory):
name, ext = os.path.splitext(pic)
if ext.lower() in accept:
index = int(name[index_start:])
img = pg.image.load(os.path.join(directory, pic))
if img.get_alpha():
img = img.convert_alpha()
else:
img = img.convert()
img.set_colorkey(colorkey)
tmp[index]= img
frame_num += 1 for i in range(frame_num):
frame_list.append(tmp[i])
return frame_list
图片显示切换
在 source\component\zombie.py 中, Zombie 类是所有僵尸类的父类,初始化 函数调用loadImages函数加载所有支持的图片类型,设置Sprite 精灵类显示需要的成员变量 image和rect。
loadFrames函数给具体的子类来调用,获取图片。
class Zombie(pg.sprite.Sprite):
def __init__(self, x, y, name, health, head_group=None, damage=1):
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.rect.centerx = x
self.rect.bottom = y
... def loadFrames(self, frames, name, image_x):
frame_list = tool.GFX[name]
rect = frame_list[0].get_rect()
width, height = rect.w, rect.h
width -= image_x for frame in frame_list:
frames.append(tool.get_image(frame, image_x, 0, width, height))
基本的功能都在Zombie 父类中实现,如果子类有特殊需求,可以重定义同名函数。
update 函数:每个tick 都会调用的入口函数,用来更新僵尸的位置,切换状态和更新图片显示。
handleState 函数:根据僵尸当前的状态来执行不同的函数。
animation 函数:每隔指定的 animate_interval 时间会显示图片类型的下一个动作。
def update(self, game_info):
self.current_time = game_info[c.CURRENT_TIME]
self.handleState()
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() def animation(self):
if (self.current_time - self.animate_timer) > self.animate_interval:
self.frame_index += 1
if self.frame_index >= self.frame_num:
if self.state == c.DIE:
self.kill()
return
self.frame_index = 0
self.animate_timer = self.current_time self.image = self.frames[self.frame_index]
下面四个函数是修改僵尸的当前状态和图片显示。
- setWalk 函数:修改为行走状态,图片显示会根据不同值设置不同的图片类型。
- setAttack 函数:修改为攻击状态,图片显示会根据不同值设置不同的图片类型。
- setDie 函数:修改为死亡状态。
- changeFrames 函数:修改图片类型后,需要重新设置成员变量frame_num,frame_index, image和rect的值。
def setWalk(self):
self.state = c.WALK
self.animate_interval = 150 if self.helmet:
self.changeFrames(self.helmet_walk_frames)
elif self.losHead:
self.changeFrames(self.losthead_walk_frames)
else:
self.changeFrames(self.walk_frames) def setAttack(self, plant):
self.plant = plant
self.state = c.ATTACK
self.animate_interval = 100 if self.helmet:
self.changeFrames(self.helmet_attack_frames)
elif self.losHead:
self.changeFrames(self.losthead_attack_frames)
else:
self.changeFrames(self.attack_frames) def setDie(self):
self.state = c.DIE
self.animate_interval = 200
self.changeFrames(self.die_frames) def changeFrames(self, frames):
'''change image frames and modify rect position'''
self.frames = frames
self.frame_num = len(self.frames)
self.frame_index = 0 bottom = self.rect.bottom
centerx = self.rect.centerx
self.image = self.frames[self.frame_index]
self.rect = self.image.get_rect()
self.rect.bottom = bottom
self.rect.centerx = centerx
路障僵尸类就比较简单,只需要实现 loadImages 函数,调用loadFrames函数加载该种僵尸支持的图片类型,这边主要的差异在于不同种类僵尸的图片类型的名称会有区别。
class ConeHeadZombie(Zombie):
def __init__(self, x, y, head_group):
Zombie.__init__(self, x, y, c.CONEHEAD_ZOMBIE, c.CONEHEAD_HEALTH, head_group)
self.helmet = True def loadImages(self):
self.helmet_walk_frames = []
self.helmet_attack_frames = []
self.walk_frames = []
self.attack_frames = []
self.losthead_walk_frames = []
self.losthead_attack_frames = []
self.die_frames = [] helmet_walk_name = self.name
helmet_attack_name = self.name + 'Attack'
walk_name = c.NORMAL_ZOMBIE
attack_name = c.NORMAL_ZOMBIE + 'Attack'
losthead_walk_name = c.NORMAL_ZOMBIE + 'LostHead'
losthead_attack_name = c.NORMAL_ZOMBIE + 'LostHeadAttack'
die_name = c.NORMAL_ZOMBIE + 'Die' frame_list = [self.helmet_walk_frames, self.helmet_attack_frames,
self.walk_frames, self.attack_frames, self.losthead_walk_frames,
self.losthead_attack_frames, self.die_frames]
name_list = [helmet_walk_name, helmet_attack_name,
walk_name, attack_name, losthead_walk_name,
losthead_attack_name, die_name] for i, name in enumerate(name_list):
self.loadFrames(frame_list[i], name, tool.ZOMBIE_RECT[name]['x']) self.frames = self.helmet_walk_frames
学习视频关注讨论群:887934385 源码、及相关素材
编译环境
python3.7 + pygame1.9
Python 植物大战僵尸代码实现: 图片加载和显示切换的更多相关文章
- 从代码分析Android-Universal-Image-Loader的图片加载、显示流程
从UNIVERSAL IMAGE LOADER. PART 3(四个DisplayImage重载方法详解)中,我们学习了Android-Universal-Image-Loader(以下简称UIL)中 ...
- vue中的图片加载与显示默认图片
HTML: <div class="content-show-img"> <div class="show-img"> <img ...
- WP 图片加载时显示加载中
private BitmapImage srcimage = new BitmapImage(); public MainPage() { InitializeComponent(); progres ...
- JS图片加载失败显示默认图片
代码如下: <div id='photo<%# Container.DataItemIndex+1%>' style="position: absolute; displa ...
- (js有关图片加载问题)dom加载完和onload事件
引用旺旺的话...哈哈哈DOMContentLoaded事件表示页面的DOM结构绘制完成了,这时候外部资源(带src属性的)还没有加载完.而onload事件是等外部资源都加载完了就触发的.$.read ...
- 图片加载框架之fresco
FaceBook推出的图片处理框架主页: https://github.com/facebook/fresco中文文档:http://fresco-cn.org/docs/index.html 功能 ...
- 【第三篇】Volley图片加载之NetworkImageView代码分析
在Volley的使用之加载图片讲过使用NetWorkImageView进行图片加载的例子,本文着重讲解NetWorkImageView内部是如何实现的,以及Volley这个控件有什么特性. 1,通 ...
- 基于webpack的前端工程化开发解决方案探索(二):代码分割与图片加载
今天我们继续来进行webpack工程化开发的探索! 首先来验证上一篇文章 基于webpack的前端工程化开发解决方案探索(一):动态生成HTML 中的遗留问题:webpack将如何处理按需加载的 ...
- Android中常见的图片加载框架
图片加载涉及到图片的缓存.图片的处理.图片的显示等.而随着市面上手机设备的硬件水平飞速发展,对图片的显示要求越来越高,稍微处理不好就会造成内存溢出等问题.很多软件厂家的通用做法就是借用第三方的框架进行 ...
随机推荐
- CSS常用函数calc等
>>CSS常用函数<<
- 将Eclipse中文注释字体变大方法
今天下了最新的eclipse玩,结果发现注释变得灰常小,差点看瞎哥24K氪金狗眼 于是在网上找了找解决方法,结果都不对 最后自己试出来了... 方法: Window --> Preferenc ...
- 2019-11-13-如何在国内发布-UWP-应用
title author date CreateTime categories 如何在国内发布 UWP 应用 lindexi 2019-11-13 08:46:44 +0800 2019-02-17 ...
- [学习笔记]整体DP
问题: 有一些问题,通常见于二维的DP,另一维记录当前x的信息,但是这一维过大无法开下,O(nm)也无法通过. 但是如果发现,对于x,在第二维的一些区间内,取值都是相同的,并且这样的区间是有限个,就可 ...
- 排他网关(ExclusiveGateWay)
网关(ExclusiveGateWay) 作者:邓家海2018年3月11日 00:13:25 情景:某一家公司最近在给一个单位做一个财务审批的OA.具体需求是这样的:当部门申请的金额小于一万块的时候, ...
- SuperSocket通过本地证书仓库的证书来启用 TLS/SSL
你也可以通过本地证书仓库的证书,而不是使用一个物理文件. 你只需要在配置中设置你要使用的证书的storeName和thumbprint: <server name="EchoServe ...
- python新知识
# 强制字符串转化 repr(1.1 + 2.2) # 字符串换行 a = "hello, world. " \ "it's a nice day. " \ & ...
- Python--day68--Django ORM常用字段、不常用的字段、自定义字段
ORM和数据库的对应关系: Django ORM 常用字段和参数 常用字段 AutoField int自增列,必须填入参数 primary_key=True.当model中如果没有自增列,则自动会创建 ...
- springmvc web.xml和application.xml配置详情(附:完整版pom.xml)
web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="htt ...
- java.util.NoSuchElementException: No value present
错误: java.util.NoSuchElementException: No value present 原因: 经查询博客Java 8 Optional类深度解析发现,究其原因为: 在空的Opt ...