飞天鸽 发表于 2015-4-24 21:48:28

如果学会了python,我可以做游戏吗(格斗篇2)

接上一贴

所谓精灵动画,就是要让每一帧都动起来,这里我采用的是单帧的方式,利用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.w,\
                               h=item.h,\
                               itemlen=item.itemlen,\
                               index=nextindex)
            setattr(self,item,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,\
                         *itemlen)

    def get_next_rect(self):
      itor =
      def next_itor():
            now = itor
            itor = (itor + 1) % self.__itemlen
            return self.rects
      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-n,n-n))
      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 -= self.speed
      point = max(0,point)
      
    def action_right(self):
      point = self.point
      point += self.speed

归根结底,程序完全是依赖注入的方式,这种设计下添加一个新的动作只需要设定 item_dict就可以动起来,添加action_名称就可以完成逻辑
这么一看代码复用度起始还是蛮高的呀

既然获得了各种状态,那么,状态之间的转换似乎就有必要讲一讲了
下节课,我们来讲讲FSM,传说中的状态机,嗯(深思状)
讲述比较乱,还是附上代码吧,运行game.py 就可以测试运行效果了,233

http://pan.baidu.com/s/1o6JtBse




秋栗子 发表于 2015-5-11 19:36:43

好教程贴居然没人顶

命数 发表于 2015-5-11 20:21:06

又见一枚技术宅

命数 发表于 2015-5-11 20:21:13

又见一枚技术宅

ID:L1 发表于 2015-5-13 12:58:52

坐等大神教程全了

Q版宝宝 发表于 2016-10-8 20:52:36

楼主这么无私的教程我觉得我应该有必要顶上天=3=
页: [1]
查看完整版本: 如果学会了python,我可以做游戏吗(格斗篇2)