cocos2d-x 五(精灵与动画)

精灵可以看作是演员、角色,在游戏中,精灵可以是一张图片,一个控件(比如label也是继承了精灵类)……在场景中,精灵会根据需要进行相关的“表演”,这个“表演”可以是待在某个坐标,也可以是一组动画……游戏中的动画效果是很多的,可以是动态变换位置、大小的菜单按钮,也可以是个游戏人物的行为动画……这里先推荐两篇不错的文章,里面详细介绍了各种动画效果和实现的原理:

http://blog.csdn.net/honghaier/article/details/8197892

http://blog.csdn.net/honghaier/article/details/8214030

如果想做什么效果的话随时可以参考上面两篇文章的说明进行相关调用,而官方原例中的Action Test已经输出了所有效果,这里记录一下关键的、常用的内容:

1.CCSpriteBatchNode

它用于批量绘制精灵,当要绘制的精灵所用的纹理图片都在一张图片中时,使用它能大大提高绘制的效率,因为它可以一次性绘制所有存储在里面的精灵。在CCSpriteBatchNode创建时需要一张纹理图,这也是绘制精灵时要用到的纹理,当然,创建的方式有几种,这里只给出官方原例中PerformanceTest里使用的方式:

CCSpriteBatchNode* BatchNode = CCSpriteBatchNode::create("grossini_dance_atlas.png", 50);
sprite = CCSprite::createWithTexture(batchNode->getTexture(), CCRectMake(0, 0, 52, 139));
batchNode->addChild(sprite, 0, tag+100);

第二个参数是设定CCSpriteBatchNode的容量,也就是容纳精灵的数量,当超过这个容纳上限被超过时,容量将自动按33%来增加。需要注意的是,CCSpriteBatchNode只能用来存储精灵对象,像粒子、标签、层是不能被加进去的。当创建精灵时,可以引用BatchNode创建时绑定的纹理图,并可以通过CCRectMake设定一个纹理区域来使用整个纹理图中被划定区域中的纹理部分进行绘制。最后一句话便是将创建好的精灵加入BatchNode了。

2.CCSpriteFrameCache

它与CCTextureCache的功能一样,只是用于精灵帧(精灵序列帧动画中的一帧)的缓存,在官方原例中的action Progress test里也使用过这个类,例子中根据CCTextureCache中的精灵帧创建绘制出三个引用了不同的精灵帧纹理的精灵:

CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("zwoptex/grossini.plist");

CCProgressTimer *left = CCProgressTimer::create(CCSprite::createWithSpriteFrameName("grossini_dance_01.png"));
("grossini_dance_01.png"));   
CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("zwoptex/grossini.plist");

CCProgressTimer *left = CCProgressTimer::create(CCSprite::createWithSpriteFrameName("grossini_dance_01.png"));
其中plist文件是用于存储精灵帧的索引信息,包括纹理索引名和对应的纹理区域,这个信息文件对应其目录下名为grossini.png的图片,然后创建精灵时便 可以引用缓存中的信息进行对应绘制。

下面说下动画,精灵可以使用runaction来跑一个动画,这个动画可以是CCActionInterval类定义的,也可以是基于CCActionInterval的类定义的,如CCRotateTo,在跑动画时可以有很多方式,比如只跑单个动画,也可以按顺序跑两个动画,还可以是永远循环着跑一个动画……这些使用方法都可以在官方原例中找到,下面是具体的使用代码:

    CCActionInterval*  action1 = CCTintTo::create(2, 255, 0, 255);
    CCActionInterval*  action2 = CCTintBy::create(2, -127, -255, -127);
    CCActionInterval*  action2Back = action2->reverse();
    sprite->runAction( action1);
    //CCSequence用于按顺序播放两组动画
    sprite1->runAction( CCSequence::create( action2, action2Back, NULL));
 // Manual animation
    //手动设置动画播放
    CCAnimation* animation = CCAnimation::create();
    for( int i=1;i<15;i++)    //这里按图片路径及名称向animation里添加15帧的精灵帧
    {
        char szName[100] = {0};
        sprintf(szName, "Images/grossini_dance_d.png", i);
        animation->addSpriteFrameWithFileName(szName);
    }
    // should last 2.8 seconds. And there are 14 frames.设定播放延时
    animation->setDelayPerUnit(2.8f / 14.0f);
    animation->setRestoreOriginalFrame(true);//动画播放完成后是否返回到原始帧

    CCAnimate* action = CCAnimate::create(animation);
    m_grossini->runAction(CCSequence::create(action, action->reverse(), NULL));
    
    //
    // File animation
    //
    // With 2 loops and reverse
    CCAnimationCache *cache = CCAnimationCache::sharedAnimationCache();
    cache->addAnimationsWithFile("animations/animations-2.plist");
    CCAnimation *animation2 = cache->animationByName("dance_1");

    CCAnimate* action2 = CCAnimate::create(animation2);
    m_tamara->runAction(CCSequence::create(action2, action2->reverse(), NULL));//reverse可以逆向播放动画

// TODO:
//     observer_ = [[NSNotificationCenter defaultCenter] addObserverForName:CCAnimationFrameDisplayedNotification object:nil queue:nil usingBlock:^(NSNotification* notification) {
// 
//         NSDictionary *userInfo = [notification userInfo);
//         NSLog(@"object %@ with data %@", [notification object), userInfo );
//     });


    //
    // File animation
    //
    // with 4 loops,设置循环次数
    CCAnimation *animation3 = (CCAnimation *)animation2->copy()->autorelease();
    animation3->setLoops(4);


    CCAnimate* action3 = CCAnimate::create(animation3);
    m_kathia->runAction(action3);


    CCActionInterval*  move = CCMoveBy::create(1, ccp(150,0));
    //<span style="font-family: Arial, Helvetica, sans-serif;"> CCDelayTime::create(2)等于延时两秒</span>
    CCFiniteTimeAction*  action = CCSequence::create( move, CCDelayTime::create(2), move, NULL);

    sprite->runAction(action);

    CCActionInterval*  move = CCMoveBy::create(1, ccp(150,0));
    //<span style="font-family: Arial, Helvetica, sans-serif;"> CCDelayTime::create(2)等于延时两秒</span>
    CCFiniteTimeAction*  action = CCSequence::create( move, CCDelayTime::create(2), move, NULL);

    sprite->runAction(action);


   //下面使用的是动画完成后执行回调函数,第二和第三使用了组合动画
    CCFiniteTimeAction*  action = CCSequence::create(
        CCMoveBy::create(2, ccp(200,0)),
        CCCallFunc::create(this, callfunc_selector(ActionCallFunc::callback1)), 
        NULL);
  //CCCallFuncN用于第一个动画执行完毕后调用回调函数
    CCFiniteTimeAction*  action2 = CCSequence::create(
        CCScaleBy::create(2 ,  2),
        CCFadeOut::create(2),
        CCCallFuncN::create(this, callfuncN_selector(ActionSequence2::callback2)), 
        NULL);
  //CCCallFuncND用于两个动画执行完毕后调用回调函数,ND就是Node和Data之意,data用的是void* 所以可以是任何数据类型
    CCFiniteTimeAction*  action3 = CCSequence::create(
        CCRotateBy::create(3 , 360),
        CCFadeOut::create(2),
        CCCallFuncND::create(this, callfuncND_selector(ActionSequence2::callback3), (void*)0xbebabeba), 
        NULL);

    sprite->runAction(action);
    sprite1->runAction(action2);
    sprite2->runAction(action3);

//------------------------------------------------------------------
void ActionCallFuncND::onEnter()
{
    ActionsDemo::onEnter();

    centerSprites(1);
    //这里执行了清除精灵的回调函数
    CCFiniteTimeAction* action = CCSequence::create(CCMoveBy::create(2.0f, ccp(200,0)),
        CCCallFuncND::create(this, callfuncND_selector(ActionCallFuncND::removeFromParentAndCleanup), (void*)true),
        NULL);

    m_grossini->runAction(action);
}

std::string ActionCallFuncND::title()
{
    return "CallFuncND + auto remove";
}

std::string ActionCallFuncND::subtitle()
{
    return "CallFuncND + removeFromParentAndCleanup. Grossini dissapears in 2s";
}

void ActionCallFuncND::removeFromParentAndCleanup(CCNode* pSender, void* data)
{
    bool bCleanUp = data != NULL;
    //按回调的参数data来执行removeFromParentAndCleanup
    m_grossini->removeFromParentAndCleanup(bCleanUp);
}
其它的动画形式可以自己参考官方原例或者参考其它文章进行理解学习。