更新日志:
2014-01-31 增加了cocoStudio动画编辑器的说明
内容概述:
从今天开始,我们就正式进入cocos2d-x3.0的开发教程了,本篇的核心内容是序列帧动画。
1、首先我们建立一个新的场景类,作为我们本系列教程的一个主场景。
创建方法就是在TestGame中右键选择“添加”-》“新建项”,然后分别建立我们的.h文件和.cpp文件,我将他们命名为"GameScene",如图:
注意红框的设置,我之所以选择这种做法是因为我们建立的类必须要存在于Classes目录下,否则是无法被访问到的。如果我们使用了“添加“-》”类”这个操作,我们就会发现新建的类会自动的进入“F:\TestGame\proj.win32”目录下,而无法被AppDelegate.cpp等文件所识别,所以此处我只有使用“新建项”这个方法。当然,用这个方法也有一个弊端,那就是全部的代码都要手写。例如类名,引入包之类的,此处我诚心的向各位大牛求教,你们是否有更好的办法。相对于flashbuilder,我只能说vs这个地方做的太差了。
2、编写GameScene.h文件,代码如下:
#include "cocos2d.h"
class GameScene : public cocos2d::Scene
{
public:
//初始化方法
virtual bool init();
//调用宏CREATE_FUNC,使系统自动的为GameScene类创建一个create方法
CREATE_FUNC(GameScene);
private:
//定义一个私有方法,用来执行动画播放函数
void runAnimation();
};
3、编写GameScene.cpp,代码如下:
#include "GameScene.h"
//使用cocos2d-x的命名空间
USING_NS_CC;
bool GameScene::init(){
//调用父类的初始化方法
if (!Scene::init()){
return false;
}
runAnimation();
return true;
}
//以后将在这里进行动画函数的调用
void GameScene::runAnimation(){
}
4、修改AppDelegate.cpp,使其调用我们的主场景,代码如下:
//将AppDelegate.cpp的29行代码注释掉,替换成GameScene的create方法创建一个GameScene
//auto scene = HelloWorld::createScene();
auto scene = GameScene::create();
5、重新生成,调试。至此我们的准备工作就做完了。
1、编写AnimationManager.h文件,代码如下:
#include "cocos2d.h"
//此处的继承不知道是否是最合适的,目前几个容器的关系我还没有弄得太懂,待后期我会重新整理
class AnimationManager :public cocos2d::Sprite{
public:
//调用宏CREATE_FUNC,使系统自动的为GameScene类创建一个create方法
CREATE_FUNC(AnimationManager);
//基础序列帧动画播放
void testAnimationOne();
//plist序列帧动画播放
void testAnimationTwo();
//应用级序列帧动画播放
void testAnimationThree();
};
2、编写AnimationManager.cpp,代码如下:
#include "AnimationManager.h"
//使用cocos2d-x的命名空间
USING_NS_CC;
void AnimationManager::testAnimationOne(){
//打印日志
CCLOG("run testAnimationOne");
//此处使用了一组序列帧图片,改组图片存放于项目的Resource/actor目录下。图片的名称是从00001.png开始,至00008.png结束,
//代码与资源请参见教程索引页面的下载地址,索引页面的链接在文章的底部
Sprite* sp = Sprite::create("actor/00001.png");
sp->setPosition(ccp(170, 200));
addChild(sp);
//创建一个动画对象
Animation* animation = Animation::create();
char str[100] = { 0 };
//图片的总长度为8
for (int i = 1; i <= 8; i++){
//下面的方法是将循环中的索引拼成我们序列帧的资源名称
sprintf(str, "actor/0000%i.png", i);
animation->addSpriteFrameWithFileName(str);
}
animation->setDelayPerUnit(0.03f);//必须设置否则不会动态播放
animation->setRestoreOriginalFrame(true);//是否回到第一帧
animation->setLoops(-1);//重复次数 (-1:无限循环)
FiniteTimeAction * animate = Animate::create(animation);
sp->runAction(animate);
}
3、在GameScene.cpp中调用动画播放方法,代码如下:
//以后将在这里进行动画函数的调用
void GameScene::runAnimation(){
AnimationManager *sprite = AnimationManager::create();
addChild(sprite);
//执行基础序列帧动画播放
sprite->testAnimationOne();
}
上面的这个方法是动画播放最基础的一个原理展示方法,其核心思想就是将一组序列帧加载如内存,然后循环播放。大家可以注意到,此处使用了很多的硬编码,例如资源名称,资源长度等,另外,独立的加载每一张图片资源对于性能来说也是极其消耗的,我们在实际的项目中是不会这样去进行开发的。下面我介绍一种进阶方法。
有关plist文件,网上的介绍很多,这里不作赘述。我们只需要把它理解成xml文件即可。
本方法的原理就是,将原本细碎的序列帧拼接成一张大图,同时将各碎图的信息存储在plist文件或者其他格式的文件中,然后在代码中对碎图的信息进行解析,最后播放。这样做的好处是一次载入,减少I/O,提高速度。
1、本段使用TexturePacker工具生成了actor.png和actor.plist文件。其中actor.png就是拼接后的大图。同上,需要将这两个文件考入项目的Resource目录。
2、编写AnimationManager.cpp中的testAnimationTwo方法,代码如下:
void AnimationManager::testAnimationTwo(){
CCLOG("run testTwo");
Texture2D::PVRImagesHavePremultipliedAlpha(true);
SpriteFrameCache *spriteFrameCache = SpriteFrameCache::sharedSpriteFrameCache();
spriteFrameCache->addSpriteFramesWithFile("actor.plist");
//此处是一种优化做法,但是具体的优化度,本人尚未测试,待后期给大家一个答复
CCSpriteBatchNode *spriteBatchNode = CCSpriteBatchNode::create("actor.png");
addChild(spriteBatchNode);
//利用帧缓存创建精灵
Sprite* sp = Sprite::createWithSpriteFrameName("00001.png");
sp->setPosition(ccp(170, 200));
spriteBatchNode->addChild(sp);
//创建一个序列帧的vector
Vector<SpriteFrame *> spriteFrame;
char str[100] = { 0 };
for (int i = 1; i <= 8; i++){
sprintf(str, "0000%i.png", i);
SpriteFrame *frame = spriteFrameCache->getSpriteFrameByName(str);
//将每帧的动画信息存入vector
spriteFrame.pushBack(frame);
}
Animation* animation = Animation::createWithSpriteFrames(spriteFrame, 0.03f);
animation->setLoops(-1);
sp->runAction(Animate::create(animation));
spriteFrameCache->removeSpriteFrameByName("actor.plist");
}
3、在GameScene.cpp中调用动画播放方法,代码如下:
//以后将在这里进行动画函数的调用
void GameScene::runAnimation(){
AnimationManager *sprite = AnimationManager::create();
addChild(sprite);
//执行plist序列帧动画播放
sprite->testAnimationTwo();
}
看到此处,大家可能发现了问题,在上面的代码中依然有些数据是硬编码,例如资源的长度8。的确,我查了很久,也没有在api找到如何才能获取plist文件长度的方法。同样上面的方法也不适用于实际的开发,仅能作为原理示例代码,毕竟我们要懂得原理才能进行深入的开发。下面,我将介绍一种真正的播放方法,而这种方法将是我们在实际开发中最常用的一种。
在这里,我给大家介绍一款工具——“红孩儿工具箱”,官网地址:http://www.game2z.com/ QQ群:20970366。该工具是我继mornUI之后,所见到的第二款国人制作的优秀开发工具,在这里,我向作者表示衷心的感谢,感谢他的无私奉献。
1、利用红孩儿工具箱,的拼图和动画编辑功能,我生成了三个文件,分别是“actor_0.plist”,“actor_0.png”,“testani.plist”。具体的做法这里就不赘述,大家可以参考工具箱的使用说明。前两个文件与方法二所用的文件相同,是用来描述各碎图序列帧的信息的。第三个文件,才是本方法的重点,他在里面存储了具体的动画信息,大家可以使用工具生成后,自行查看。
2、编写AnimationManager的testAnimationThree方法,代码如下:
void AnimationManager::testAnimationThree(){
CCLOG("run testAnimationThree");
//读取拼接图片的信息plist文件
CCSpriteFrameCache *frameCache = CCSpriteFrameCache::sharedSpriteFrameCache();
frameCache->addSpriteFramesWithFile("actor_0.plist");
// Purge previously loaded animation
CCAnimationCache::purgeSharedAnimationCache();
CCAnimationCache *animCache = CCAnimationCache::sharedAnimationCache();
// 向缓存中增加动画信息
animCache->addAnimationsWithFile("testani.plist");
//读取名称为testAni的动画信息
CCAnimation *testAni = animCache->animationByName("testAni");
testAni->setRestoreOriginalFrame(true);
//创建动画
CCAnimate *anim1 = CCAnimate::create(testAni);
//创建动画序列
CCSequence *seq = (CCSequence*)CCSequence::create(anim1, NULL);
CCSprite *yezhu = CCSprite::create();
CCSpriteFrame *frame = frameCache->spriteFrameByName("00001.png");
yezhu->setDisplayFrame(frame);
CCSize winSize = CCDirector::sharedDirector()->getWinSize();
yezhu->setPosition(ccp(winSize.width / 2, winSize.height / 2));
addChild(yezhu);
// run the animation
yezhu->runAction(seq);
}
3、在GameScene.cpp中调用动画播放方法,代码如下:
//以后将在这里进行动画函数的调用
void GameScene::runAnimation(){
AnimationManager *sprite = AnimationManager::create();
addChild(sprite);
sprite->testAnimationThree();
}
至此,一个适用于应用级的动画播放就完成了。虽然其中还有一些硬编码的内容,但是这已经不再影响,我们可以通过具体的资源管理来处理这个问题。我在此再来解释一遍,首先我们的做法与方法二相同,是将“actor_0.plist”与“actor_0.png‘ 加载进来,这两个文件存储了每个序列帧的信息,然后我们再将存储动画信息的”testAni.plist"加载进来,这里存储了每个动画的序列帧的顺序及动画间隔,然后再对照前两个文件找出当前序列帧的偏移,宽高等信息,最终播放出来。
怎么样,你们发现问题了吗?那就是,为什么要使用两个plist文件才能播放动画?这个问题我百思不得其解,不过参考苹果以往那些反人类的做法,我也就释然了。所以,至此我决定自己进行第四种方法的制作,那就是将序列帧的图片信息和动画信息组合在一起成为一个文件的方法。
但是,不是在现在。在节后,我会将这件事情的优先级排到最高,尽快给出一个高效的工具。
最后讲解一下CocoStudio的动画编辑器。该工具也提供了强大的动画编辑功能,操作简单。具体的操作方法,可以参见CocoStudio的手册说明,简单的说来,就是将一组序列帧拖拽到舞台,然后导出项目,此刻就会生成三个文件,一个png文件,一个plist文件,一个ExportJson文件。png文件是拼合的大图文件,plist文件存储的是每一张序列帧图片的信息,ExportJson文件存储的则是动画的信息,这三个文件的功用基本与方法三的三个文件雷同,在此也不再进行赘述。
好了,本节教程到此结束,下一节,我将给大家讲解Cocos2d-x的ui组件。