Cocos2dx学习笔记(9)——序列帧动画

本章知识较多,总结了之前所讲的知识,内容较复杂。

本文仅供个人记录和复习,不用于其他用途

与动画相关的类

  • Animation,存储动画相关信息
  • AnimationCache,动画缓存
  • AnimationFrame,动画帧
  • Animate,动画动作
  • SpriteFrame,精灵帧
  • SpriteFrameCache,精灵帧缓存

3.0版本的改变

  • 去掉CC
  • 单例改为getInstance(),销毁单例改为destoryInstance()
  • createWithSpriteFrames()中的CCArray参数改为VectorSpriteFrame指针的引用
  • create()setFrames()getFrames()中的CCArray参数改为VectorAnimationFrame指针的引用
  • addSpriteFrameWithFileName()改为addSpriteFrameWithFile()

动画帧

所谓的动画帧,就是在精灵帧的基础上,设置每帧之间的间隔帧数。

1
2
3
4
5
6
7
8
9
10
11
CCArray* array = new CCArray();
char str[] = "xxx.png";
CCSpriteFrame* frame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(str);
CCAnimationFrame* animationFrame = new CCAnimationFrame();
animationFrame->setSpriteFrame(frame);
animationFrame->setDelayUnits(i);
array->addObject(animationFrame);

创建一个数组,将图片加载入精灵帧缓存,同时创建动画帧,使用精灵帧初始化动画帧,设置每帧之间间隔帧数,最后将动画帧加入到数组。无论是动画帧还是精灵帧(实际上动画帧就是精灵帧,精灵帧的内部创建的动画帧间隔为1),一般来讲都是先载入缓存,随后加入到数组中,然后对Animation类进行初始化。

Animation类实例化

Animation类的初始化大致有下述三种方法,分别是创建空动画、使用精灵帧创建、使用动画帧创建对象。

1
2
3
4
5
6
7
8
9
10
// 创建一个空动画
CCAnimation::create();
// 使用CCSpriteFrame精灵帧数组,单位帧间隔delay秒
// 实际上在内部创建了动画帧CCAnimationFrame,且间隔帧数为:1
CCAnimation::createWithSpriteFrames(CCArray* arrayOfSpriteFrameNames, float delay = 0.0f);
// 使用CCAnimationFrame动画帧数组,单位帧间隔delayPerUnit秒,重复次数loops
// 其中loops:0为执行动画;1为执行一次,……,-1为无限循环
static CCAnimation* create(CCArray *arrayOfAnimationFrameNames, float delayPerUnit, unsigned int loops = 1);

关于如何添加动画帧,有以下四种方法:

1
2
3
4
5
6
7
8
9
10
11
12
// 使用精灵帧来添加一个动画帧,间隔帧数:1
void addSpriteFrame(CCSpriteFrame *pFrame);
// 使用图片资源(jpg,png等)来添加一个动画帧,间隔帧数:1
void addSpriteFrameWithFileName(const char *pszFileName);
// 从图片纹理中截取CCRect部分区域的图片,间隔帧数:1
void addSpriteFrameWithTexture(CCTexture2D* pobTexture, const CCRect& rect);
// 直接设置动画帧CCAnimationFrames数组
void setFrames(CCArray* pAnimationFrames);
CCArray* getFrames();

下面介绍一下其他的属性设置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 设置单位帧间隔delayPerUnit秒
void setDelayPerUnit(float);
float getDelayPerUnit();
// 间隔帧数的总和totalDelayUnits
// 即:每个精灵帧到下一精灵帧的间隔帧数之和
float getTotalDelayUnits();
// 动画总时间:delayPerUnit * totalDelayUnits
float getDuration();
// 设置动画帧CCAnimationFrames数组
void setFrames(CCArray* pAnimationFrames);
CCArray* getFrames();
// 当动画结束时,是否还原为第一帧
void setRestoreOriginalFrame(bool);
bool getRestoreOriginalFrame();
// 动画执行次数
// 0表示不执行动画,1表示执行1次,……,-1表示无限循环
void setLoops(unsigned int );
unsigned int getLoops();

Animate类

事实上,光有动画不行,必须有动画动作来执行它:

1
2
CCAnimate* animate = CCAnimate::create(animation);
sprite->runAction(animate);

这里是让一个精灵来执行动画动作,注意,一般来讲要将精灵设置为动画的第一张图。

具体实现

我们可以直接添加所有图片来创建动画:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
bool T05Animation::init()
{
if (!CCLayer::init())
{
return false;
}
CCSize winsize = CCDirector::sharedDirector()->getWinSize();
CCSprite *bg = CCSprite::create("sprite/background.jpg");
bg->setAnchorPoint(ccp(0, 0));
bg->setPosition(ccp(0, 0));
addChild(bg);
// 创建精灵sprite,设置为动画的第一张图
CCSprite* sprite = CCSprite::create("sprite/framecache/z_1_attack_01.png");
sprite->setPosition(winsize / 2);
this->addChild(sprite);
// 创建CCAnimation
CCAnimation* animation = CCAnimation::create();
// 直接添加图片资源
for (int i = 1; i != 11; i++)
{
char str[50];
sprintf(str, "sprite/framecache/z_1_attack_%02d.png", i);
animation->addSpriteFrameWithFileName(str); //添加动画帧
}
// 设置属性
animation->setRestoreOriginalFrame(true); // 还原第一帧
animation->setDelayPerUnit(2.0 / 18.0); // 单位帧间隔
animation->setLoops(-1); // -1无限循环
// 创建CCAnimate
CCAnimate* animate = CCAnimate::create(animation);
// 执行动画动作
sprite->runAction(animate);
}

如果精灵帧的话,只需要更改添加资源的步骤以及单位帧间隔:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 将plist批处理的多张图片,添加到精灵帧缓冲池中
CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("sprite/zombie.plist");
// 创建精灵帧CCSpriteFrame数组
CCArray* array = new CCArray();
for(int i = 1; i != 11; i++)
{
char str[50];
sprintf(str, "z_1_attack_%02d", i);
CCSpriteFrame* frame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(str);
array->addObject(frame);
}
// 使用精灵帧数组创建,单位帧间隔为2.0/14.0秒
CCAnimation* animation = CCAnimation::createWithSpriteFrames(array, 2.0/14.0);

使用动画帧其实就是用精灵帧初始化而已:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 将plist批处理的多张图片,添加到精灵帧缓冲池中
CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("sprite/zombie.plist");
// 创建动画帧CCAnimationFrame数组
CCArray* array = new CCArray();
for(int i =1; i != 11; i++)
{
char str[50];
sprintf(str, "z_1_attack_%02d", i);
CCSpriteFrame* frame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(str);
CCAnimationFrame* animationFrame = new CCAnimationFrame();
animationFrame->setSpriteFrame(frame); //设置精灵帧
animationFrame->setDelayUnits( i ); //设置间隔帧数
array->addObject(animationFrame);
}
// 使用动画帧数组创建,单位帧间隔0.2秒
CCAnimation* animation = CCAnimation::create(array, 0.2f);

最后效果

如果没问题的话,就能看见小僵尸在愉快的啃空气→_→
效果图

注意事项

这里讲一个内存优化的知识,我们知道,将一张.plist的大图加载进入缓存时,图片越大,那么消耗的资源越多。让我们先看看下面这张图片:

我们可以看到,这张图片有很多空白的地方被浪费了。于是有人可能会问,能不能这么做呢:

确实,如果按照图中箭头方向进行裁剪,图片无疑被大大的缩小了。但是,这张图片是用TexturePacker软件做出来的,难道这个软件有错误的地方吗?很显然不是,因为TexturePacker这么做是为了符合Cocosdx的要求。

事实上,Cocos底层加载.plist文件时,规格都是用的2的n次方。比如我们之前用到的僵尸图片,就是256×512,而这个植物的图片则是256×256。举个例子,如果长和宽为256时无法摆放所有精灵,那么就会尝试长为512的情况。如果还不行,那么就尝试长和宽都为512。这样一来,图片就大了不少。当精灵越多,图片越大时,经常发生这种状况。哪怕你的精灵只多了一个,但只要无法放下,那么就会尝试下一级。我们知道,2的n次方成指数型增长,越到后面数字变化越大,对于内存的消耗自然不用说。因此,我们在制作图片时,应当考虑到这些问题。