如何在cocos2d里面使用动画和spritesheet 3.0 & C++版

    在游戏编程中,动画可谓必不可少,今天就向大家介绍cocos2d里面如何使Sprite加载动画,同时我们利用了SpriteSheet技术来简化操作及提高效率。

    本文是基于大神子龙山人翻译的博客修改而成,但原文是基于cocos2d 2.x版本及object-c语言的,因此我将它进一步加工成了C++语言,并尽量基于cocos2d 3.0的实现版本。由于本人也是cocos2d的初学者,因此文中有任何不妥之处,尽管拍砖!

   子龙山人博客:http://www.cnblogs.com/andyque/archive/2011/04/11/2012770.html

    英文原版地址:http://www.raywenderlich.com/1271/how-to-use-animations-and-sprite-sheets-in-cocos2d

   废话少说,让我们开始吧。

什么是SpriteSheet

      SpriteSheet,翻译为“精灵表单”。好吧,这个翻译也许并不那么直白。SpriteSheet的定义,见原文翻译:

      “如果你从来没有使用过spritesheet,你可以把它看作是一张巨大的图片,你可以把许许多多的sprite放进去。与spritesheet对应的,还有一个plist文件,这个文件指定了每个独立的sprite在这张“大图”里面的位置和大小,当你在代码之间需要使用这个sprite的时候,就可以很方面地使用plist文件中的这些信息来获取sprite。”

    SpriteSheet能够方便的帮我们管理众多Sprite,比如,你的游戏人物有一个射击的动作,你将射击分解为了4个帧的画面,每个画面,你可能都要创建一个精灵并依次渲染它们,这样,共渲染4次。而SpriteSheet却将4帧画面整合为一个SpriteSheet,当你需要显示连续的射击动画时,这4个画面会被当做一个大的SpriteSheet画面渲染1次!你需要做的仅是将存储在SpriteSheet中的帧画面取出并组成Action,并让你的Sprite来run这个action。感觉出来了吗,没有SpriteSheet,你需要渲染4次Sprite画面,而现在你只要渲染一次,大大提高了效率!原文解释如下。

    “为什么这会提高效率呢?因为cocos2d对它进行了优化!如果你使用spritesheet来获取sprite,那么当场景中有许多sprite的时候,如果这些sprite共享一个spritesheet,那么cocos2d就会使用一次OpenGL ES调用来渲染这些sprite。但是,如果是单个的sprite的话,那么就会有N次OpenGL ES call,这个代价是相当昂贵的”

如何运用SpriteSheet

    好了,既然SpriteSheet这么牛逼,那么我们具体应该如何使用它那?

    首先,SpriteSheet作为cocos2d的一种技术,需要有特殊的资源文件配合其实现。什么特殊资源文件呢,两部分:

    1、整合了所有帧画面的大png图片。大png图片如下所示,包含了熊散步动画的所有帧画面。

    2、描述大png图片信息的plist文件(plist文件包含描述信息,里面包含了图片在spritesheet中的位置、大小和名字等信息)。

    如何在cocos2d里面使用动画和spritesheet 3.0 & C++版_第1张图片

    

    那我们怎么获取这两个文件呢,我们有工具 Zwoptex!

    工具不知怎么上传不到云盘,是mac版的,有需要可以留邮箱发送。

    具体怎么使用工具?看原文博客,有详细说明。但原文软件版本和我的版本并不一致,操作上略有不同。具体细节大家自己摸索咯(偷个懒)。

    OK,有了资源文件,我们就可以新建工程,来让我们的熊出来散散步吧~

    顺便介绍一下我的开发环境

    Xcode5-DP  and cocos2d-x-3.0beta2.

简单动画

    让我们先创建一个叫做WalkBear的cocos2d工程(具体创建方法可询问度娘或现在正关小黑屋的谷哥)。

    在HelloWorldScene.h 里面添加如下属性。

   

    CC_SYNTHESIZE_RETAIN(cocos2d::Sprite*, _bear, BearSprite);
    CC_SYNTHESIZE_RETAIN(cocos2d::Action*, _walkAction, WalkAction);
    CC_SYNTHESIZE_RETAIN(cocos2d::Action*, _moveAction, MoveAction);

        好了,这下我们有个了三个private属性了,分别为cocos2d::Sprite*类型的_bear(熊精灵) cocos2d::Action*类型的_walkAction(熊走路动画)、_moveAction(熊移动动作)。并分别定义了它们的get和set方法。

    啥?没看懂上面是什么意思?别忘了,cocos2d可是开源的,让我们一起看看CC_SYNTHESIZE_RETAIN宏定义(cocos2d的注释写的很详细,有不明白的地方可以直接看源码、注释)。

#define CC_SYNTHESIZE_RETAIN(varType, varName, funName)    \
private: varType varName; \
public: virtual varType get##funName(void) const { return varName; } \
public: virtual void set##funName(varType var)   \
{ \
    if (varName != var) \
    { \
        CC_SAFE_RETAIN(var); \
        CC_SAFE_RELEASE(varName); \
        varName = var; \
    } \
} 

   这下明白了吧,其实我们运用宏   

CC_SYNTHESIZE_RETAIN(cocos2d::Sprite*, _bear, BearSprite);

   相当于写了如下一堆代码,

private: cocos2d::Sprite*  _bear;
public: virtual cocos2d::Sprite* getBearSprite(void) const { return _bear; } 
public: virtual void setBearSprite(cocos2d::Sprite* var) 
{ 
    if (_bear != var)   // 注意,这里只有当原值与新值不相等时,才会赋值
    { 
        CC_SAFE_RETAIN(var); 
        CC_SAFE_RELEASE(_bear); 
        _bear = var; 
    } 
} 

    好,那位看官又有问题了,那为啥不用宏CC_SYNTHESIZE,而用CC_SYNTHESIZE_RETAIN呢?这个…… 说实话,我也不是很清楚。(莫喷,毕竟我也是初学者啊)

    我的感觉是加上RETAIN(汉语为”保留“)表示告诉cocos2d的内存处理机制,这个对象是属于我的,你cocos2d在自动清理空闲内存的时候,别给我自动清了,哥留着有用。如果不加上retain,可能会导致cocos2d自动将你生成的对象认为没有用了而清理掉。这样你再想用它的时候,可就找不回来了。当然,这是我个人浅薄的理解半猜性质,希望能有大神进一步解释。

     在HelloWorld的构造函数里,我们初始化这三个变量为空指针。(nullptr是C++11标准中代替NULL的,记得加入头文件memory.h)

HelloWorld():_bear(nullptr), _walkAction(nullptr), _moveAction(nullptr), _walking(false){}

     好了,我们的演员(_bear)、剧本(_walkAction、_moveAction,当然现在时“空剧本”),都到位了,就让我们的熊动起来吧!

初级阶段 简单的动画

    在初级阶段,我们要实现熊的走路动画,当然,是向着一个方向傻傻的走,在高级点阶段里面,我们将实现触屏来指引熊移动方向的功能。

    在初级阶段里面,我们在init()方法里面添加代码。共分为5个步骤。

1)缓冲sprite帧和纹理

   SpriteFrameCache* shareSpriteBearFrameChache = SpriteFrameCache::getInstance();
   shareSpriteBearFrameChache->addSpriteFramesWithFile(“AnimBear.plist”);

在上面的代码里面,我们建立了一个SpriteFrameCache对象实例,同时,调用了其addSpriteFramesWithFile方法。该方法完成两件事情:

(1)寻找工程目录下面和输入的参数名字一样,但是后缀是.png的图片文件。然后把这个文件加入到共享的TextureCache中。(这我们这个例子中,就是加载AnimBear.png)

(2)解析plist文件,追踪所有的sprite在spritesheet中的位置,内部使用SpriteFrame对象来追踪这些信息


2) 创建一个精灵批处理结点

 SpriteBatchNode* spriteSheet = SpriteBatchNode::create("AnimBear.png");
    
 this->addChild(spriteSheet);

精灵批处理节点的说明,请看原文,其中的2.0的类名称,各位看官可自行对比。

  “接下来,创建CCSpriteBatchNode对象,把spritesheet当作参数传进去。spritesheet在cocos2d中的工作原理如下:

你创建一个CCSpriteBatchNode对象,通过传递一个包含所有sprite的spritesheet的名字作为参数,并把它加入到当前场景之中。

接下来,你从spritesheet中创建的任何sprite,你应该把它当作CCSpriteBatchNode的一个孩子加进去。只要sprite包含在spritesheet中,那么就没问题,否则会出错。

CCSpriteBatchNode可以智能地遍历它的所有的孩子结点,并通过一次OpenGL ES call来渲染这些孩子,而不是以前每个sprite都需要一个OpenGL call,这样渲染速度就会更快。

3) 收集帧列表

cocos2d::Vector walkAniFrams;
    for (int i = 1; i <=8; i++) {
        
        std::ostringstream os;
        os << "bear" << i << ".png";
        std::string strFrameNmae = os.str();
        
        walkAniFrams.pushBack(shareSpriteBearFrameChache->getSpriteFrameByName(strFrameNmae));
    }

    这里我们用一个循环,将之前存储在SpriteFrameCache中的动画帧都取出来,放在了一个vector容器里面。记住,它们已经在缓存里了,因为我们前面调用了addSpriteFramesWithFile方法。

4) 创建动画对象

Animation* walkAnim = Animation::createWithSpriteFrames(walkAniFrams, 0.1);

简单,只有一句话,我们将上一步存储动画帧的vector当做参数传进去,并指定了动画帧之间的播放间隔0.1秒(单位是秒吧)


5) 创建sprite并且让它run动画action

  创建精灵并显示

 _bear = Sprite::createWithSpriteFrameName("bear1.png");
    _bear->setPosition(Point(visibleSize.width/2, visibleSize.height/2));
    _walkAction = RepeatForever::create(Animate::create(walkAnim));
    _walkAction->retain();
    
    _bear->runAction(_walkAction);
    spriteSheet->addChild(_bear);

     在上面的代码里,我们首先创建了一个精灵对象。注意,我们这里创建精灵的方法,并没有使用creata方法,而是用了createWithSpriteFrameName。对于该方法,cocos2d里面是这样说明的

/**
     * Creates a sprite with an sprite frame name.
     *
     * A SpriteFrame will be fetched from the SpriteFrameCache by spriteFrameName param.
     * If the SpriteFrame doesn't exist it will raise an exception.
     *
     * @param   spriteFrameName A null terminated string which indicates the sprite frame name.
     * @return  A valid sprite object that is marked as autoreleased.
     */

    这里就是说,我们用spriteframe当做初始化模板,创建了一个精灵。而这个当做模板的spriteframe,必须是已经存在于SpriteFrameCache中的。而这个SpriteFrameCache,就是我们之前在第一步中创建的shareSpriteBearFrameChache,它存储了关于熊的spritefame。

    创建好sprite对象之后,我们接着又创建了一个让熊表演的action对象,同时让熊精灵来run这个action。

    注意,在最后一步,我们把熊加个场景中—把它当作spritesheet的孩子加到spritesheet中去。注意,如果在这里我们没有把它加到spritsheet中,而是加到当前层里面的话。那么我们将得不到spritesheet为我们带来的性能提升!!!

     OK!编译运行吧,一只熊就在你手机上运行了!

高级阶段 让熊按照你的旨意行动

(待续)

源代码 http://pan.baidu.com/s/1mg8zV40


















你可能感兴趣的:(如何在cocos2d里面使用动画和spritesheet 3.0 & C++版)