在 quick-cocos2d-x 中使用 DragonBonesCPP(全文完)

原文地址:在 quick-cocos2d-x 中使用 DragonBonesCPP

Using DragonBonesCPP in quick-cocos2d-x.
[size=6]1 前言[/size]
DragonBones 官方C++版本 for cocos2d-x 这篇文章中,我已经简单地介绍过了 DragonBonesCPP 这套用于取代 CCArmature 的库。
在我自己修改的 quick-cocos2d-x 版本中,我已经把 CCArmature 库删除,完全使用 DragonBonesCPP 。
我们的产品也完全使用 Flash 和 DragonBonesCPP 来制作骨骼动画。所有AS3版本的DragonBones提供的功能,在CPP版本中都能完整地实现。
几个月之前,我就已经将 DragonBonesCPP 整合到了quick-cocos2d-x 中,只是一直没有向官方库推送PR。这也是因为有几个内存泄露的BUG还没有解决。
现在,我终于有时间可以来做这件事。我的计划如下:
[list=1][li]写一篇关于 DragonBonesCPP 的教程(quick专用哦);[/li][li]解决 DragonBonesCPP 的遗留问题,并进一步封装lua api方便使用;[/li][li]将 DragonBonesCPP 推送到quick的官方仓库。[/li][/list]那么,这篇文章就是第一件事了。
[size=6]2 Samples and Codes[/size]

本文用到的所有 范例文件 在这里提供。性急的同学可以直接下载代码研究。
DragonBonesCPP 在quick中的内容,包含在这样几个路径下:
[list][li]samples/dragonbones 范例项目[/li][li]extensions/DragonBones C++库支持[/li][li]luabinding/extensions/DragonBones lua绑定[/li][li]CCDragonBonesExtend framework Extend支持[/li][li]display.newDragonBones framework display支持[/li][/list]在下一步之前,还需要一些必要的资源。
[list][li]DragonBones samples 这是DragonBones提供的一些FLA文件源文件,你可以自行修改他们;[/li][li]DragonBones Design Panel 这是一个Flash插件,支持Flash CS5~Flash CC 。你需要首先安装它。本文使用的是 2.4.1 版;[/li][/list]这些资源可以在这里下载:http://dragonbones.github.io/download.html
本文讲解的3个范例都在这里:samples/dragonbones ,请提前下载。
这3个范例使用的都是Dragon这个动画,就是那只可爱的绿色小龙啦!
在 DragonBones Design Panel 中,可以输出多种文件格式。但是在 DragonBonesCPP 中,目前仅能支持 PNG+XML 这种格式。
如果选择 PNG+XML 格式输出,每个骨骼动画会包含三个文件:
[list][li]skeleton.xml 骨骼数据以及动画数据[/li][li]texture.png 纹理素材,采用碎图拼接而成[/li][li]texture.xml 碎图拼接的数据文件,和TexturePacker生成的plist的作用相同[/li][/list]

[size=6]3 显示Dragon[/size]

打开 samples/dragonbones/scripts/demos/DragonDemoEntry.lua 这个文件,让我们看看如何显示一个骨骼动画。
这个范例的效果是这样的:

[attachment=73637]

function DragonDemoEntry:_createDB()
    self._db = dragonbones.new({
            skeleton="dragon/skeleton.xml",
            texture="dragon/texture.xml",
            skeletonName="Dragon",
            armatureName="Dragon",
            aniName="",
        })
        :addTo(self, 10)
        :pos(display.cx,100)
        :addMovementScriptListener(handler(self, self._onMovement))
    local aniList = self._db:getAnimationList()
    for i=0,aniList:count()-1 do
        _ANIMATION_LIST[#_ANIMATION_LIST+1] = aniList:objectAtIndex(i):getCString()
    end
end

上面的 _createDB() 方法做了两件事,一是显示一个骨骼动画,二是将这个骨骼动画中的所有动作名称提取出来,加入到 _ANIMATION_LIST 列表中。
display.newDragonBones 方法封装在 display 库中。它需要1个table格式的参数:
[list][li]skeleton 骨骼数据的XML文件路径;[/li][li]texture 素材数据的XML文件地址;[/li][li]dragonBonesName skeleton.xml的根元素的name值,查看skeleton.xml即可看到,一般与FLA文件的文件名相同;[/li][li]armatureName 骨骼名称。一个dragonbones文件可以包含多个骨骼,这里一般指定主骨骼;[/li][li]aniName 播放这个名称指定的动画,空字符串代表不播放动画。[/li][/list]返回的对象是 CCDragonBones 的实例,它继承自 CCNode ,我在framework中使用了 CCDragonBonesExtend 来增加它的功能。在这个例子中,它被保存到 self._db 对象。
貌似quick的framework关于扩展功能这部分做了修改和整合,那么在我将DragonBonesCPP提交到quick的时候,这个类的名称可能会改变。
getAnimationList() 返回的是一个 CCArray 对象,其中每个项是 CCString 对象。for循环将其中的字符串提出并假如到 lua table 中。
看看 addMovementScriptListener 的定义:

function CCDragonBonesExtend:addMovementScriptListener(listener)     self:removeMovementScriptListener()     self:addScriptListener(CCDragonBonesExtend.EVENTS.START, listener)     self:addScriptListener(CCDragonBonesExtend.EVENTS.COMPLETE, listener)     self:addScriptListener(CCDragonBonesExtend.EVENTS.LOOP_COMPLETE, listener)     return self end

这个方法其实是注册了三个事件,这三个事件分别在动画开始播放、播放完成和循环播放完成的时候调用。
让我们看看在点击 Change Animation 菜单的时候会发生什么:

[code]function DragonDemoEntry:_onChangeAnimation()
    _aniIndex = _aniIndex + 1
    if _aniIndex > #_ANIMATION_LIST then
        _aniIndex = _aniIndex - #_ANIMATION_LIST
    end

    self._db:gotoAndPlay(_ANIMATION_LIST[_aniIndex])
end[/code]

Dragon这个小龙的动作一共有4个:walk,stand,jump,fall。上面的代码做的事情就是将它们循环播放。
要停止或者播放动画,可以使用下面的代码:


self._db:getAnimation():stop()
self._db:getAnimation():play()

4 换装

打开 https://github.com/zrong/quick-cocos2d-x/blob/zrong/samples/dragonbones/scripts/demos/DragonSwitchClothes.lua 这个文件,让我们看看如何实现一个简单的换装。
这个范例的效果是这样的:

在文件开头,我定义了一个4个元素的table,用来保存每个不同服装的纹理:

local _TEXTURES = {
    "parts/clothes1",
    "parts/clothes2",
    "parts/clothes3",
    "parts/clothes4",
}

这些纹理的名称,是根据FLA文件的库结构自动生成的,我们可以打开texture.xml这个文件,就能看到这个DragonBones的所有纹理。
在 _onSwitchClothes 事件处理函数中,不断循环调用不同的纹理实现换装。

function DragonSwitchClothes:_onSwitchClothes()
    _textureIndex = _textureIndex + 1
    if _textureIndex > #_TEXTURES then
        _textureIndex = _textureIndex - #_TEXTURES
    end

    self._db:setBoneTexture("clothes", _TEXTURES, "Dragon");
end

setBoneTexture 是为了方便换装而封装在 CCDragonBones 中的一个函数。它的内容如下:



void CCDragonBones::setBoneTexture(const char* boneName, const char* textureName, const char* textureAtlasName)
{

    Cocos2dxFactory* fac = Cocos2dxFactory::getInstance();
    Object* clothesObj = fac->getTextureDisplay(textureName, textureAtlasName);

    //CCLOG("CLOSE %d", clothesObj);

    Bone* bone = getArmature()->getBone(boneName);
    CocosNode* oldClothesObj = static_cast<CocosNode*>(bone->getDisplay());
    bone->setDisplay(clothesObj);
}

从上面我们可以看出,换装的方法就是根据提供的纹理新建一个对象,然后找到要切换纹理的骨骼,将这个骨骼的显示部分替换成这个纹理对象。

在DragonBones中,有很多方式可以实现换装。因为一个骨骼不一定是一个单独的纹理,还可能是一个逐帧动画对象。这里描述的换装方法,只能把骨骼替换成单个纹理。
以后我会专门撰文描述其它的换装方式。

这个例子其实并不只是说明了换装一种用法,还有如何让动画移动等等。请参考相关代码自行理解。

[size=6]5 追鸟[/size]

打开 samples/dragonbones/scripts/demos/DragonChaseStarling.lua 这个文件,让我们看看Dragon如何孽待一只叫做Starling的小八哥。

这个例子中的小鸟来自基于Flash Stage3D技术的2D引擎 Starling ,这个引擎也算是cocos2d-x的竞争对手吧

这个范例的效果是这样的:

一个月黑风高的晚上,许多乌鸦在背景的天空中飞。一直红色的小鸟正在挑逗一只胸饿的小龙(请自行脑补……),小龙带着呆萌的眼神挥舞那凶猛的爪子准备抓住小鸟然后OOXX……从此它们过上了幸福的生活……

哦,请原谅我走神。看起来这动人的一幕是这样的:

[attachment=73855]

在这个例子中,因为要控制小龙的头部、两只爪子根据小鸟的位置而移动,我们需要实现得到这三个部位的骨骼引用:

_head = self._db:getArmature():getBone(&quot;head&quot;) _armR = self._db:getArmature():getBone(&quot;armUpperR&quot;) _armL = self._db:getArmature():getBone(&quot;armUpperL&quot;)

小鸟就是一个CCSprite而已,小龙和上面的创建一样,这里就不贴出代码了:


self._starlingBird = display.newSprite(&quot;starling.png&quot;)
&#160;&#160;&#160; :pos(display.left + 20, display.cy)
&#160;&#160;&#160; :addTo(self, 10)

当触摸屏幕的时候,需要开启一个计时器来更新。后面所有的更新都在计时器 self._update 中进行。

在触摸屏幕并移动的时候,实时更新小鸟的坐标,并记录当前触摸的坐标以供在 self._update 的时候计算骨骼的旋转角度。

[code]function DragonChaseStarling:_onTouch(event,x, y, px, py)
    if event == "began" then
        if not _isChasing then
            self:scheduleUpdate(handler(self, self._update));
            _isChasing = true
        end
        return true
    end
    if event == "moved" then
        self:_updatePosition(x, y)
    elseif event == "ended" then
    end
end

function DragonChaseStarling:_updatePosition(x, y)
    _touchX = x
    _touchY = y
    self._starlingBird:pos(x,y);
end[/code]

更新骨骼角度的代码在 self._updateBones 方法中。

下面的代码计算当前触摸点与小龙的中心点的角度,然后分别设置三个骨骼(头部、左爪,右爪)以及小鸟的旋转。
需要注意几点:
[list=1][li]DragonBones的中心点在脚底中心,因此身体中心要加上高度的一半;[/li][li]每个骨骼的起始角度以及可偏转极限不同,因此使用不同的参数与 _r 相乘;[/li][li]CCDragonBones只是一个CCNode,默认是没有ContentSize的,因此必须先使用 setContentSize 设置尺寸。[/li][/list]

function DragonChaseStarling:_updateBones() &#160;&#160;&#160; local dbsize = self._db:getContentSize() &#160;&#160;&#160; _r = math.pi + math.atan2(self._db:getPositionY() + &#160;&#160;&#160;&#160;&#160;&#160;&#160; dbsize.height / 2-_touchY , &#160;&#160;&#160;&#160;&#160;&#160;&#160; _touchX - self._db:getPositionX()) &#160;&#160;&#160; if _r &gt; math.pi then &#160;&#160;&#160;&#160;&#160;&#160;&#160; _r = _r - math.pi * 2; &#160;&#160;&#160; end &#160;&#160;&#160; &#160;&#160;&#160; _head.offset:setRotation(_r*0.3); &#160;&#160;&#160; _armR.offset:setRotation(_r*0.8); &#160;&#160;&#160; _armL.offset:setRotation(_r*1.5); &#160;&#160;&#160; self._starlingBird:setRotation(_r*0.2*(180/math.pi)); end

其它的关于小龙的移动和超范围判断,看源码即可。

6 更多

这三个例子只是展示了DragonBones的很小一部分功能。更多的例子和使用方法,请访问 http://dragonbones.github.io/ 下载。

当然,官方网站的例子都是基于 ActionScript3 的。

mark mark一下

好贴必须顶呀 酷:7::7::7:

赞一个,楼主辛苦
之前见过一个人写过一个中间件 可以直接读取swf文件,直接播放需要的动画

支持一下:2:

顶起来 顶起来 顶起来

放弃dragonbones了,因为我们的动作幅度较大,以至于肢体补间的过程中出现“肢解”现象。
我们希望的效果是能有部分帧不自动补间,但是没法设置
忍痛放弃了

顶楼主,能说说spine的数据如何在这里面用吗?:6:

貌似我们没有碰到过这个问题。

是CPP版本的问题么?你在AS版本中测试过了么?也有同样的问题么?

如果确认问题存在,你可以在DragonBones github上发issue,会有人解决的。

mark :2:

我居然更新完了。

论坛编辑器真难用。艹

:14::7:

收藏下,:2::2::2::2::2::2::2:

除了支持骨骼动画这种反向动力学框架, 如果也能支持用flash导出正向补间动力学动画的话,就完美了。
毕竟反向动力学计算量还是挺大的,而且又不是所有的动画都需要骨骼动画。

好文必须要顶!赞zrong!