请教 如何在Cocos2D里面切换魔发精灵 动画片动画

分享将Flash动画输出到Cocos2d Actions的方法
发布时间: 17:34:32
作者:Toni Sala
关于将Flash动画输出到Cocos2d动作的话题早在数月前就存在我的脑海中。基于动作的精灵表格在精灵大小和帧率上极有局限性。你的帧数越多,消耗的内存就越大。精灵越大,你需要的内存也就越多。
但实际上,内存使用率还算不上我工作至今所遇到的问题。所以才会将Flash动画输出到Cocos2d这个任务拖延到现在。
问题:《Muster my Monsters》以及高质量的动画
《Muster my Monsters》是一款玩法极为简单的游戏:它实际上是一款甚为精致的石头剪子布游戏。应该说,它是一款很棒的游戏,因为它迫使我解决将Flash动画输出到Cocos2d这个问题。
这款游戏的美术资产(游戏邦注:包括所有角色设计和动画)是由Javi Sanz这名出色的Flash美术师和开发者完成。
游戏中的角色很大。在放大时,它们实际上占据了70%-80%的屏幕面积。这些角色是极为精细的Flash矢量图片,其动画也十分流畅(得益于Flash插值/渐变系统)。这也正是精灵表格十分之大的原因。
现在,给你一些粗略的数据,在Windows Phone版本中我们有2个角色,但我们计划在iOS版本中增加为5-8个角色。每个角色有9个动画。平均每个动画约有16-18帧,每帧有420*400的图片。我需要为每个角色每个动画创建一个的精灵表格。
最终的二进制下载文件大小会受到限制,执行期间的内存消耗也同样如此。所以,即使是用压缩后的纹理格式,这也还是个问题。
flash to cocos2d(from indiedevstories)
解决方法:过程生成动画
我脑海中第一个想到的就是使用现成的工具制作过程生成动画,并其将输入我的iOS-Cocos2d游戏引擎。我发现两个很有趣的工具:LevelHelper以及新版CocosBuilder。
这两种工具都有一些基本的过程生成动画技术支持。但目前他们主要用于制作菜单按钮和UI等元素的动画。所以,虽然我们可以使用这些工作制作《Muster my Monsters》的动画,但这却并非最佳解决方案。我还考虑使用Spriter,但该项目的早期阶段却并不允许。
更重要的是,Javi已经是一个很有才华的Flash美术师,所以将Flash动画输出到Cocos2d似乎是一个可行的方法。
首次尝试:输入Flash XML文件
我从未使用Flash开发过内容,所以我的第一次尝试就是避免与之产生任何交集。Flash拥有一个允许你用XML格式输出整个Flash项目的强大功能。这看起来似乎就是最便捷的方法,因为这便于我专注于iOS-Cocos2d方面的事情。
但是,Flash XML文件格式很糟糕。它极为巨大并且如果你使用了一些功能就会改变基本的布局。每次我有一个输入者的运行版本时,都会发现运用于Flash项目时,会有一个新功能更改了所有的XML文件格式。即使创造更多XML文件,也会分裂项目元素。
再次尝试:使用Action Script输出Flash动画
此时Javi建议针对这个Flash项目编写一个简单的Action Script运行每一帧,寻找与角色一致的对象并获得转换信息。这听起来像是一个很好的替换方法,所以我最终决定学习Flash脚本和开发的基本方法。
事实证明这值得一试。实际上,这也是我最终采用的解决方案。我有一个简单的动作脚本可用每一帧动画的每个对象的转变信息编写plist(或XML)文件。
这意味着一种策略变化。在首次尝试时,我们试图输入Flash XML文件格式,我试图将所有的Flash功能映射到Cocos2d动作。所以,我尝试获取关键帧、插值帧和清除函数等高端信息。
但是,动作脚本策略产生的是逐帧的信息。这是有点粗糙的信息。所以,我不需要关心高端功能。这只是逐帧的动画描述。在一定意义上,与使用精灵表格非常相似。
这种方法有些非常有趣的优势:
*实际上减少了所需内存
*支持极高质量的动画
*支持动画师使用包括motion tweens、classic tweens甚至是基于骨骼的动画等所有Flash动画功能
*在iOS-cocos2d方面减少了动画引擎的复杂性,并令其更易扩展和维护
*在我的案例中,支持Javi持续使用Flash工具
punch-win(from indiedevstories)
在cocos2d方面
实际上,我是在Flash完成困难的内容。所以,只要我准备好plist文件,就只差解析的问题了。我为自己的引擎(用于模拟移动对象、帧和动画)创建了一些类。
然后使用这个模拟类创造一个由数个精灵组成的CCNode(游戏邦注:针对角色的每个身体部分,例如头、臂、前臂、身体、足等)。最后,我针对每个模拟帧创造了一系列可插入转变信息(定位、旋转和缩放)的cocos2d系列动作(CCSequence)。
所以实际小,最终的“角色”类是一个含有海量CCActions,能够响应Flash项目所创建动画的CCNode。你可以使用以下方法播放动画:
1 [character playAnimationWithKey:@"attack"];
characters(from indiedevstories)
针对Mac OS的播放器
这种方法的主要问题在于获取转变数据的Action Script以及cocos2d引擎制定了一些假定条件。Flash动画师必须清楚这些假定或局限性,并据此创造动画。
所以他在制作动画时需要获得一些反馈。他需要知道自己用Flash所做的东西确实能够运行于游戏引擎。这也正是我为何开发一个Mac OS播放器的原因,它可以让你动态馈送描述动画的plist文件以及响应不同测试角色的资产。
我想概括这种方法的主要优点与缺点以解决“向cocos2d actions输出Flash动画”的问题。
*极大减少所需内存
*支持极为高端的动画
*允许动画师使用包括motion tweens、classic tweens甚至是基于骨骼的动画等所有Flash动画功能
*在iOS-cocos2d方面减少了动画引擎的复杂性,并令其更易扩展和维护
*在我的案例中,支持Javi持续使用Flash工具
*令我无需开发定制动画工具
*在一定程度上,我们使用Flash Authoring工具作为游戏引擎的动画编辑器。虽然这可以节省我们开发定制工具的精力,但也意味着我无法更多地控制美术人员所制作的动画。我的意思是说,尽管我已经撰写了一份详细的文件解释了针对我们的引擎,使用Flash作为动画编辑器的指导原则和局限性,但Javi可能还是出乎我预料地使用了一些并不在支持范围的功能或角色设置。
我打算使用这种工具创造菜单等游戏屏幕。与CocosBuilder的用法相似,但会使用Flash Authoring工具作为场景编辑器。这可以让我们在Flash中使用其所提供的高级动画功能设置游戏屏幕。
游戏邦注:原文发表于日,所涉事件及数据以当时为准。(本文为游戏邦/编译,拒绝任何不保留版权的转载,如需转载请联系:游戏邦)
Exporting Flash Animations to Cocos2d Actions
Posted on July 18, 2012 by Toni Sala
Exporting Flash animations to cocos2d actions is a topic that has been in my mind since some months ago. Spritesheets based animations are very limited in terms of sprites size and frame rate. The more frames you have, the more memory you consume. And the bigger the sprites, the more memory you need as well.
However, actually, memory usage has not been an issue in the projects I have worked so far. So, exporting Flash animations to cocos2d is a task that I have been procrastinating… till now.
The problem: Muster my Monsters and high quality animations
Muster my Monsters is a very simple game in terms of gameplay: it is basically a rock-paper-scissors game with a nice dress. Actually, a very nice one because it has forced me to solve the problem of exporting Flash animations to cocos2d.
Here you have a video preview of the game that shows the kind of animations that the game features. By the way, all the game art (including all character design and animation) is done by Javi Sanz, an incredibly talented Flash artist and developer.
As you can see, the characters are very big. Actually, they take about 70-80% of the screen size when the zoom effect is active. The characters are extremely detailed Flash vector images and the animations are very smooth (due to the Flash interpolating/tweening system). That’s why the resulting spritesheets are huge.
So, to give you some rough numbers, in the Windows Phone version we have 2 characters but we plan to increase it to 5-8 for the iOS version. Every character has 9 animations. On average, every animation has about 16-18 frames and every frame is a 420×400 image. I needed to build a
spritesheet for every animation of every character…
The download size of the resulting binary would be prohibitive. And also the memory consumed during execution. So, even using a compressed texture format, this is kind of a problem :p
The solution: procedural animation
The first thing that came to my mind was to use some already existing tool that will allow us to create procedural animations and import them into my iOS-cocos2d game engine. I found out two interesting tools: LevelHelper and the new version of CocosBuilder.
Both tools has some basic procedural animation support. But, currently, they are intended to animate things like menu buttons and UI elements in general. So, although, it would be possible to create the animations in Muster my Monsters using these tools, it would probably not be the best solution. I also considered Spriter, but the early stage of the project put me back from it.
Moreover, Javi is already a talented Flash artist so exporting Flash animations to cocos2d seemed to be the way to go.
First try: Importing Flash XML file
I have never developed in Flash, so my first attempt was to avoid all kind of interaction with it :p Flash has a nice feature that allows you to export an entire Flash project in XML format. It
seemed the easiest solution as it will allow me to focus on the iOS-cocos2d side.
However, the Flash XML file format is hell. It’s extremely huge and the basic layout changes if you use some features or others. Every time I had a working version of the importer, I found a new feature that, when used in the Flash project, changed all the resulting XML file format. Even creating more XML files, fragmenting the project elements.
Second try: exporting Flash animations using Action Script
At this point Javi suggested to write a simple Action Script that go though every frame of the Flash project timeline, looking for the objects that conform the character and getting the transformation information. This sounded like a very good alternative, so I finally decided to learn the basics of Flash scripting and development
It was worth it. Actually, this is the solution I finally adopted. I have a simple action script that is able to write a plist (or XML) file with the transformation information of every object in the scene for every frame of the animation.
This implies a change of strategy. On the first try, when attempting to import the Flash XML file format, I was trying to map all the Flash features to cocos2d actions. So, I was trying to get high
level information such as keyframes, interpolation frames and ease functions.
However, the action script strategy produces frame by frame information. It is kind of raw information. So, I don’t need to care about high level features. It is just a frame by frame animation
description. In some sense, it is very similar to using spritesheets!
This approach has some very interesting advantages:
Actually reduces the needed memory
Allows for extremely high quality smooth animations.
Allows the animator to use all the Flash animation features including motion tweens, classic tweens or even bones-based animation.
Reduces the complexity of the animations engine in the iOS-cocos2d side and makes it easily scalable and maintainable.
In my particular case, allows Javi to keep working in Flash.
I went from this (x20):
Size reduced by 100 times! Great!
On the cocos2d side
Actually, the hard work is done on the Flash side. So, once I have the plist file ready, it is only a matter of parsing it. I created a few classes for my engine that model movable objects, frames and animations.
Then I use this model classes to create a CCNode that is composed by a few sprites, one for each body part of the characters (head, arm, forearm, body, foot…). Finally, I create a bunch of cocos2d sequence actions (CCSequence) that interpolate the transform information (position, rotation and scale) for every modelled frame.
So, basically, the resulting “Character” class is a CCNode with a dictionary of CCActions corresponding to the animations created on the Flash project. You can play the animations with a method like this:
?1 [character playAnimationWithKey:@"attack"];
A desktop player for Mac OS
The main problem of this approach is that the Action Script that gets the transformation data and the cocos2d engine make some assumptions. The Flash animator must be aware of these assumptions or limitations and needs to create the animations according to them.
So he needs some kind of feedback while creating the animations. He needs to know that what he is doing on Flash, is actually working on the game engine. That’s why I have also developed a Mac OS Player which you can feed dynamically with plist files describing the animation and the assets corresponding to the different characters you want to test.
Following, you have a video that shows the whole process: from creating some (simple) animations with Flash to viewing it with the Desktop player.
Conclusions
I would like to put together the main advantages and disadvantages of this method to solve the “exporting Flash animations to cocos2d actions” issue.
Advantages:
Drastic reduction of the needed memory
Allows for extremely high quality smooth animations.
Allows the animator to use all the Flash animation features including motion tweens, classic tweens or even bones-based animation.
Reduces the complexity of the animations engine in the iOS-cocos2d side and makes it easily scalable and maintainable.
In my particular case, allows Javi to keep working in Flash.
Allows me to avoid investing time on developing a custom animation tool.
Disadvantages:
In some sense, we are using the Flash Authoring tool as an animation editor for our game engine. Although this allows us to avoid investing efforts on developing a custom tool, it also implies that I have less control on the kind of animations that the artist produces. I mean, I have written a detailed document explaining the guidelines and limitations on using Flash as an animation editor for our engine, but it could be the case that Javi uses some not supported feature or layouts the character in a way that I have not anticipated.
Future work
I plan to also use this tool to create game screens such as menus. Similar to CocosBuilder but, again, using the Flash Authoring tool as an scenes editor. This will allow us to layout game screens in Flash using the advanced animation features that Flash provides.()
CopyRight Since 2010 GamerBoom All rights reserved &&闽ICP备&号-1gyenty18 的BLOG
用户名:gyenty18
文章数:17
访问量:6456
注册日期:
阅读量:5863
阅读量:12276
阅读量:420612
阅读量:1108910
51CTO推荐博文
接上一节,在GameScene.h中声明:#pragma once
#include "cocos2d.h"
USING_NS_CC;
class GameScene : public CCLayer
GameScene(void);
~GameScene(void);
//重写init()方法
virtual bool init();
//必须重写scene方法
static CCScene* scene();
//相当于create函数,重写了CCLayer里的create函数
CREATE_FUNC(GameScene);
virtual void registerWithTouchDispatcher(void);
virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent);
virtual void ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent);
virtual void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent);
virtual void ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent);
//成员变量
};并在GameScene.cpp的init()中设置: this-&setTouchEnabled(true); 然后在registerWithTouchDispatcher(void)函数中注册Touch事件:CCDirector::sharedDirector()-&getTouchDispatcher()-&addTargetedDelegate(this, 0, true);完整代码如下:#include "GameScene.h"
GameScene::GameScene(void){}
GameScene::~GameScene(void){}
CCScene* GameScene::scene()
CCScene* scene =CCScene::create();
GameScene* gameScene = GameScene::create();
scene-&addChild(gameScene);
bool GameScene::init()
if(!CCLayer::init())
//获取屏幕大小
CCSize size = CCDirector::sharedDirector()-&getWinSize();
//从图片创建一个精灵
CCSprite* map = CCSprite::create("bg.png");
//设置精灵在场景中的位置
map-&setPosition(ccp(size.width / 2, size.height / 2));
//添加精娄到场景中
this-&addChild(map, 0);
CCArray* spriteArray = CCArray::createWithCapacity(4);
for(i = 0; i & 4; i++)
//截取图片部分
CCSpriteFrame* spriteFrame = CCSpriteFrame::create("lubi.png",
CCRectMake(i * 80, 80 * 2, 80, 80));
spriteArray-&addObject(spriteFrame);
//精精灵添加到场景中,默认第一帧图片
if(i == 0){
sprite = CCSprite::createWithSpriteFrame(spriteFrame);
sprite-&setPosition(ccp(size.width / 2 - sprite-&getContentSize().width,
size.height / 2 + sprite-&getContentSize().height));
this-&addChild(sprite);
//创建动画
CCAnimation* spriteAnimation = CCAnimation::createWithSpriteFrames(spriteArray, 0.2f);
sprite-&runAction(CCRepeatForever::create(CCAnimate::create(spriteAnimation)));
this-&setTouchEnabled(true);
void GameScene::registerWithTouchDispatcher(void)
CCDirector::sharedDirector()-&getTouchDispatcher()-&addTargetedDelegate(this,0,true);
bool GameScene::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent)
sprite-&setPosition(ccp(pTouch-&getLocation().x, pTouch-&getLocation().y));
void GameScene::ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent)
void GameScene::ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent)
void GameScene::ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent)
}在触摸的时候,更新精灵的位置。
了这篇文章
类别:┆阅读(0)┆评论(0)
本文收录至博客专题:《》Cocos2D-JS 动作回调取得当前动作精灵 - 简书
Cocos2D-JS 动作回调取得当前动作精灵
今天碰到一个需求,要在动作结束的回调函数中取到当前动作的精灵,实现在执行动作之后自动 remove 掉这个精灵。查了一下,没有结果。然后看了一下 API ,发现利用 cc.CallFunc 构造函数中的 data 参数可以解决这个问题。下面是 cc.CallFunc 的构造函数和 init 方法的实现:
ctor:function(selector, selectorTarget, data){
cc.FiniteTimeAction.prototype.ctor.call(this);
if(selector !== undefined){
if(selectorTarget === undefined)
this.initWithFunction(selector);
else this.initWithFunction(selector, selectorTarget, data);
initWithFunction:function (selector, selectorTarget, data) {
if (selectorTarget) {
this._data =
this._callFunc =
this._selectorTarget = selectorT
else if (selector)
this._function =
selector 和 selectorTarget 不赘述。注意到在 init 方法中,将 ctor 方法传入的 data 赋给了 this 的 _data 。再看它的 execute 方法 :
execute:function () {
if (this._callFunc != null)
//CallFunc, N, ND
this._callFunc.call(this._selectorTarget, this.target, this._data);
else if(this._function)
this._function.call(null, this.target);
在执行这个回调的时候将 this._data 传入了原方法中,这样我们就可以将当前动作精灵指针传入回调函数,并在回调中取得它。
先构造一个动作队列并运行,代码如下:
var actionSprite = new cc.Sprite("res/me.png");
actionSprite.setAnchorPoint(0, 0);
actionSprite.x = 0;
actionSprite.y = 0;
this.addChild(actionSprite, 1);
var moveAction0 = cc.MoveBy(0.5, cc.p(cc.winSize.width/2, 0));
var moveAction1 = cc.MoveBy(0.5, cc.p(0, cc.winSize.height/2));
var moveAction2 = cc.MoveBy(0.5, cc.p(-cc.winSize.width/2, 0));
var moveAction3 = cc.MoveBy(0.5, cc.p(0, -cc.winSize.height/2));
var actionCallbackFunction = cc.CallFunc(this.actionCallbackFunction, this);
var actionFinishCallbackFunction = cc.CallFunc(this.actionFinishCallFunction, this);
var actionArray = [];
actionArray.push(moveAction0);
actionArray.push(actionCallbackFunction);
actionArray.push(moveAction1);
actionArray.push(actionCallbackFunction);
actionArray.push(moveAction2);
actionArray.push(actionCallbackFunction);
actionArray.push(moveAction3);
actionArray.push(actionFinishCallbackFunction);
var actionSequence = cc.Sequence(actionArray);
actionSprite.runAction(actionSequence);
actionCallbackFunction : function(){
cc.log("Action Callback Function");
actionFinishCallFunction : function(){
cc.log("Action Finish Callback Function");
下面对以上代码做一下小修改,来取得执行动作的精灵。首先我们在构造回调函数时要将精灵的指针作为 data 参数传入:
var actionCallbackFunction = cc.CallFunc(this.actionCallbackFunction, this, actionSprite);
var actionFinishCallbackFunction = cc.CallFunc(this.actionFinishCallFunction, this, actionSprite);
其次我们要在定义回调方法的参数表中加入 data ,以便在函数实现中使用它:
actionCallbackFunction : function(data){
cc.log("Action Callback Function");
if (data != null && data != "undefine"){
cc.log("Get Data");
cc.log(typeof data);
cc.log(data.x);
cc.log(data.y);
actionFinishCallFunction : function(data){
cc.log("Action Finish Callback Function");
if (data != null && data != "undefine"){
cc.log("Get Data");
cc.log(typeof data);
cc.log(data.x);
cc.log(data.y);
data.removeFromParent();
运行试试看~
控制台这一部分输出为:
JS: Action Callback Function
JS: Get Data
JS: object
JS: Action Callback Function
JS: Get Data
JS: object
JS: Action Callback Function
JS: Get Data
JS: object
JS: Action Finish Callback Function
JS: Get Data
JS: object
看到在每一次动作执行完毕后输出 data 类型为 object 和精灵当前的坐标,最后的 removeFromParent() 也成功了。
接下来试一下来构造一个函数,返回一个动作队列。这样,只要创建动作队列然后在精灵上 runAction 就自动实现了精灵动作并在动作结束之后自动清掉这个精灵。创建动作队列的代码如下:
createActionSequence : function(actionSpritePointer){
var moveAction0 = cc.MoveBy(0.5, cc.p(cc.winSize.width/2, 0));
var moveAction1 = cc.MoveBy(0.5, cc.p(0, cc.winSize.height/2));
var moveAction2 = cc.MoveBy(0.5, cc.p(-cc.winSize.width/2, 0));
var moveAction3 = cc.MoveBy(0.5, cc.p(0, -cc.winSize.height/2));
var actionCallbackFunction = cc.CallFunc(this.actionCallbackFunction, this, actionSpritePointer);
var actionFinishCallbackFunction = cc.CallFunc(this.actionFinishCallFunction, this, actionSpritePointer);
var actionArray = [];
actionArray.push(moveAction0);
actionArray.push(actionCallbackFunction);
actionArray.push(moveAction1);
actionArray.push(actionCallbackFunction);
actionArray.push(moveAction2);
actionArray.push(actionCallbackFunction);
actionArray.push(moveAction3);
actionArray.push(actionFinishCallbackFunction);
var actionSequence = cc.Sequence(actionArray);
return actionS
参数 actionSpritePointer 要在创建时传入要执行这个动作队列的精灵,这样,就能把这个精灵的指针传入回调函数中。创建动作队列并执行的代码如下:
var actionSprite0 = new cc.Sprite("res/me.png");
var actionSprite1 = new cc.Sprite("res/me.png");
actionSprite0.setAnchorPoint(0, 0);
actionSprite1.setAnchorPoint(1, 0);
actionSprite0.x = 0;
actionSprite0.y = 0;
actionSprite1.x = cc.winSize.width/2;
actionSprite1.y = 0;
this.addChild(actionSprite0);
this.addChild(actionSprite1);
var actionSequence0 = this.createActionSequence(actionSprite0);
var actionSequence1 = this.createActionSequence(actionSprite1);
cc.log(typeof actionSequence0);
actionSprite0.runAction(actionSequence0);
actionSprite1.runAction(actionSequence1);
谢谢观赏,如果有错误,欢迎指正~

参考资料

 

随机推荐