spine cocos2d-x runtime修改:支持挂接CCNode到slot,并跟随动画(包括TSR, color, alpha)
2014-03-22 14:39
531 查看
spine的cocos2d-x runtime不是专门为cocos2d-x写的,核心部分是一个通用的spine c runtime,所以对于cocos2d-x的支持不是很好。当然如果只是播个动画啥的也足够了,但是真实使用场景中会有各种需求。比如我们现在需要在spine动画的某个骨骼上绑定一个CCSprite。因为spine的骨骼并不是一个CCNode,所以无法直接绑定,因此需要对spine runtime做一些修改。
核心想法是对于需要绑定的骨骼(其实是slot),生成一个CCNodeRGBA,然后把这个CCNodeRGBA作为CCSkeleton的child,根据骨骼的SRT更新node的TSR,根据slot的rgba设置node的RGBA。这样就可以将任意一个CCNode,如一个CCSprite挂接到该slot/bone上面。如果需要同时支持color动画,则要递归设置node的setCascadeColorEnabled和setCascadeOpacityEnabled。
为了以后升级合并官方runtime方便,我将修改限定在CCSkeleton.h和CCSkeleton.cpp文件中。
CCSkeleton.h中添加:
方法getNodeForSlot用来根据一个slot name获取一个CCNodeRGBA,如果node不存在则创建。
解释一下为什么不获取骨骼而是获取slot。因为spine runtime中一个骨骼可以带有多个slot,并且除了TSR动画(位移缩放旋转),我们还需要挂接上去的Node能支持Color动画(包含alpha),
那么slot就可以满足条件,并且从slot很容易获取相应的bone。也因为如此,使用CCNodeRGBA而不是CCNode。
我们使用m_slotNodes来保存slot name到slot和node的映射。
下面是CCSkeleton.cpp中的实现:
getNodeForSlot是被客户代码调用的,使用场景是创建CCSkeletonAnimation后,需要将某个CCNode挂接到某个slot上一起动画。此时调用getNodeForSlot获取该slot对应的CCNodeRGBA。
并且使用获取到的CCNodeRGBA作为父node来执行addChild(需要挂接的node)。
getNodeForSlot的实现很简单:如果该slot name对应的CCNodeRGBA不存在,则创建一个并且放入map中。
之后就剩下最核心的工作了,根据bone和slot来更新CCNodeRGBA。按理说,根据骨骼更新transform的代码应该放到spSkeleton_updateWorldTransform中,但是我不想多修改一个文件,所以我直接放到了CCSkeleton::draw ()中,虽然不那么完美,但是直接有效。另外如果放到CCSkeleton::update中其实是会慢一帧的,所以也不能用。
这段代码遍历所有我们添加的node,并且找出他所绑定的slot和slot所属的bone。使用bone的x,y,rotation,scale来设置node的相应属性,使用slot的rgba来设置node的opacity和color。
这里的rotation需要反转一下,因为spine runtime的旋转正方向和cocos2d-x不一样。
几乎全部搞定了,但是此时如果将一个CCSprite挂接到getNodeForSlot获取的node上去动画,发现颜色和alpha并不会改变,这是由于我们还没设置CascadeColor和CascadeOpacity,
下面这个方法是从cocos2d-x testcpp工程中拿来的,可以将一个CCNode和其子node递归设置级联透明度和颜色,前提是该node实现了CCRGBAProtocol。因为我们创建的是CCNodeRGBA所以肯定是支持的了,而CCSprite也是支持的,所以再将sprite挂接到rgba node后,执行setEnableRecursiveCascadingRGBA(rgba_node,true)再播放动画,sprite的颜色和alpha就会跟着动画变化了。
基本的内容都说完了,那么这个功能的作用是啥呢?可以发挥各种想象力,在这里我要喷一下spine的skin功能,这个功能虽然想法很好,但是局限性太大,必须在编辑器里面将skin创建好并绑定。
如果skin很少还好说,如果有非常多的skin,并且以后游戏内容还是动态更新下载的,就很不方便了,这种大批量的情况下,编辑器里面的操作远远没有配置表方便实用。
当然spine是一个很好的编辑器,我非常喜欢,只是由于作者精力有限并且也不可能了解到所有其他人的需求,所以关键时候我们还得自己动手。
核心想法是对于需要绑定的骨骼(其实是slot),生成一个CCNodeRGBA,然后把这个CCNodeRGBA作为CCSkeleton的child,根据骨骼的SRT更新node的TSR,根据slot的rgba设置node的RGBA。这样就可以将任意一个CCNode,如一个CCSprite挂接到该slot/bone上面。如果需要同时支持color动画,则要递归设置node的setCascadeColorEnabled和setCascadeOpacityEnabled。
为了以后升级合并官方runtime方便,我将修改限定在CCSkeleton.h和CCSkeleton.cpp文件中。
CCSkeleton.h中添加:
class CCSkeleton { public: cocos2d::CCNodeRGBA* getNodeForSlot(const char* slotName); private: struct sSlotNode { spSlot* slot; cocos2d::CCNodeRGBA* node; }; typedef std::map<const char*,sSlotNode> SlotNodeMap; typedef SlotNodeMap::iterator SlotNodeIter; SlotNodeMap m_slotNodes; };
方法getNodeForSlot用来根据一个slot name获取一个CCNodeRGBA,如果node不存在则创建。
解释一下为什么不获取骨骼而是获取slot。因为spine runtime中一个骨骼可以带有多个slot,并且除了TSR动画(位移缩放旋转),我们还需要挂接上去的Node能支持Color动画(包含alpha),
那么slot就可以满足条件,并且从slot很容易获取相应的bone。也因为如此,使用CCNodeRGBA而不是CCNode。
我们使用m_slotNodes来保存slot name到slot和node的映射。
下面是CCSkeleton.cpp中的实现:
cocos2d::CCNodeRGBA* CCSkeleton::getNodeForSlot(const char* slotName){ SlotNodeIter iter = m_slotNodes.find(slotName); if (iter!=m_slotNodes.end()) { sSlotNode& slot_node = iter->second; return slot_node.node; } else{ spSlot* slot = findSlot(slotName); if(slot!=NULL){ CCNodeRGBA* node = new CCNodeRGBA(); if(node->init()){ node->autorelease(); } node->setPosition(0, 0); this->addChild(node); sSlotNode slot_node; slot_node.slot = slot; slot_node.node = node; m_slotNodes.insert(SlotNodeMap::value_type(slotName,slot_node)); return node; } else{ return NULL; } } }
getNodeForSlot是被客户代码调用的,使用场景是创建CCSkeletonAnimation后,需要将某个CCNode挂接到某个slot上一起动画。此时调用getNodeForSlot获取该slot对应的CCNodeRGBA。
并且使用获取到的CCNodeRGBA作为父node来执行addChild(需要挂接的node)。
getNodeForSlot的实现很简单:如果该slot name对应的CCNodeRGBA不存在,则创建一个并且放入map中。
之后就剩下最核心的工作了,根据bone和slot来更新CCNodeRGBA。按理说,根据骨骼更新transform的代码应该放到spSkeleton_updateWorldTransform中,但是我不想多修改一个文件,所以我直接放到了CCSkeleton::draw ()中,虽然不那么完美,但是直接有效。另外如果放到CCSkeleton::update中其实是会慢一帧的,所以也不能用。
void CCSkeleton::draw () { //原始代码 //我们添加的代码放到最后好了,放到if (debugSlots) 这行之前即可。 //for each attached CCNodeRGBA, update the TSR and RGBA for (SlotNodeIter iter=m_slotNodes.begin(), end=m_slotNodes.end(); iter!=end; ++iter) { sSlotNode& slot_node = iter->second; spSlot* slot = slot_node.slot; CCNodeRGBA* node = slot_node.node; spBone* bone = slot->bone; if(bone!=NULL){ node->setPosition(ccp(bone->worldX, bone->worldY)); node->setRotation(-bone->worldRotation); node->setScaleX(bone->worldScaleX); node->setScaleY(bone->worldScaleY); } node->setOpacity(255*slot->a); node->setColor(ccc3(255*slot->r, 255*slot->g, 255*slot->b)); } }
这段代码遍历所有我们添加的node,并且找出他所绑定的slot和slot所属的bone。使用bone的x,y,rotation,scale来设置node的相应属性,使用slot的rgba来设置node的opacity和color。
这里的rotation需要反转一下,因为spine runtime的旋转正方向和cocos2d-x不一样。
几乎全部搞定了,但是此时如果将一个CCSprite挂接到getNodeForSlot获取的node上去动画,发现颜色和alpha并不会改变,这是由于我们还没设置CascadeColor和CascadeOpacity,
下面这个方法是从cocos2d-x testcpp工程中拿来的,可以将一个CCNode和其子node递归设置级联透明度和颜色,前提是该node实现了CCRGBAProtocol。因为我们创建的是CCNodeRGBA所以肯定是支持的了,而CCSprite也是支持的,所以再将sprite挂接到rgba node后,执行setEnableRecursiveCascadingRGBA(rgba_node,true)再播放动画,sprite的颜色和alpha就会跟着动画变化了。
static void setEnableRecursiveCascadingRGBA(CCNode* node, bool enable) { CCRGBAProtocol* rgba = dynamic_cast<CCRGBAProtocol*>(node); if (rgba) { rgba->setCascadeColorEnabled(enable); rgba->setCascadeOpacityEnabled(enable); } CCObject* obj; CCArray* children = node->getChildren(); CCARRAY_FOREACH(children, obj) { CCNode* child = (CCNode*)obj; setEnableRecursiveCascadingRGBA(child, enable); } }
基本的内容都说完了,那么这个功能的作用是啥呢?可以发挥各种想象力,在这里我要喷一下spine的skin功能,这个功能虽然想法很好,但是局限性太大,必须在编辑器里面将skin创建好并绑定。
如果skin很少还好说,如果有非常多的skin,并且以后游戏内容还是动态更新下载的,就很不方便了,这种大批量的情况下,编辑器里面的操作远远没有配置表方便实用。
当然spine是一个很好的编辑器,我非常喜欢,只是由于作者精力有限并且也不可能了解到所有其他人的需求,所以关键时候我们还得自己动手。
相关文章推荐
- 让 cocos2d-x 的 CCHttpRequest 支持https
- Cocos2D研究院之CCNode详解(三)
- CCLayerColor和CCClippingNode组合新手引导功能---cocos2d-x学习之路[4]
- Cocos2d-JS v3.0 alpha不支持cocos2d-x的Physics integration
- Cocos2d-x CCNode 类的属性及与节点有关的函数
- (转)【iOS-cocos2d-X 游戏开发之十】自定义CCSprite/Layer/CCNode及静态类模版&自定义类细节说明&Cocos2dx触屏事件讲解
- cocos2d-x 2.0 小心CCNode::setVisible,一不小心就看不见了
- Cocos2D研究院之CCNode详解(三)
- 使用Spine软件制作骨骼动画供Cocos2d-x使用
- 详解CCProgressTimer 进度条并修改cocos2d源码实现“理想”游戏进度条!
- 让FLAnimatedImage支持SDWebImage的修改 -- Flipboard开源高性能动画GIF引擎(源码)
- 基于cocos2d-x的spine动画残影拖尾效果
- 【iOS-Cocos2d游戏开发之十五】详解CCProgressTimer 进度条并修改cocos2d源码实现“理想”游戏进度条!
- 在froyo源码上修改来电支持动画
- Spine动画在Cocos2d-x中,多动画切换播放出现残影的解决方法
- cocos2d学习笔记(六)CCParallaxNode和Tile Map
- Cocos2d-x CCNode 类中常用函数
- quick-Cocos2d/framework/display.lua(这个组件提供的功能是很重要的,包括场景、层、精灵、动画这些游戏中必要的组件)
- cocos2d-x默认字体修改之CCLabelTTF, 修改为微软雅黑
- Android 组合动画包括四种属性动画和一种帧动画,自由组合,动态控制,只需在服务器修改json。