dragonBones5.6.300解析关键帧的actions异常的bug

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: dragonBones5.6.300解析关键帧的actions异常的bug

db5.6和db5.7的差异

我看到creator使用的龙骨是5.6(dragonebones/lib/dragonbones.js),我build了dragonBones官方的源码,发现creator 2.4.10使用的是自己修改过的,主要是删除了yDown的逻辑

image.png

image.png


修改的内容还是比较多的,但是去cocos仓库找到了对应的龙骨仓库,发现编译出来的还是和2.4.10内置的不一样,我只看到了修改过package.json,后来问了才知道是直接对build的结果上修改的。

没办法,只能参考ts源码,硬啃dragonBones.js的代码了

最小可复现demo

当第10帧增加一个关键帧,问题就消失了

image.png

所有的关键帧都是在切换动画

image.png

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;
};

image.png

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);
    }
};

image.png

从这个actionFrames里面我们可以推理出

  • 第00帧,播放[0-pan, 2-yu14]
  • 第10帧,播放3 yu15
  • 第20帧,播放1 pan

脑补一下效果也没啥问题,接着看解析

image.png

应该是这个this._actionFrames的问题,因为它是先把20送进去的导致了无法解析到10

我尝试着拍了下序列

javascript
复制代码
this._actionFrames.sort((a,b)=>{return a.frameStart-b.frameStart;});

这样解析到了,也正常了,牛逼~

修复方式

可以根据rawFrames的类型修复这个问题,但是这么干感觉风险比较大

另外的修复方式是在某个api之后,对this._actionFrames进行排序

image.png

可以看到解析原始json和frame都会经过_parseTimeline函数,他们都有一个公共的源头_parseAnimation,在解析完毕slot之后,再去解析frame的

image.png所以我们可以对parseSlotTimelinehack,让其调用完毕后,对_actionFrames排序,观察下_parseSlotTimeline的函数原型,他是ObjectDataParser的成员函数

js
复制代码
ObjectDataParser.prototype._parseSlotTimeline = function (rawData) {
};

全局变量持有了这个对象dragonBones.ObjectDataParser,问题就变的很简单了

相关修复已经提交个cocos

最新的dragonBones似乎不是通过这种方式修复的



目录
相关文章
|
6月前
|
存储 Java 程序员
Java之异常的详细解析
1. 异常 1.1 异常概念 异常,就是不正常的意思。在生活中:医生说,你的身体某个部位有异常,该部位和正常相比有点不同,该部位的功能将受影响.在程序中的意思就是:
41 0
|
1月前
|
网络安全 Docker 容器
【Bug修复】秒杀服务器异常,轻松恢复网站访问--从防火墙到Docker服务的全面解析
【Bug修复】秒杀服务器异常,轻松恢复网站访问--从防火墙到Docker服务的全面解析
25 0
|
3月前
|
网络协议 Java 数据库连接
13 Java异常(异常过程解析、throw、throws、try-catch关键字)
13 Java异常(异常过程解析、throw、throws、try-catch关键字)
76 2
|
5月前
|
Java 网络安全
解析connectionReset异常的原因与解决方案
解析connectionReset异常的原因与解决方案
|
6月前
|
存储 编解码 API
【解码与渲染 异常情况】深入解析视频中绿色竖线现象(一)
【解码与渲染 异常情况】深入解析视频中绿色竖线现象
211 6
【解码与渲染 异常情况】深入解析视频中绿色竖线现象(一)
|
Arthas NoSQL Java
JDK11现存性能bug(JDK-8221393)深度解析(1)
作为一名工程师,面对上面的现象,你会怎么做? 我想你的第一反应肯定是业务代码有问题?是不是有什么地方导致内存泄露? 是不是业务代码里有什么地方加载的数据太多,越来越慢?…… 同事尝试过dump堆里的内容,dump jstak线程…… 都没看出来什么异常,也优化了业务代码里之前一些不合理的逻辑,始终没有解决问题。 当时的问题是他们都没有往热点代码的方向排查,主要是因为他们不知道有啥好用的工具。
135 0
|
6月前
|
存储 编解码 算法
【解码与渲染 异常情况】深入解析视频中绿色竖线现象(二)
【解码与渲染 异常情况】深入解析视频中绿色竖线现象
151 1
|
6月前
|
人工智能 Go 开发者
Golang语言异常机制解析:错误策略与优雅处理
Golang语言异常机制解析:错误策略与优雅处理
|
安全 Java 测试技术
避免低级错误:深入解析Java的ConcurrentModificationException异常
在软件开发中,我们常常会遇到各种错误和异常。其中有一类比较低级但又常见的错误就是`ConcurrentModificationException`异常。最近了我就写了个这种异常,这个异常通常发生在使用迭代器遍历集合时,同时对集合进行修改,从而导致迭代器检测到集合结构发生变化而抛出异常。在测试环境中可能因为数据量较小或者测试场景不充分未能显现问题,但一旦部署到生产环境,场景增多,并发操作增多,这个低级错误就会爆发。
327 0
避免低级错误:深入解析Java的ConcurrentModificationException异常
|
6月前
|
SQL JSON Apache
Flink问题之嵌套 json 中string 数组的解析异常如何解决
Apache Flink是由Apache软件基金会开发的开源流处理框架,其核心是用Java和Scala编写的分布式流数据流引擎。本合集提供有关Apache Flink相关技术、使用技巧和最佳实践的资源。
301 1

推荐镜像

更多