最近把unity升级到了2019.3.4f1。
至于为什么要升级,em。。。好吧,我承认我是馋她身子(笑)。
毕竟我两年前第一次接触unity的时候就觉得UI丑了,而这次终于新版本换了扁平化的UI,深得我意,没道理不升级啊。
但是没想到,这就是恶梦的开始。。
其实半年前我们原来的客户端研发还在的时候,就尝试升级过unity,但是后来回退了,而现在,我切实知道什么原因了。
这里主要是通过几个案例,来分享一下定位bug的分析过程,以及我这几年总结的一个定位bug的杀手锏。
一. Android平台Spine动画失效,游戏内全是白块
如图:
这个问题是当时回滚版本的主要原因。
因为自己对unity不是很熟,对客户端代码也是刚接过来,所以这个问题查起来也是一头雾水。 由于没有办法通过看代码来定位问题(看不懂),所以我们只能用别的方法了。
先把能解决的解决一下:
- 升级spine的运行库版本。
- 升级spine编辑的版本,重新导出动画。
但是很遗憾并没有解决。。
不过这里说个题外话,这里的升级还是很有用的,官方确实了改进了unity2019.3的兼容性,从而很方便的解决了局内物品白块和局外某个英雄动画无法播放的问题。
OK,先来看几个现象:
- 虽然都是使用了spine动画,但是局外动画没问题,只有局内动画有问题。
- 局内动画使用像素风格,并且支持皮肤换装;局外动画为正常风格,不支持皮肤换装。
从上面的现象可以思考几个问题的可能性:
- 像素图片的Filter Mode(point)在android上不支持。
- 像素图片的压缩方式在android上不支持。
- 支持皮肤换装会导致异常。
而针对性的,我们通过如下方式来测试:
- 修改像素图片的Filter Mode为bilinear,测试,依然有问题。
- 修改像素图片的压缩格式为None,即不压缩,测试,依然有问题。
- 建立一个空的场景,仅拖入局内的动画,不做任何代码上的处理,测试,没有问题。
测试的结论如下:
- 图片的Filter Mode是point/bilinear没有影响。
- 图片的压缩格式确实会有影响。像素图片只有不压缩才能正常显示,只要使用了压缩,就一定会出现白块。
这个在unity editor里面可以重现。 - android平台可以正常的播放spine无损的像素风格+皮肤换装的动画。
三个结论综合在一起,我们可以得出一条线索:
问题一定出现在了与播放spine动画相关的代码上。
既然一定定位在了代码上,只要去看代码就好了。
但是前面也说过,客户端代码我刚接过来,很多都看不懂。所以这里其实用了我文章开头提到的杀手锏方法,这里留到后面再说。
最终定位到了是皮肤repack相关的代码的bug,即这段代码。
这段代码是spine官方提供的示例代码(MixAndMatch.cs),文档在spine-unity-mix-and-match.md,估计spine官方自己也还没有完全兼容好。
// 1. Collect all the attachments of all active skins.
if (_collectedSkin == null)
{
_collectedSkin = new Spine.Skin("Collected skin");
}
_collectedSkin.Clear();
_collectedSkin.AddAttachments(_skeletonAnimation.Skeleton.Data.DefaultSkin);
_collectedSkin.AddAttachments(_equipsSkin);
// 2. Create a repacked skin
Clear();
_repackedSkin = _collectedSkin.GetRepackedSkin("Repacked skin",
_skeletonAnimation.SkeletonDataAsset.atlasAssets[0].PrimaryMaterial,
out _runtimeMaterial, out _runtimeAtlas);
_collectedSkin.Clear();
// 3. Use the repacked skin.
_skeletonAnimation.Skeleton.Skin = _repackedSkin;
RefreshSkeletonAttachments();
至此就解决完了人物局内动画的白块问题了。
至于之后的局内物品白块和某些局外人物动画没法播放的问题,前面已经提到,跟着spine升级版本一起解决了。
二. iPhone在xcode拔线后运行黑屏
这个问题花了我整整两天时间,中间为了解决这个bug,顺便改进了一堆问题。
先来看一下现象:
- 在xcode连线调试一切正常,虽然有些不太常规的提示。
- 一旦拔线,点击图标运行就会直接卡死在黑屏界面,连unity的logo都没加载出来。之后因为超时被系统杀掉。
- Unity在build工程的时候会抛出一些不兼容异常,主要是TargetGuidByName那个接口。
还是一样,我们先把能解决的优先解决一下,先把Unity在编译工程时的异常消灭掉,之后又运行了一遍,问题还在。
既然如此,我们就要去查错误日志了,这个问题麻烦的原因就是xcode上调试一切正常,所以我们只能去设备上看日志。
但是不出所料,设备日志果然没有什么有价值的信息。
那么这里只能根据经验,先来猜测几个可能性的方向了
- 场景挂载的脚本异常,导致无法初始化游戏。
- unity2019.3本身的问题,其导出的xcode项目就是有问题。
- 我们项目设置有问题,导致导出的xcode项目有问题。
我们来逐个测试一下:
- 将初始场景的脚本都禁用掉,运行,依然有问题。
- 使用unity重新创建一个新的空工程,编译运行,没有任何问题。
- 将我们自己项目中的所有内容都删除,仅创建一个空的场景,编译运行,没有任何问题。
到了这里,我们基本能够得到如下结论:
- 这个问题与脚本执行无关。
- unity本身没有问题。
- 我们项目的工程设置没有问题。
那么只有一个可能:
我们项目工程中某些编译到IOS项目中的文件有问题。
那么麻烦的事情来了,怎么才能找到这个/这些文件呢?
这里就要祭出我多年的杀手锏了:
二分删除法
哈哈,是不是很常见的名字?不过这个方法确实很有用,如果你在解决一个问题已经没有任何头绪的时候,这种方法是极其有用的。
举个例子:
一段代码有问题,但是不知道哪里有问题,怎么办?
删代码。
从哪里删?怎么删?
从中间开始删,一次删一半。
如果删掉之后还有问题,就说明问题代码在保留的一半里;如果删掉之后正常了,就说明问题在删掉的代码里。
之后再到有问题的代码里继续做二分删除即可。
再举个例子:
Git提交记录1000出现问题,而可以确定的是提交记录100没有问题。
怎么办?
一样,二分删除,找到有问题的那次提交。
可以直接使用git bitsect
命令,感谢 hxgdzyuyi 指出。
那么文中的文件定位呢?文件目录稍微麻烦一点。
- 你需要自己约定一下顺序,比如以文件名/修改时间排序即可。
- 先对文件目录建立一下Git。 这样删掉某些文件后解决了问题后,可以很方便的回滚到上次提交。并且可以很方便的看到删除历史。
- 如果不想使用git,那么利用好回收站也可以。
那如果不是一行代码/一次提交导致的问题怎么办?
其实没有区别,即使是多个地方导致的问题,你最终也能定位到有问题的代码行/提交区间的边缘。
从而最终也会直到是哪个地方出了问题,解决一个后继续缩小范围即可。
而正是用这种方法,我最终定位到居然是一个小小的lib库导致了这个问题。。。这谁能猜的到。。
不过为什么会花整整两天呢?而且这两天还是都加班到了凌晨3点?
因为Unity编译+xcode编译+手动修改xcode工程时间实在太久了啊。。。再加上Git reset和调试的时间,一次差不多要1个小时左右,也就是我8个小时只能调8次。。
所以这次顺带着把xcode的工程修改都改成自动化的了。
想起以前cocos的自动编译,引入gradle的各种规则,到最后我自己都快看不懂了。。unity果然还是成熟很多。
参考链接: iOS.Xcode.PBXProject
OK,差不多就这样了。
这套方法很早之前就想跟大家分享了,希望对大家有点用。
XA on #
其实二分查找法适用于生活中很多问题,不仅限于代码问题,而且很多计算机算法都可以延伸到很广的领域去应用~
Reply
Dante on #
是的,我记得之前有个经典的问题是钥匙掉到池塘里怎么找最快,有很多回答,比如从中间向四周找,或者从四周向两边找,最后给出的标准答案是从一边向另一边找。
而有了计算机的帮助之后,这个答案可以变得更加高效。
Reply
hxgdzyuyi on #
所以还有时间上的二分查找 。git bitsect 大法
Reply
Dante on #
赞! 我之前不知道有这个命令,很好用,解放了人工记录的时间
Reply