重视点:

  • 定制引擎
  • 龙骨烘托流程
  • 合批问题
  • 龙骨格局
  • 怎么提交烘托

unity

github.com/DragonBones…

cocos creator DragonBones 源码阅览

运转替换插槽的测试例

cocos creator DragonBones 源码阅览

能够看到切换插槽是能够参加烘托排序的,unity在这块确实做的十分完善,包括9.ReplaceSkin也是经常用的功用。

应该是dragonBones官方对unity的支持十分到位,究竟这个库房是DragonBones官方保护的。

cocos

挂点

先查挂点数据是怎么同步的,挂点都是在子节点上

cocos creator DragonBones 源码阅览

查renderFlow意义不大

从Assembler入手,主体逻辑

let ArmatureDisplay = cc.Class({
    name: 'dragonBones.ArmatureDisplay',
    extends: RenderComponent,
})
  • extensionsdragonboneswebgl-assembler.js 绑定Assembler
export default class ArmatureAssembler extends Assembler {
    updateRenderData (comp, batchData) {}
    fillBuffers (comp, renderer) {
        // 组装极点的中心逻辑
        // renderer._meshBuffer == cc.renderer._handle._meshBuffer 是等价的
       _buffer = renderer._meshBuffer;
        // Traverse all armature.
        renderer.worldMatDirty  ; // 这个worldMatDirty的效果参阅扩展
        // 这个函数会填充_buffer
        this.realTimeTraverse(armature, worldMat, 1.0);
        // sync attached node matrix, 同步挂点的逻辑,需求重视下attachUtil的逻辑
        // 首要逻辑便是更新了attachNode的矩阵,而且标记对应的flag
        // 这样当再次烘托到这些node的时分,render_flow会自动更新相关的特点
        comp.attachUtil._syncAttachedNode();
    }
}
Assembler.register(Armature, ArmatureAssembler);
    _syncAttachedNode () {
        let bone = isCached 
            ? boneInfos[boneNode._boneIndex] 
            : boneNode._bone; // 多了一个_bone特点,在组件初始化的时分会赋值好
    }

dragonBones_ArmatureDisplay._armature 来历

组件初始化流程,在脚本初始化的时分,就会调用_buildArmature函数,是初始化逻辑的一部分

cocos creator DragonBones 源码阅览

ctor () {
    this.attachUtil = new AttachUtil();
    this._factory = dragonBones.CCFactory.getInstance(); // 工厂单例
},
_buildArmature(){
    // 解析ske.json,armatureKey选用的是 ske_json_uuid # png_uuid
    // 调用dblib.parseDragonBonesData(ske_json_data, name) 将原始数据解析为 DragonBonesData 实例,并缓存到工厂中。
    // 牵扯到dblib的就暂时不关心了,dblib提供了相似回调的机制,相关的适配逻辑代码在CCFactory.js
    this._armatureKey = this.dragonAsset.init(this._factory, atlasUUID);
    if (this.isAnimationCached()) {
        this._armature = this._armatureCache.getArmatureCache(this.armatureName, this._armatureKey, atlasUUID);
    } 
    if (CC_EDITOR || this._cacheMode === AnimationCacheMode.REALTIME) {
        // 落在这个逻辑里边,需求看下_factory的来历
        this._displayProxy = this._factory.buildArmatureDisplay(this.armatureName, this._armatureKey, "", atlasUUID);
        // 来历就在这儿,需求往上看
        this._armature = this._displayProxy._armature; 
    }
    // 将挂点和bones关联起来,其中node._bone便是在这儿边赋值的
    this.attachUtil._associateAttachedNode();
}

最中心的逻辑: realTimeTraverse填充烘托数据

既然我们知道终究数据是填充到_buffer里边了,直接倒推逻辑即可

export default class ArmatureAssembler extends Assembler {
    realTimeTraverse (armature, parentMat, parentOpacity){
     // armature 来自 dragonBones_ArmatureDisplay._armature,它是db的数据类型
      let slots = armature._slots;
        let vbuf, ibuf, uintbuf;
        let material;
        let vertices, indices;
        let slotColor;
        let slot;
        let slotMat;
        let slotMatm;
        let offsetInfo;
        for (let i = 0, l = slots.length; i < l; i  ) {
            slot = slots[i]; // 需求看下这个来历
            slotColor = slot._color;
            if (!slot._visible || !slot._displayData) continue;
            if (parentMat) {
                slot._mulMat(slot._worldMatrix, parentMat, slot._matrix);
            } else {
                Mat4.copy(slot._worldMatrix, slot._matrix);
            }
            if (slot.childArmature) {
                this.realTimeTraverse(slot.childArmature, slot._worldMatrix, parentOpacity * slotColor.a / 255);
                continue;
            }
            material = _getSlotMaterial(slot.getTexture(), slot._blendMode);
            if (!material) {
                continue;
            }
            if (_mustFlush || material.getHash() !== _renderer.material.getHash()) {
                _mustFlush = false;
                _renderer._flush();
                _renderer.node = _node;
                _renderer.material = material;
            }
            _handleColor(slotColor, parentOpacity);
            slotMat = slot._worldMatrix;
            slotMatm = slotMat.m;
            vertices = slot._localVertices; // 极点数据来历
            _vertexCount = vertices.length >> 2;
            indices = slot._indices; // 极点索引数据来历
            _indexCount = indices.length;
            offsetInfo = _buffer.request(_vertexCount, _indexCount); // 请求缓冲区
            _indexOffset = offsetInfo.indiceOffset;  // 请求的缓冲区偏移
            _vfOffset = offsetInfo.byteOffset >> 2;
            _vertexOffset = offsetInfo.vertexOffset; // 请求的缓冲区偏移
            vbuf = _buffer._vData;
            ibuf = _buffer._iData;
            uintbuf = _buffer._uintVData;
            _m00 = slotMatm[0];
            _m04 = slotMatm[4];
            _m12 = slotMatm[12];
            _m01 = slotMatm[1];
            _m05 = slotMatm[5];
            _m13 = slotMatm[13];
            // 填充极点缓冲区
            for (let vi = 0, vl = vertices.length; vi < vl;) {
                // 运转时动态核算bones的xy、uv
                _x = vertices[vi  ]; 
                _y = vertices[vi  ];
                vbuf[_vfOffset  ] = _x * _m00   _y * _m04   _m12; // x
                vbuf[_vfOffset  ] = _x * _m01   _y * _m05   _m13; // y
                vbuf[_vfOffset  ] = vertices[vi  ]; // u
                vbuf[_vfOffset  ] = vertices[vi  ]; // v
                uintbuf[_vfOffset  ] = _c; // color
            }
            // 填充极点索引缓冲区
            for (let ii = 0, il = indices.length; ii < il; ii   ) {
                ibuf[_indexOffset  ] = _vertexOffset   indices[ii];
            }
        }
    }
}

CCFactory

var BaseFactory = dragonBones.BaseFactory;
var CCFactory = dragonBones.CCFactory = cc.Class({
    // 承继自dragonBones.Factory,所以它有db_lib.buildArmature等函数
    extends: BaseFactory,
    ctor () {
        let eventManager = new dragonBones.CCArmatureDisplay();
        this._dragonBones = new dragonBones.DragonBones(eventManager); // 这个便是DragonBones官方的lib库
    },
    update (dt) {
        this._dragonBones.advanceTime(dt); // 让时刻往前走,驱动龙骨的更新
    },
    buildArmatureDisplay (armatureName, dragonBonesName, skinName, textureAtlasName) {
        // 经过缓存的 DragonBonesData 实例和 TextureAtlasData 实例创建一个骨架。
        // buildArmature也会回调到CCFactory的_buildArmature
        // buildArmature内部也会处理slot
        let armature = this.buildArmature(
            armatureName, 
            dragonBonesName, // 这个便是_armatureKey
            skinName, 
            textureAtlasName
        );
        // _display其实便是CCArmatureDisplay,相似显现署理,从命名上也能看出来
        return armature && armature._display; 
    },
    _buildSlot (dataPackage, slotData, displays) {
        let slot = BaseObject.borrowObject(dragonBones.CCSlot);
        let display = slot;
        slot.init(slotData, displays, display, display);
        return slot;
    },
    _buildArmature (dataPackage) {
        // db_lib的类型
        let armature = BaseObject.borrowObject(dragonBones.Armature);
        armature._skinData = dataPackage.skin;
        armature._animation = BaseObject.borrowObject(dragonBones.Animation);
        armature._animation._armature = armature;
        armature._animation.animations = dataPackage.armature.animations;
        armature._isChildArmature = false;
        // fixed dragonbones sort issue
        // armature._sortSlots = this._sortSlots;
        var display = new dragonBones.CCArmatureDisplay(); // display真正的来历
        // 会设置display,同时会设置display.dbInit,也便是CCArmatureDisplay.dbInit
        armature.init(dataPackage.armature,
            display, display, this._dragonBones
        );
        return armature;
    },
}    
dragonBones.CCArmatureDisplay = cc.Class({
    name: 'dragonBones.CCArmatureDisplay',
    // db api
    // armature: 龙骨的类型Armature
    dbInit (armature) {
        this._armature = armature;
    },
}

CCSlot

vertices = slot._localVertices; // 极点数据来历
indices = slot._indices; // 极点索引数据来历

都是初始化进行了填充,indices是固定的[0,1,2,1,3,2]

_updateFrame () {
    // 这儿边进行了初始化
}

换装

大致的联系如下图:

cocos creator DragonBones 源码阅览

var armatureDisplay = this.node.getComponent(dragonBones.ArmatureDisplay);
const factory = dragonBones.CCFactory.getInstance();
const armatureKey = armatureDisplay.getArmatureKey();
const slot = armatureDisplay.armature().getSlot("2");
const b = factory.replaceSlotDisplay(
  armatureKey, // 实例的缓存称号
  armatureDisplay.armatureName, // 骨架数据称号
  "1", // 插槽数据称号
  "1", // 显现目标数据称号
  slot // 要把这个插槽的内容替换为 1,1
);

replaceSlotDisplay的注释

 /**
 * - 用特定的显现目标数据替换特定插槽当前的显现目标数据。
 * 用 "dragonBonesName/armatureName/slotName/displayName" 指定显现目标数据。
 * @param dragonBonesName - DragonBonesData 实例的缓存称号。
 * @param armatureName - 骨架数据称号。
 * @param slotName - 插槽数据称号。
 * @param displayName - 显现目标数据称号。
 * @param slot - 插槽。
 * @param displayIndex - 被替换的显现目标数据的索引。 (如果未设置,则替换当前的显现目标数据)
 * @example
 * <pre>
 *     let slot = armature.getSlot("weapon");
 *     factory.replaceSlotDisplay("dragonBonesName", "armatureName", "slotName", "displayName", slot);
 * </pre>
 * @version DragonBones 4.5
 * @language zh_CN
 */
BaseFactory.prototype.replaceSlotDisplay = function (dragonBonesName, armatureName, slotName, displayName, slot, displayIndex) {

总结

DragonBones组件会一次性将龙骨悉数极点数据提交到buffer,所以导致无法穿插节点,由于cocos是依照节点顺序进行烘托的。

扩展

worldMatDirty

_proto._children = function (node) {
    let cullingMask = _cullingMask;
    let batcher = _batcher;
    let parentOpacity = batcher.parentOpacity;
    let opacity = (batcher.parentOpacity *= (node._opacity / 255));
    let worldTransformFlag = batcher.worldMatDirty ? WORLD_TRANSFORM : 0; // 这儿
    let worldOpacityFlag = batcher.parentOpacityDirty ? OPACITY_COLOR : 0;
    let worldDirtyFlag = worldTransformFlag | worldOpacityFlag;
    let children = node._children;
    for (let i = 0, l = children.length; i < l; i  ) {
        let c = children[i];
        // Advance the modification of the flag to avoid node attribute modification is invalid when opacity === 0.
        c._renderFlag |= worldDirtyFlag;
        if (!c._activeInHierarchy || c._opacity === 0) continue;
        _cullingMask = c._cullingMask = c.groupIndex === 0 ? cullingMask : 1 << c.groupIndex;
        // TODO: Maybe has better way to implement cascade opacity
        let colorVal = c._color._val;
        c._color._fastSetA(c._opacity * opacity);
        flows[c._renderFlag]._func(c);
        c._color._val = colorVal;
    }
    batcher.parentOpacity = parentOpacity;
    this._next._func(node);
};

_armatureKey

cocos creator DragonBones 源码阅览