美文网首页
2018-09-25

2018-09-25

作者: 前端林志文 | 来源:发表于2018-09-26 14:57 被阅读0次

    BUG

    loading时偶尔卡住bug

    表现:无法继续,无报错,出现概率:2/10,所有平台都会重现,引入声音加载后更容易重现。

    • 调试目标:由于声音容易重现,所以从声音模块入手调试。

    • 重现步骤:发现将cpu速度下降,和加载大量声音,和开启大量加载线程,每次进游戏后几乎必现。必现很重要,不然无法确定修改后的bug是否正确。碰到偶然bug,第一时间是看代码逻辑,没法从逻辑中找到问题,只能创造必现的条件。

    • 步骤:

    1. 如何获取卡住的目标模块堆栈信息?卡住后程序实际还在运行,这时候打算加入断点,但目标模块已经不在运行,加入断点也无效,这时候加入的地方是一个系统Timer,当卡住后,在Timer的callback中打断点,游戏主程序会被断下来。

    2. 在Timer打断点,如何才能获取目标模块的内存信息?这时候可以预先在目标模块加入调试代码,例如window.__xxxModule = RES.instance; 将目标的的局部变量引用存到全局变量里面,这样断点的时候可以在watch窗口输入window.__xxxModule查看目标的内存信息。

    3. 查看window.__xxxModule模块后,发现没有声音模块的引用,无法获得声音模块的对象信息,重新修改引擎代码,将声音模块的引用加入到其父模块中,例如RES.instance.sound = sound; 注意新加的引用最好加入到其父模块,阅读大概的加载流程和父子模块的生命周期是否一致,子模块可以由父模块哪些变量管理。加完子引用后重新执行步骤1。

    结论:发现加入引用后bug无法重现啊。经分析是白鹭的资源加载模块,没管理子加载模块的引用,导致浏览器在gc的时候被回收,callback无法回调结果卡住。

    修复:声音模块引用有问题,需要确定其他加载模块是否也存在同样问题。经发现,图片加载模块也是有bug的,图片加载模块使用了缓存池,前4组线程一旦成功就会被缓存池引用,但万一刚加载就被gc掉了,也会导致卡住的问题。所以都要给父模块加入子模块的引用才行。

    关闭某UI时,偶尔报错

    表现:报错堆栈被中断,无法获取有效信息。在某些情况下,Timer或者Promise回调回来的堆栈不完整,无法获取所有堆栈内容。出现概率 2/50。

    • 调试目标: 该UI模块

    • 重现步骤:由于出现概率极低,首要任务是将重现概率提高。

    • 步骤:

    1. 由于问题出现在关闭UI的瞬间,只要提升关闭次数,就能提高重现概率,编写测试代码,
    while(1000) {
        UIMgr.open(xxx);
        UIMgr.close(xxx);
    }
    

    若以上代码不出错,考虑到偶现的都是定时器或者动画引起的,需加入延迟关闭的测试代码,

    while(1000) {
        UIMgr.open(xxx);
        Timer.After(1000, UIMgr.close(xxx));
    }
    

    结论:经发现,是有子模块的对象没被释放,对象内的定时器还在运行,回调的对象已经无效。

    修复:在onExit中加入对子模块的释放。

    某动画移动的位置总是不对

    表现:某一时刻出现很多飞金币飞资源动画,但只有飞金币动画位置不对,查看代码逻辑没发现问题。

    • 调试目标:仅仅针对飞金币动画。

    • 重现步骤:将飞金币动画和部分模块,迁到登录界面,运行时看是否重现。这里就突显函数封装的重要性,好的程序结构,调试bug也更方便。函数必须是要功能单一和完整的,要做到这样,就要求函数尽量只依赖参数,然后返回结果。少用this对象。尤其是UI动画,按道理来说应该比较独立,对其他UI和业务逻辑依赖比较少才对。

    游戏越来越卡

    表现:刚进游戏流程,游戏后期很变卡和发热严重。

    • 调试目标:猜测内存泄漏问题

    • 重现步骤:有可能是常用的UI模块或者某重复创建销毁的大对象释放有误导致的。

    • 步骤:

    1. 假设是最消耗内存的骨骼动画释放问题:在游戏登录界面,编写测试代码,增加两个button: add/sub, addBtn创建1000个骨骼动画, subBtn删除1000个骨骼动画,重复操作几次,若是界面骨骼动画数量为0时,内存和cpu都没法下降,必然是骨骼动画释放机制有错,导致内存泄漏。若是UI问题,操作方式也差不多。

    UI关闭时骨骼动画释放偶尔报错

    表现:偶尔会出现调用到无效骨骼对象的报错堆栈信息

    • 调试目标:白鹭的骨骼动画,创建是需要加入全局时钟 clock.add(arm); 移除时需要从全局时钟中移除 clock.remove(arm);堆栈信息是从remove中的报错,初步怀疑是没有remove已经释放的arm导致的。但看代码没看出问题所在(其实是有问题的)

    • 重现步骤:出现概率比较低,无法必现。但通过重复开关UI的方式统计错误信息。

    • 步骤:

    1. 给每一个arm对象增加唯一标志: this.name = Math.random();clock.add(arm)的前一句代码中加入 console.log("add:" + this.name); close.remove(arm)前一句代码中加入 console.log("remove:" + this.name); 通过输出log,观察每个对象创建是否都已经被释放。用眼睛看log比较累,其实可以通过写测试模块,封装好关键信息,定时收集,不匹配就输出断言,这样就不需要用眼睛对比log信息。

    2. 经发现,前两次的UI创建和释放log交错输出,统计发现总是差一次释放。

    结论:是由于关键的add,remove代码不同步导致的,主要原因是创建骨骼动画是异步回调的。例如:

    UIxxx {
        onEnter() {
            let cb = (arm)=> {
                clock.add(arm)
                this.arm = arm;
            }
            createArm(cb)
        }
    
        onExit() {
            if (this.arm) {
                clock.remove(this.arm);
                this.arm = null
            }
        }
    }
    
    

    修复:关键的add,remove尽量不要引入其他条件判断(谨慎)。进出的逻辑操作一定要尽量贴近对象的创建和释放代码。

    某IOS版本上,拖放对象时出现黑块

    表现:只在特定IOS版本上才能重现

    • 调试目标:采用debug模块,手机直接输入debug地址

    • 重现步骤:只要拖动就会显示黑块,必现。

    • 步骤:

    1. 初步怀疑是UI中的某些节点干扰,采用排除法,先将无关的节点隐藏。发现还是能重现。

    2. UI没找到问题,接着采用代码屏蔽法,先将拖动的代码注释掉,发现表现正确了。初步确定问题所在。

    3. 逐步注释代码,最后将hitTest(true);修改为hitTest();发现表现正确,而且操作逻辑无误。

    结论:使用了特定ios版本不支持的像素检查属性,预估是白鹭的bug.

    修复:修改为hitTest()就行了,正常逻辑本来就不需要采用像素检查。

    IPHONE SE 二维码无法生成

    表现:只在特定iphone se上才能重现

    • 调试目标:采用debug模块,手机直接输入debug地址

    • 重现步骤:必现。要生成二维码的游戏步骤过多,直接将生成代码贴到登录界面,省去loading时间。

    • 步骤:

    1. 查看系统Image加载dataURL的代码,经发现是加载dataURL时WebImage发生错误,但没有输出具体错误信息。

    2. WebImage代码不多,只有十几行,采用代码注释排除法。最后将Image.crossOrigin注释掉。发现二维码能生成了。

    结论:iphone_se不支持匿名跨域创建Image DataURL。

    修复:在Image loader里面,判断当前src是否dataURL,若是临时关闭跨域属性。

    IPHONE X 游戏模糊

    表现:经发现,不仅仅是IPHONE X,只要在特定的宽高比就会变得模糊

    • 调试目标:采用浏览器

    • 重现步骤:webgl模式下拖放浏览器,整个游戏会模糊,必现。但canvas没问题。

    • 步骤:

    1. 怀疑白鹭改出bug了

    2. 下载白鹭github上面的工程。

    3. 采用git log -S 命令刷选 模糊 关键字。查看修改的代码,尝试还原测试。

    结论:发现有一次代码提交,是修正字体模糊,针对canvas所加的。

    if (egret.Capabilities.renderMode == "canvas") {
        canvasScaleX = Math.ceil(canvasScaleX);
        canvasScaleY = Math.ceil(canvasScaleY);
    }
    

    修复:发现将针对canvas的条件后注释,模糊问题基本解决;

    总结

    1. 创建测试环境
    2. 创建必现条件
    3. 编写测试代码
    4. 注释代码(参考二分查找算法)
    5. 查找历史记录log

    相关文章

      网友评论

          本文标题:2018-09-25

          本文链接:https://www.haomeiwen.com/subject/nsxroftx.html