db5.6和db5.7的差异
我看到creator使用的龙骨是5.6(dragonebones/lib/dragonbones.js
),我build了dragonBones官方的源码,发现creator 2.4.10使用的是自己修改过的,主要是删除了yDown的逻辑
修改的内容还是比较多的,但是去cocos仓库找到了对应的龙骨仓库,发现编译出来的还是和2.4.10内置的不一样,我只看到了修改过package.json,后来问了才知道是直接对build的结果上修改的。
没办法,只能参考ts源码,硬啃dragonBones.js的代码了
最小可复现demo
当第10帧增加一个关键帧,问题就消失了
所有的关键帧都是在切换动画
js 复制代码 Armature.prototype.advanceTime = function (passedTime) { // Do actions.要观察_actions的来源 if (this._actions.length > 0) { this._lockUpdate = true; for (var _i = 0, _a = this._actions; _i < _a.length; _i++) { var action = _a[_i]; var actionData = action.actionData; if (actionData !== null) { if (actionData.type === 0 /* Play */) { if (action.slot !== null) { var childArmature = action.slot.childArmature; if (childArmature !== null) { childArmature.animation.fadeIn(actionData.name); } } else if (action.bone !== null) { for (var _b = 0, _c = this.getSlots(); _b < _c.length; _b++) { var slot = _c[_b]; if (slot.parent === action.bone) { var childArmature = slot.childArmature; if (childArmature !== null) { childArmature.animation.fadeIn(actionData.name); } } } } else { this._animation.fadeIn(actionData.name); } } } action.returnToPool(); } this._actions.length = 0; this._lockUpdate = false; } this._proxy.dbUpdate(); } Armature.prototype._bufferAction = function (action, append) { if (this._actions.indexOf(action) < 0) { if (append) { this._actions.push(action); } else { this._actions.unshift(action); } } };
js 复制代码 // 获取rawData[key]的值,如果不存在则返回defaultValue ObjectDataParser._getNumber = function (rawData, key, defaultValue) { if (key in rawData) { var value = rawData[key]; if (value === null || value === "NaN") { return defaultValue; } return +value || 0; } return defaultValue; };
dragonBones的数据结构是非常紧凑的
• frameArray js 复制代码 // 对displayFrame的解析,2个一组,格式为 [frame_start, display-index] 0,0, 20,0, // slot-pan 0,0, 10,0, // slot-yupian //-------------------分割线----------------------- 0,2, // [frame_start, action_count],因为有2个action,所以后边数2个 0,2, // 对应this._armature._actions里面的偏移,数据来自_mergeActionFrame的结果 20,1, // 与上同理 1, 0,-1,0,-1,0
- timelineArray
js 复制代码 100, // scale, 0, // offset 2, // displayFrameCount 0, // frameValueCount永远是0 0, // animation.frameIntOffset、animation.frameFloatOffset、0 // 以上5个数值是配置数据,后边的数据长度和displayFrameCount有关系,这里是2 0, // frame1的frameArray偏移,对应的事slot-pan 2, // frame2的frameArray偏移 // 新的数据 100,0, 2 ,0,0,// 5个配置数据 4,6, // 对应的事slot-yupian //-------------------分割线----------------------- // 新数据 100,0, 3 ,0,0, 8,12,0, // 新数据 100,0, 1 ,0,0, 0, // 新数据 100,0, 1 ,0,0, 0
js 复制代码 ObjectDataParser.prototype._mergeActionFrame = function (rawData, frameStart, type, bone, slot) { var actionOffset = dragonBones.DragonBones.webAssembly ? this._armature.actions.size() : this._armature.actions.length; // 将当前displayFrame里面的actions序列话出来 var actions = this._parseActionData(rawData, type, bone, slot); var frameIndex = 0; var frame = null; for (var _i = 0, actions_2 = actions; _i < actions_2.length; _i++) { var action = actions_2[_i]; // 将每个action都放到armature里面 this._armature.addAction(action, false); } if (this._actionFrames.length === 0) { // First frame. frame = new ActionFrame(); frame.frameStart = 0; this._actionFrames.push(frame); frame = null; } // 找到起始帧相同的frame for (var _a = 0, _b = this._actionFrames; _a < _b.length; _a++) { // Get same frame. var eachFrame = _b[_a]; if (eachFrame.frameStart === frameStart) { frame = eachFrame; break; } else if (eachFrame.frameStart > frameStart) { break; } frameIndex++; } if (frame === null) { // Create and cache frame. frame = new ActionFrame(); frame.frameStart = frameStart; this._actionFrames.splice(frameIndex + 1, 0, frame); } // 将这个action放在对应frameStart的_actionsFrame里面 // 这样在播放到某一帧的时候,我们就知道该帧都要触发哪些actions // 这里只是索引, 从this._armature.actions里面获取 for (var i = 0; i < actions.length; ++i) { // Cache action offsets. frame.actions.push(actionOffset + i); } };
从这个actionFrames里面我们可以推理出
- 第00帧,播放[
0-pan
,2-yu14
] - 第10帧,播放
3 yu15
- 第20帧,播放
1 pan
脑补一下效果也没啥问题,接着看解析
应该是这个this._actionFrames
的问题,因为它是先把20送进去的导致了无法解析到10
我尝试着拍了下序列
javascript 复制代码 this._actionFrames.sort((a,b)=>{return a.frameStart-b.frameStart;});
这样解析到了,也正常了,牛逼~
修复方式
可以根据rawFrames的类型修复这个问题,但是这么干感觉风险比较大
另外的修复方式是在某个api之后,对this._actionFrames
进行排序
可以看到解析原始json和frame都会经过_parseTimeline
函数,他们都有一个公共的源头_parseAnimation
,在解析完毕slot之后,再去解析frame的
所以我们可以对parseSlotTimeline
hack,让其调用完毕后,对_actionFrames排序,观察下_parseSlotTimeline
的函数原型,他是ObjectDataParser的成员函数
js 复制代码 ObjectDataParser.prototype._parseSlotTimeline = function (rawData) { };
全局变量持有了这个对象dragonBones.ObjectDataParser
,问题就变的很简单了
相关修复已经提交个cocos
最新的dragonBones似乎不是通过这种方式修复的