spine骨骼中添加节点

http://www.58player.com/blog-2533-92424.html        

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中添加: 
         
        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是一个很好的编辑器,我非常喜欢,只是由于作者精力有限并且也不可能了解到所有其他人的需求,所以关键时候我们还得自己动手。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值