各位大神,问一个关于cocos2d-x 3.xcocostudio动画编辑器播放的问题,求解答

关于Cocos2d-x中获取正在播放动画第几帧的问题
博客专家
关于Cocos2d-x中获取正在播放动画第几帧的问题
一。问题描述
今天在写游戏的时候,为了统一动画和攻击的时间,我取消了原先依靠时间来同步的想法(太烂了,总是不协调)。所以就上网搜了下,看看cocos2d-x中有没有提供这样的方法(汗。。。初学当中ing......)。很快就搜到了一篇文章,说是使用CCSprite中的displayFrame方法返回正在播放的动画帧,然后调用CCAnimate中的getFrames方法来获取正在播放动画的所有的CCSpriteFrame*帧,之后就可以通过指针比较来进行了。这个方法,文章的作者也发现了有点问题,所以在后面给出了改进的地方,displayFrame返回的实际上是新建的一个CCSpriteFrame,而不是原来的CCSpriteFrame*,说的抽象,看代码:
CCSpriteFrame* CCSprite::displayFrame(void)
& & return CCSpriteFrame::createWithTexture(m_pobTexture,
& & & & & & & & & & & & & & & & & & & & & &CC_RECT_POINTS_TO_PIXELS(m_obRect),
& & & & & & & & & & & & & & & & & & & & & &m_bRectRotated,
& & & & & & & & & & & & & & & & & & & & & &CC_POINT_POINTS_TO_PIXELS(m_obUnflippedOffsetPositionFromCenter),
& & & & & & & & & & & & & & & & & & & & & &CC_SIZE_POINTS_TO_PIXELS(m_obContentSize));
看出来了吧,直接是创建一个新的CCSpriteFrame对象,这么说两个对象完全不能够使用==来进行比较了。作者又想到使用CCSpriteFrame中的Texture的ID来进行比较,就算新建了一个精灵帧,用的还是自己原来的纹理m_pobTexture,看上面的代码就知道了。所以,应该能够实现,因为CCTexture里面有个getName()用来返回唯一的ID号,靠这个就应该能够进行正确的比较了,所以兴奋的去游戏中实现下。
结果,还是一样,不能同步,调试下发现,displayFrame返回的精灵帧中的纹理ID总是保持不变,这下悲催了,不知道怎么弄了。。。(不知道为什么纹理不变,难道播放动画的时候,不是将精灵的纹理替换掉,而是在原来的位置覆盖一下???不会这么烂吧??? 不明白。。。。)
二。解决问题
被逼无奈之下,我就看了下CCSprite的源码,无意之中,发现了下面的一个函数:
bool CCSprite::isFrameDisplayed(CCSpriteFrame *pFrame)
& & CCRect r = pFrame-&getRect();
& & return (r.equals(m_obRect) &&
& & & & & & pFrame-&getTexture()-&getName() == m_pobTexture-&getName() &&
& & & & & & pFrame-&getOffset().equals(m_obUnflippedOffsetPositionFromCenter));
看上面的那个函数名,就知道是判断给定的精灵帧是否进行了播放。
而且这个的判断条件更加的多,不仅判断了纹理名字是否相同,还判断了矩形区域,偏移。
(:& 又不懂了,上面那个display返回的ID保持不变,这里使用纹理的ID来进行判断 ,不还是一样不能够解决吗???)
先不管这个,游戏要紧,直接拿进游戏中试一下,
oh,My god ,还真TMD的可以哎。完全同步了 ,没有一点滞留。
以下是这个解决方法的部分代码:
CCArray* spriteArray = m_pLocateSkillAction-&getAnimation()-&getFrames(); & & //其中的m_pLocateSkillAction是一个CCAnimate的对象指针
CCAnimationFrame*tempFrame = (CCAnimationFrame*)spriteArray-&objectAtIndex(61) ; & //这里要注意了,返回的是CCAnimationFrame的指针,不是CCSpriteFrame*
if(m_Sprite-&isFrameDisplayed(tempFrame-&getSpriteFrame())) & & //m_Sprite就是正在播放动画的那个精灵
三。遗留问题
上面的isFrameDisplayed中也使用到了纹理啊,而displayFrame也是用这个m_pobTexture属性的啊,为什么这里的可以,上面的displayFrame不可以,实在不明白。。。希望有高手解释下。。。(难道是所谓的动画播放搞的鬼???)
我的热门文章
即使是一小步也想与你分享博文(361)
提出如题所示的问题,心里非常别扭,但的确是事实。因此,Cocos Studio(我目前使用的是 2.3.2)在许多方面还有改进的地方,包括与之相对应的cocos2d-x中的代码操作部分。
&&&&&&& 目前,我的试验结果发现,使用cocos2d-x 3.8.1中提供的如下方法:
ArmatureDataManager::getInstance()-&addArmatureFileInfo(filename);
&&&&&&& 无法正常加载Cocos Studio 2.3.2导出的骨骼动画资源文件。例如如下代码无法正常通过项目构建:
ArmatureDataManager::getInstance()-&addArmatureFileInfo(&DemoPlayer.csb&);
令人遗憾的例子
尽管如此,但是cocos2d-x 3.8.1的cpp-tests实例中的确提供了使用addArmatureFileInfo方法加载.csb骨骼动画文件的例子!!
是的,因为.csb文件是二进制格式,目前还找不到其反编译工具,但是,从使用简单的工具分析,cpp-tests实例中提供的示例.csb骨骼动画文件的版本与Cocos Studio 2.3.2导出的骨骼动画资源文件.csb并不一致。
下面给出Notepad++观察到的结果图的对照(第1张是Cocos Studio 2.3.2导出骨骼动画文件DemoPlayer.csb查看结果,显然版本号是2.1.0.0,第2张是cpp-tests实例中提供的示例Cowboy.csb骨骼动画文件查看结果,显然版本号是1.0.1):
为了进一步分析上述问题,我还专门把cocos2d-x 3.8.1的cpp-tests实例中提供的使用addArmatureFileInfo方法加载其提供的相应.csb骨骼动画文件的代码复制到一个简单示例工程中进行测试,的确OK。相关代码如下所示:
const&&char*&HelloWorld::m_binaryFilesNames[4]&=&{&&bear.csb&,&&horse.csb&,&Cowboy.csb&,&ccc.csb&};
const&&char*&HelloWorld::m_armatureNames[4]&=&{&&bear&,&&horse&,&Cowboy&,&Skeleton1&};
&ArmatureDataManager::getInstance()-&addArmatureFileInfo(m_binaryFilesNames[3]);
&Armature&*m_armature&=&Armature::create(m_armatureNames[3]);
&m_armature-&getAnimation()-&playWithIndex(0);
&m_armature-&setScale(1.0f);
&Size&size&=&Director::getInstance()-&getWinSize();
&m_armature-&setPosition(size.width/2,&size.height/2);
&addChild(m_armature);
对于数组中相应的前三个.csb文件(应该是老版本的STUDIO导出的骨骼动画csb文件),运行上述代码非常顺利(当然,上述addArmatureFileInfo方法调用更早的ExportJson骨骼动画文件的情况也是能够顺利运行)。事实上,cpp-tests自然也已经在我的机器上顺序调试通过(我的环境是windows 7 64bits Visual Studio 2013)。但是,对于最后那个csb文件(使用当前新版本Cocos Studio 2.3.2导出的骨骼动画文件),则根本不行,执行中断停止在addArmatureFileInfo调用的下一行。
在经过部分的源码跟踪后,我尝试着使用碎图技术生成csb文件,尽量使之与cpp-tests提供的文件形式上一致,结果也根本通不过!
太遗憾了,我就是想使用Armature及相应的如下技术:
&&&armature-&getAnimation()-&setMovementEventCallFunc(CC_CALLBACK_0(TestAnimationEvent::animationEvent,&this,&std::placeholders::_1,&std::placeholders::_2,&std::placeholders::_3));
但是,很遗憾,只能干瞪眼!没有Armature,我们根本无法使用setMovementEventCallFunc回调函数及其相应技术了。
遗憾的是,对于上述问题,官方网站上及DEMO中只字未提!
对于我目前的程序中的上述要求,我只能尝试着其他的变通方法,因为我的要求也并不高。于是我尝试着使用帧事件方法解决了上述问题。
在此,我粘贴上我的示例游戏中的相关代码。
第一部分如下:
&Node*&node2&=&CSLoader::createNode(&SplashAnimationSkeleton.csb&);
&addChild(node2);
&node2-&setPosition(Vec2(VisibleRect::center().x,&VisibleRect::center().y));
&ActionTimeline*&action2&=&CSLoader::createTimeline(&SplashAnimationSkeleton.csb&);
&node2-&runAction(action2);
&action2-&gotoFrameAndPlay(0,false);
&action2-&setFrameEventCallFunc(CC_CALLBACK_1(SplashScene::onFrameEvent,&this));
注意,上面的SplashAnimationSkeleton.csb是使用cocos studio 2.3.2创建的简单的骨骼动画文件。
我原先设计的使用setMovementEventCallFunc方法结合Armature数据想实现的是目标是:当骨骼动画播放结束,触发另一个既定事件,并在这个事件中完成另外的动画播放任务。
对于上述目标,使用帧事件应该是可以的,只是稍微麻烦一些罢了。例如,需要在studio设计器中填写帧事件数据;但是,总算还可以实现。
另外一部分相关代码如下:
void&SplashScene::onFrameEvent(Frame*&frame)
&EventFrame*&evnt&=&dynamic_cast&EventFrame*&(frame);
&if&(!evnt)
&std::string&str&=&evnt-&getEvent();
&if&(str&==&&lastFrame&)
&&Node*&butterfly_01&=&CSLoader::createNode(&ButterflyArmature_01.csb&);
&&addChild(butterfly_01,100);
&&butterfly_01-&setPosition(Vec2(VisibleRect::right().x&+&100,&0));
&&ActionTimeline*&action2&=&CSLoader::createTimeline(&ButterflyArmature_01.csb&);
&&butterfly_01-&runAction(action2);
&&action2-&gotoFrameAndPlay(0,&true);
&&Node*&p1&=&_rootLayer-&getChildByName(&Mushroom_Point&);
&&auto&&action&=&Sequence::create(
&&&MoveTo::create(2,&p1-&getPosition()),
&&&CallFunc::create(CC_CALLBACK_0(SplashScene::callback0,&this)),
&&&nullptr);
&&butterfly_01-&runAction(action);
大家看到,我在帧事件回调函数中进行判断,当动画播放到特定帧时(正是我以前要求的第一个动画播放结束时)触发另一个蝴蝶飞入动画的播放。&
通过学习与研究部分cocos2d-x 及cocos studio最新版本技术可以学习到更优秀的开发技术的同时,注定我要牺牲许多时间去“踏坑”,也许有得就有失吧。
最后,再提醒一下新手同学,示例工程中的代码部分与资源数据文件部分都有些不太明确的调用,当然,看起来官方是要尽量使用最新的c++代码来使用(或者说保护)早期studio导出的资源。但在同时,却露出了不少急于求成的“马脚”。
登录乐搏学院官网
或关注我们的官方微博,还有更多惊喜哦~
本文出自 “” 博客,请务必保留此出处
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:21724次
排名:千里之外
转载:359篇
(84)(75)(56)(53)(48)(40)(14)cocos2d(15)
使用plist文件,结合cocos2dx里面的动画类Animation来实现,这种方式比较推荐
代码如下:
void BaseLayer::testTextture(){
Size visibleSize = Director::getInstance()-&getVisibleSize();
Vector&SpriteFrame*& spriteFrameV
auto spriteFrameCache = SpriteFrameCache::getInstance();
// 这里使用plist文件
spriteFrameCache-&addSpriteFramesWithFile(&test.plist&, &test.png&);
char path[256] = { 0 };
for (int i = 1; i &= 6; ++i)
sprintf(path, &%d.png&, i);
log(&path = %s&, path);
SpriteFrame *pSpriteFrame = spriteFrameCache-&getSpriteFrameByName(path);
spriteFrameVec.pushBack(pSpriteFrame);
// 0.1那个参数必须设定,可以设定除默认值意外的任何值,如果你不设定,根本就无法播放Animate动作
// Cocos2d-x的坑还不少啊,各位需谨慎啊
auto animation = Animation::createWithSpriteFrames(spriteFrameVec, 0.1);
animation-&setDelayPerUnit(2.8f / 14.0f);//必须设置否则不会动态播放
animation-&setRestoreOriginalFrame(true);//是否回到第一帧
animation-&setLoops(-1);//重复次数 (-1:无限循环)
auto sprite = Sprite::create();
// 方法1:从SpriteFrameCache中根据名字获得对应的精灵帧,再设置
sprite-&setSpriteFrame(spriteFrameCache-&getSpriteFrameByName(&1.png&));
// 方法2:从Animation中获得对应的帧,再进行设置
AnimationCache::getInstance()-&addAnimation(animation, &dogs&);
// sprite-&setDisplayFrameWithAnimationName(&dogs&, 0);
sprite-&setPosition(Vec2(visibleSize.width / 2, visibleSize.height / 2));
addChild(sprite, 1, 1);
FiniteTimeAction * animate = Animate::create(animation);
sprite-&runAction(animate);
官方文档:
You have to tell cocos2d-x which sprite frames should be used in an animation. The easiest way for this is to create a helper method that creates a list:
Vector HelloWorld::getAnimation(const char *format, int count)
auto spritecache = SpriteFrameCache::getInstance();
Vector animF
char str[100];
for(int i = 1; i &= i++)
sprintf(str, format, i);
animFrames.pushBack(spritecache-&getSpriteFrameByName(str));
return animF
This method retrieves the animation frames from the sprite sheet that follow a given format. You can call it in the following way:
Vector frames = getAnimation(&capguy/walk/%04d.png&, 8);
The format string %04d creates 4 digit numbers prefixed with 0: , 0003,...
The call returns a list of 8 sprite frames: capguy/walk/0001.png,... capguy/walk/0008.png
Add the following code block to the bottom of the init() method in HelloWorldScene.cpp before the return true:
auto frames = getAnimation(&capguy/walk/%04d.png&, 8);
auto sprite = Sprite::createWithSpriteFrame(frames.front());
background-&addChild(sprite);
sprite-&setPosition(100,620);
auto animation = Animation::createWithSpriteFrames(frames, 1.0f/8);
sprite-&runAction(RepeatForever::create(Animate::create(animation)));
The first line is the one you already know from above — to create the animation frames. The second creates a sprite using the first frame from the list.
You add the sprite as child of the background, not as child of the scene. This ensure that the sprite is always positioned in the right spot on the background.
与动画相关的类
AnimationCache
AnimationCache类也是一个单例类,用于缓存所有的动画和动画帧。主要有以下成员函数:
* 获得单个实例
static AnimationCache* getInstance();
* 释放这个
static void destroyInstance();
* 添加一个Animation对象,并指定一个名字
void addAnimation(Animation *animation, const std::string& name);
* 从缓存中根据名字删除一个Animation
void removeAnimation(const std::string& name);
* 根据名字得到一个Animation对象
* 如果在缓存中没有对应名字的Animation对象,则返回NULL
* 得到新的Animation对象之后,需要对应的调用retain函数
Animation* getAnimation(const std::string& name);
* 从plist文件添加一个Animation对象
* 请确保在调用该函数之前,所有的帧已经被添加到SpriteFrameCache中了
void addAnimationsWithFile(const std::string& plist);
一般使用时,都是通过getInstance获得AnimationCache对象,然后通过addAnimation函数,加入Animation对象;在使用的时候,再通过getAnimation函数,传入Animation的名称,就可以从缓存中获得对应的Animation对象。
AnimationFrame
和精灵帧SpriteFrame类似,动画帧也是单张的图片,也可以通过精灵帧进行定义。这货在实际开发中,我怎么用的不是很多啊,很多时候,都是SpriteFrame啊。Animation
Animation就是动画,存储一个动画动作需要的所有帧,一会在下面的实例中看代码吧。Animate
这个才是关键啊,你定义的动画帧或者精灵帧再多,没有动起来,也是徒劳啊。一切的关键就是Animate了。Animate动画动作就是一个动作类,继承自ActionInterval类。我们可以通过Animation来创建一个Animate,然后执行这个动作了。效果是什么样子的,就看下面的吧。
总结了这么多了,该来一个实例了。看看效果吧。
封装代码ru
class BaseLayer : public Layer
BaseLayer( );
~BaseLayer( );
CREATE_FUNC( BaseLayer ) ;
bool init( ) ;
void initPoolAnimation();
void playPoolAnimation(bool isPlay);
Sprite* poolS //动画元素
Animation*
FiniteTimeAction*
void BaseLayer::initPoolAnimation(){
Size visibleSize = Director::getInstance()-&getVisibleSize();
Vector&SpriteFrame*& spriteFrameV
auto spriteFrameCache = SpriteFrameCache::getInstance();
// 这里使用plist文件
spriteFrameCache-&addSpriteFramesWithFile(&test.plist&, &test.png&);
char path[256] = { 0 };
for (int i = 1; i &= 6; ++i)
sprintf(path, &%d.png&, i);
log(&path = %s&, path);
SpriteFrame *pSpriteFrame = spriteFrameCache-&getSpriteFrameByName(path);
spriteFrameVec.pushBack(pSpriteFrame);
// 0.1那个参数必须设定,可以设定除默认值意外的任何值,如果你不设定,根本就无法播放Animate动作
// Cocos2d-x的坑还不少啊,各位需谨慎啊
animation = Animation::createWithSpriteFrames(spriteFrameVec, 0.1);
animation-&setDelayPerUnit(2.8f / 14.0f);//必须设置否则不会动态播放
animation-&setRestoreOriginalFrame(true);//是否回到第一帧
animation-&setLoops(-1);//重复次数 (-1:无限循环)
poolSprite = Sprite::create();
// 方法1:从SpriteFrameCache中根据名字获得对应的精灵帧,再设置
poolSprite-&setSpriteFrame(spriteFrameCache-&getSpriteFrameByName(&1.png&));
// 方法2:从Animation中获得对应的帧,再进行设置
AnimationCache::getInstance()-&addAnimation(animation, &dogs&);
// sprite-&setDisplayFrameWithAnimationName(&dogs&, 0);
poolSprite-&setPosition(Vec2(visibleSize.width / 2, visibleSize.height / 2));
addChild(poolSprite, 1, 1);
void BaseLayer::playPoolAnimation(bool isPlay){
if (isPlay) {
animate = Animate::create(animation);
poolSprite-&runAction(animate);
poolSprite-&stopAction(animate);
参考地址:
对应资源下载地址:&
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:68921次
积分:1230
积分:1230
排名:千里之外
原创:56篇
转载:21篇
评论:24条
(1)(2)(2)(24)(1)(3)(1)(3)(2)(3)(1)(1)(9)(9)(5)(1)(3)(6)Cocos2d-x开发(71)
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__
#include &cocos2d.h&
#include &Ball.h&
USING_NS_CC;
class HelloWorld : public cocos2d::Layer
static cocos2d::Scene* createScene();
virtual bool init();
CREATE_FUNC(HelloWorld);
#endif // __HELLOWORLD_SCENE_H__
#include &HelloWorldScene.h&
Scene* HelloWorld::createScene()
auto scene = Scene::create();
auto layer = HelloWorld::create();
scene-&addChild(layer);
bool HelloWorld::init()
if ( !Layer::init() )
Size visibleSize = Director::getInstance()-&getVisibleSize();
Vec2 origin = Director::getInstance()-&getVisibleOrigin();
// 根据模型和纹理生成3D精灵
auto sprite = Sprite3D::create(&tortoise.c3b&);
sprite-&setPosition(visibleSize / 2.0);
sprite-&setScale(0.3f);
this-&addChild(sprite);
// 给3D精灵自定义动作:在1秒钟之内,环绕x轴和y轴旋转45度,z轴不动
//sprite-&runAction(RepeatForever::create(RotateBy::create(1.0f, Vec3(45, 45, 0))));
// 播放模型动画
auto animation = Animation3D::create(&tortoise.c3b&);
auto animate = Animate3D::create(animation);
// 播放一次模型动画
//sprite-&runAction(animate);
// 永远播放模型动画
sprite-&runAction(RepeatForever::create(animate));
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:81292次
积分:3289
积分:3289
排名:第8743名
原创:257篇}

我要回帖

更多关于 cocos studio 动画 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信