静观人间 发表于 2024-7-15 17:58

如何定位游戏发烧问题

1)如何定位游戏发烧问题
​2)Unity获取指定脚本的引用对象
3)如何知道打包时的一个Shader有多少变体
4)如何优化Font.CacheFontForText频繁造成的耗时峰值
<hr/>这是第300篇UWA技术常识分享的推送。今天我们继续为大师精选了若干和开发、优化相关的问题,建议阅读时间10分钟,当真读完必有收获。
UWA 问答社区:answer.uwa4d.com
UWA QQ群2:793972859(原群已满员)
Performance

Q:目前项目的发烧问题很头疼,2D游戏,基于TileMap、SpriteRenderer和UGUI的衬着,封锁了垂直同步,TargetFrameRate设置为60。Android和iOS上发烧都很严重,而且在斗劲好的机型(比如iPhone 12这种),发烧现象甚至更明显。
跟大部门情况分歧,发烧并没有怎么影响帧率,在大部门机型上,帧率都不是问题,连iPhone 8机型,都能60帧跑满。
在Unity Profile和Xcode都进行过性能分析,CPU最明显的热点函数就是Spine的骨骼动画更新计算。然而到一个没有骨骼动画的场景,发烧现象稍好点,但还是比预期的要烫不少(场景中除了地面和一些静态贴图,基本就没有多少东西)。
另一个斗劲遍及的发烧点是网络测试,在某场景封锁网络后,发烧依然严重。甚至启动游戏,逗留在登录界面一会儿,发烧现象都比此外游戏更明显。
猜测是否由于每帧的顶点数量过多造成的,在游戏中Unity的Status面板,顶点数量Verts达到了40KB,三角形Tris也有几乎20KB。看起来很多,但我不太清楚当前主流游戏这个数值的级别概略是多少。而且,顶点和三角形的数量很难解释登录界面依然容易发烧,毕竟登录界面这些数值不成能很高。
本身也做了很多测试了,实在搞不清楚问题究竟在哪儿,Unity是还有什么出格需要优化的,针对发烧的点吗?
A1:可以用Xcode抓帧看看带宽,Load Store Action是否合理。感激littlesome@UWA问答社区提供了回答
A2:以下是我的建议:
1. iPhone 8上跑满60帧,证明CPU、GPU都没有达到瓶颈,消耗在较为合理的范围。保举在Unity Profiler看一下CPU端的消耗,以及查看一下DrawCall数量。
2. 保举使用FrameDebugger,看一下是否有冗余的物体或者后措置在衬着,尤其是你说的启动界面有发烧。
3. 如果初级错误都排查过了,那么建议看一下是否用了原生的插件。
4. 衬着的分辩率是否调整过了,RenderScale的值和FrameDebugger可以查出来分辩率。感激张振东@UWA问答社区提供了回答
A3:关于发烧的问题,凡是要从几个角度排查:CPU压力(耗时)、GPU压力(耗时和带宽,可以考虑降低分辩率看看发烧问题是否会有改善)和IO等几个角度。从题主的问题上看,耗时应该是没问题,都能跑满帧(当然60帧本身就是对发烧影响斗劲大的一点,可以看看限制30帧会不会发烧有下降),所以要看看一些隐形的东西是否有问题。比如带宽,可以用Snapdragon在高通手机上跑一跑。如果带宽较高,看看纹理的一些设置是否合理,比如是否压缩、是否开启Mipmap,这两项凡是都是需要设置成开启的。还可以查看是否有不必要的BlitCopy操作,在URP项目中斗劲容易呈现Copy Color和Copy Depth浪费。对于IO,需要看看是否存在子线程里面有频繁IO的现象。感激Xuan@UWA问答社区提供了回答,欢迎大师转至社区交流:
https://answer.uwa4d.com/question/62910e1ab87a457351669b0c
<hr/>Script

Q:Unity获取指定脚本的引用对象:一个GameObject上挂载了一个Script,这个Script引用了很多资源。如何只获得这个Script引用的资源呢?AssetDatabase是会获取所有的引用,但是并没有做区分。


A1:有Guid可以使用以下代码加载:
var assetPath = AssetDatabase.GUIDToAssetPath(guid);
var texture2D = AssetDatabase.LoadAssetAtPath(assetPath);感激萧小俊@UWA问答社区提供了回答
A2:通过SerializedObject拿到了所有的ObjectReference,然后AssetDatabase.GetAssetPath获取对应路径:var assetObj = AssetDatabase.LoadAssetAtPath<Object>(path);
if (assetObj != null)
{
    GameObject gameObj = assetObj as GameObject;
    if (gameObj != null)
    {
      MeshRendererTextureStreamingData streamingOBJ =
            gameObj.GetComponent<“taget component”>();
      if (streamingOBJ != null)
      {
            SerializedObject so = new SerializedObject(streamingOBJ);
            SerializedProperty it = so.GetIterator();
            bool first = true;


            while (it.Next(true))
            {
                if (it.propertyType == SerializedPropertyType.ObjectReference && it.objectReferenceValue!=null)
                {
                  // Debug.LogError(it.objectReferenceValue);
                  var depPath = AssetDatabase.GetAssetPath(it.objectReferenceValue);
                  streamingAssets.Add(depPath);
                }
            }

      }
    }
}感激题主null@UWA问答社区提供了回答,欢迎大师转至社区交流:
https://answer.uwa4d.com/question/6291b10cb87a457351670c7e
<hr/>Shader

Q:有什么方式可以知道打包的时候的一个Shader有多少变体?
A:直接使用Unity提供的回调函数,该函数会在Shader被打包(无论是打AssetBundle包还是Build的时候)时自动调用。

如图的三个参数:“shader”暗示回调函数当前在措置的Shader资源,”data”是参与编译的变体列表。所以data.count()就是该Shader资源的(参与编译的)变体数量了。所以,只要在回调函数逻辑里把这两个参数Log输出就行了。感激Faust@UWA问答社区提供了回答,欢迎大师转至社区交流:
https://answer.uwa4d.com/question/629d6e39b87a4573517010b0
<hr/>UGUI

Q:请问 Font.CacheFontForText频繁造成耗时峰值,该如何优化?



A:可以参考这篇文章:《Unity3D UGUI 优化:Font.CacheFontForText》

Font.CacheFontForText是在Text衬着时,当对应字体的FontTexture找不到需要衬着的字符时,就会从头生成FontTexture,FontTexture尺寸越大,从头生成这张FontTexture就越耗时。

因此为了减少运行过程中该函数造成的耗时峰值,可以事先衬着所需要的字符,避免在运行时呈现从头生成FontTexture。

使用UWA的GOT Online东西验证了一下上面所提到的解决方案,成果对比如下所示:


优化前



优化后


优化后之所以还是存在部门Font.CacheFontForText的耗时峰值,是因为在测试后期又衬着了之前FontTexture中没有的字符。
感激宗卉轩@UWA问答社区提供了回答,欢迎大师转至社区交流:
https://answer.uwa4d.com/question/629d6710b87a45735170097b
封面图来源于网络
<hr/>今天的分享就到这里。当然,生有涯而知无涯。在漫漫的开发周期中,您看到的这些问题也许都只是冰山一角,我们早已在UWA问答网站上筹备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你插手,也许你的方式恰能解别人的燃眉之急;而他山之“石”,也能攻你之“玉”。
官网:www.uwa4d.com
官方技术博客:blog.uwa4d.com
官方问答社区:answer.uwa4d.com
UWA学堂:edu.uwa4d.com
官方技术QQ群:793972859(原群已满员)
页: [1]
查看完整版本: 如何定位游戏发烧问题