接上一贴
所谓精灵动画,就是要让每一帧都动起来,这里我采用的是单帧的方式,利用pivotanimator来制作火柴人动画
做法请自行百度,导出单帧图片在一个目录然后利用photoshop结合成一张大图
这里我只做了部分,从上向下是向右走,向左走和待机状态(其实待机状态也分为面向左还是右)
最后,我们希望通过
if event.type == userevent.PERSONCHECKCHANGE:
p1.update(screen,p1state) #screen是屏幕对象,p1state是palyer1的状态
然后,每50ms触发一次userevent.PERSONCHECKCHANGE,来画下一帧
接下来,就需要仔细的思考一下这个person类要如何来做,才能满足只传入状态就能获得当前帧
要求:对于update函数传入‘left’,函数会在第一行循环圈定矩形,9帧
对于update函数传入‘right’,函数会在第二行循环圈定矩形,9帧
对于update函数传入‘right’,函数会在第二行循环圈定矩形,9帧
于是,我在class Person1类中做了如下定义,每个动作占一整行
item_dict=[
("left",personutils.child(w=256,h=256,itemlen=9)), #w和h是每帧的宽高
("right",personutils.child(w=256,h=256,itemlen=9)),
("normal",personutils.child(w=256,h=256,itemlen=9))
#可能会添加新的动作,比如'出拳',‘出脚’
]
child只是一个nametuple:child = collections.namedtuple('child','w h itemlen')
在init函数里加入一个init_child来加载我们的设定
def init_child(self):
dic = self.item_dict
nextindex = 0
for item in dic:
value = childRects(w=item[1].w,
h=item[1].h,
itemlen=item[1].itemlen,
index=nextindex)
setattr(self,item[0],value)
nextindex = value.nextindex
分析一下:这段代码在运行时动态的向Person1类注入名称是left,right,normal的childRects类
childRects类如下:
class childRects(object):
def __init__(self,index,itemlen,h=256,w=256):
self.__h = h
self.__w = w
self.__itemlen = itemlen
self.init_rects(index,itemlen)
self.nextindex = index + h
def init_rects(self,index,itemlen):
change_mapper = lambda x,y: (x,y,x+self.__w,y+self.__h)
split_pos = range(0,\
self.__w*itemlen,\
self.__w)
self.rects = map(change_mapper,split_pos,\
[index]*itemlen)
def get_next_rect(self):
itor = [0]
def next_itor():
now = itor[0]
itor[0] = (itor[0] + 1) % self.__itemlen
return self.rects[now]
return next_itor
这一段对于C或者C++的同学可能有些难以理解,init_rects的作用就是在一行按照w和h画itemlen个方框,返回这些的列表
不得不说,python的lambda实在太过方便了
get_next_rect是一个闭包计数器,至于为什么是闭包,之所以选择闭包,额,等一下讲哈
类里面的nextindex是为了指向下一个动作的起始高度,比如left帧在0,0像素开始,那么right帧就会在0,256开始,也就是nametuple与构造函数差掉的那个index
至此,我们可以通过某个person1的实例p1,调用p1.right.get_next_rect来创建right帧的迭代器,这里的好处就体现出来了
def update_state(self,state):
bfstate = self.now_state
if cmp(state,self.now_state) != 0:
self.now_state = state
attr = getattr(self,self.now_state,"not found")
if type(attr)!=type(""):
self.next = attr.get_next_rect()
else:
self.now_state = bfstate
通过调用update_state并传入状态值,就会在实例中更新next方法,此时的next适配了各种状态
最后的最后,完成了update
def update(self,screen,state):
point = self.point
self.update_state(state)
n = self.next()
tmp = pygame.Surface((n[2]-n[0],n[3]-n[1]))
tmp.blit(self.image,(0,0),n)
attr = getattr(self,"action_"+self.now_state,"not found") #在处理动画的同时,我们还可以编写这个精灵的逻辑函数
if type(attr)!=type(""): #例如action_right
attr()
screen.blit(tmp,tuple(point))
逻辑函数在我的例子中比较简单,只有左移右移,因为normal态是不进行任何逻辑处理的233
def action_left(self):
point = self.point
point[0] -= self.speed
point[0] = max(0,point[0])
def action_right(self):
point = self.point
point[0] += self.speed
归根结底,程序完全是依赖注入的方式,这种设计下添加一个新的动作只需要设定 item_dict就可以动起来,添加action_名称就可以完成逻辑
这么一看代码复用度起始还是蛮高的呀
既然获得了各种状态,那么,状态之间的转换似乎就有必要讲一讲了
下节课,我们来讲讲FSM,传说中的状态机,嗯(深思状)
讲述比较乱,还是附上代码吧,运行game.py 就可以测试运行效果了,233
http://pan.baidu.com/s/1o6JtBse