spine cocos2d-x runtime修改:支持挂接CCNode到slot,并跟随动画(包括TSR, color, alpha)

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



评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值