关于script:2022年度大赏-UWA问答精选

37次阅读

共计 7570 个字符,预计需要花费 19 分钟才能阅读完成。

UWA 每周推送的知识型栏目《厚积薄发 | 技术分享》曾经随同大家走过了 304 个工作周。精选了 2022 年十大精彩问答分享给大家,期待 2022 年 UWA 问答持续有您的陪伴。

UWA 问答社区:answer.uwa4d.com
UWA QQ 群:465082844


Q1:动静获取 URP 设置里自定义的 RenderFeatures

咱们在 URP 我的项目中自定义了多个 RenderFeatures 去实现游戏成果,当初想动静开启和敞开相干的 RenderFeatures,请问怎么在代码里动静获取这些 Features 呢?

A:咱们应用反射获取,供参考:

public static class ScriptableRendererExtension
{private static readonly Dictionary<ScriptableRenderer, Dictionary<string, ScriptableRendererFeature>> s_renderFeatures = new Dictionary<ScriptableRenderer, Dictionary<string, ScriptableRendererFeature>>();

    public static ScriptableRendererFeature GetRendererFeature(this ScriptableRenderer renderer, string name)
    {if (!s_renderFeatures.TryGetValue(renderer, out var innerFeatures))
        {var propertyInfo = renderer.GetType().GetProperty("rendererFeatures", BindingFlags.Instance | BindingFlags.NonPublic);
            List<ScriptableRendererFeature> rendererFeatures = (List<ScriptableRendererFeature>)propertyInfo?.GetValue(renderer);
            if (rendererFeatures == null)
            {s_renderFeatures[renderer] = null;
            }
            else
            {innerFeatures = new Dictionary<string, ScriptableRendererFeature>();
                for (var i = 0; i < rendererFeatures.Count; i++)
                {var feature = rendererFeatures[i];
                    innerFeatures[feature.name] = feature;
                }
                s_renderFeatures[renderer] = innerFeatures;
            }
        }
        if (innerFeatures != null)
        {innerFeatures.TryGetValue(name, out var result);
            return result;
        }
        return null;
    }
}

感激 yqsas@UWA 问答社区提供了答复


Q2:Skybox 的环境照明问题

A 场景,通过 additive 的形式加载 B 场景。2 个场景中都没有任何灯光(包含平行光)、Reflection Probe,且 Enviroment Reflections 的 Intensity Mulitiplier 为 0,纯靠 Enviroment Lighting 中的 Skybox 进行照明。

然而 SetActive 为第二个场景之后,就会发现照明彩色,如右图所示。从新 SetActive 为第一个场景,环境照明正确,如左图所示。

如果模式不为 Skybox 而是 Color,则没有上述问题。目前打算自建环境光球谐信息,而不应用 unity_SHAr 相干数据。有什么比拟好的解决方案吗?

A:当把场景 B 设置为 Active 的时候,整个 Game 的 Environment 的设置就主动切换成场景 B 的设置了,这时候从 FrameDebugger 外面能够看到球谐系数变成 0 了。所以两个模型都黑了。

从 Skybox 改成 Color,起作用的是下图中的 3 个数值,它们不是 0,所以不是黑的。

切换场景 A 为 Active 的时候的渲染成果,球谐系数是能够获取到的,所以成果也是失常的,如下图。

所以尝试了一下对场景 B 进行烘焙,当有了 LightingData 后,切换到场景 B,渲染成果也失常了。变亮了是因为场景 B 原始设置的 Intensity multiplier 是 5,从 FrameDebugger 外面看球谐系数不是 0 了,应该是烘焙后的 LihgtingData 外面保留了球谐系数。

感激 Xuan@UWA 问答社区提供了答复


Q3:NGUI Label 自定义材质球有效

想在 NGUI 下做一个字体溶解 Shader,自定义的 Shader 材质球给 Label 不起作用,有没有大佬理解这块内容?

A:猜想题主是要在编辑器外面的材质球对象上调整_Threshold 的数值,但在 Game 窗口发现文本没有发生变化。

实质起因是 NGUI 在对 Label 进行渲染的时候应用的并不是编辑器外面赋值的材质球,而是在 NGUI 进行合并 DrawCall 后动态创建的 Material,所以咱们须要对这个材质球进行材质球属性设置。

这里能够通过脚本来给理论渲染 Label 的材质球调整属性达到成果。以下别离是 Threshold 为 0 和 Threshold 为 0.4 的成果。

public class TestLabel : MonoBehaviour
{
    public float threshold;
    public UILabel label;

    void Update()
    {if (label.drawCall != null)
            label.drawCall.dynamicMaterial.SetFloat("_Threshold", threshold);
    }
}

PS:这样解决的害处是,和这个 Label 在同一个 DrawCall 的 Label 都会受到影响,所以须要将这些成果的 Label 的 Depth 做非凡解决,和其它的 Label 不放在同一个 DrawCall 中。

另外在 NGUI 的 UI DrawCall 脚本中,能够关上 SHOW_HIDDEN_OBJECTS,这样在编辑器外面是能够看到生成具体的 DrawCall 对象,也就能够看到它们的材质球属性变动。

从下图能够看到具体的 DrawCall,它的材质球名字会在后面加 [NGUI] 的字样,和编辑器里不是同一个材质球。

感激 Xuan@UWA 问答社区提供了答复


Q4:Target API level 降级到 31 后 Android 12 启动黑屏卡死

以后海内版本有硬性要求:Target API level 必须降级到 31,降级之后在 Android 12 机型上启动游戏,Unity 闪屏之后卡死,其余 Android 版本失常。

咱们应用的 Unity 版本:2017.4.27f
其余一些简略测试:去掉闪屏,导出新的空工程,都会呈现启动卡死。

其余一些 Unity 论坛上的形式尝试均失败:
https://forum.unity.com/threa…
https://forum.unity.com/threa…

之前咱们也遇到降级之后无奈装置的问题,而后参照其余解决了装置,只是黑屏无奈解决。以后最新尝试 Unity 2019 版本是失常,初步判断是与以下问题同时修复的:
https://issuetracker.unity3d….

不晓得是否有其余大神遇到此问题并解决了?如果有降级之后失常,请告知一下 Unity 版本号,谢谢。

A1:查了一下这个问题,是因为 TelephonyManager 的 listen 函数在 Android 12 过期了,如果没有受权 READ_PHONE_STATE 权限,此函数会抛出一个 SecurityException。

而 Unity 在启用了自带的音频系统的状况下,凑巧在启动时机会去调用这个办法以实现“在用户接电话时游戏静音”的性能,抛出的异样影响了后续的流程导致卡死。

论坛上有人遇到了相似的问题,然而体现为解体:
https://forum.unity.com/threa…

我的项目能降级引擎的话,能够试试这里提到的修复的版本:
https://issuetracker.unity3d….

如果我的项目不能降级引擎,也有一个解决办法:

  • 反编译 classes.jar
  • 批改 UnityPlayer 类的 addPhoneCallListener 实现,判断 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) return;据说 Android 12 开始不须要本人解决静音了
  • 再编回 classes.jar

另外能够试试以下这个反混同工具,本人重命名类名变量名之后再反编译。
https://github.com/FabricMC/E…

感激 littlesome@UWA 问答社区提供了答复

A2:不降级引擎 Unity2017,应用 JByteMod 批改 classes.jar 的 addPhoneCallListener 接口,就能够失常应用。

做法就是把 addPhoneCallListener 所有 Code 都删掉,我是把

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) return;

这段代码的 OpCode 插入

getstatic int Build$VERSION.SDK_INT bipush 31 if_icmplt 0 return label 0

感激 yang@UWA 问答社区提供了答复

A3:我用 2018 4.30f1 编了一个 classes.jar 的包,音频应用 Wwise,视频用的 Avpro。办法就是依照楼上 @yang 提供的计划判断 SDK 版本小于 31 则 return。

实测替换就能够解决问题 分享给还在纠结的人:
链接:https://pan.baidu.com/s/1Issr…
提取码:1111

感激 liwei@UWA 问答社区提供了答复

A4:用 2018.4.25 版本按 @yang 提供的计划也能够解决:
链接:https://pan.baidu.com/s/14r_4…
提取码:otr0

感激 lyd@UWA 问答社区提供了答复

A5:Unity 2017.4.20f2 依照下面用 JByteMod 批改 jar 的方法解决了启动黑屏的问题,摸索着插入胜利了,对某些插入不相熟,我是 jar 反编译了去看别的中央相似的写法,而后在 JByteMod 中找到对应的右键编辑查看插入属性。大体依照程序一个个写上去,label 0 插入完之后再插入 if_icmplt 0。

感激 RdUGz39WR5Cx@UWA 问答社区提供了答复


Q5:计算大文件 MD5 耗时问题

在计算大文件 MD5 的时候,存在耗时重大问题,大略 2 分钟,在手机上承受不了,有大佬有办法吗?

测试发现:改 Buffer 大小到 1MB,由 2200 毫秒变成了 1980 毫秒,优化成果并不显著。
https://itecnote.com/tecnote/…

A:能够尝试应用 xxHash 算法,比照过性能数据,比 MD5 算法快很多。
https://github.com/uranium62/…
https://github.com/Cyan4973/x…

感激马三小伙儿 @UWA 问答社区提供了答复


Q6:资源打包关系依赖树

想做包体资源剖析,大家有什么好的树显示工具或者思路举荐吗?有比拟好的开源计划也能够。最简略就像 N 叉树一样,比方 root 一个文件名,而后开展整个树结构。

A1:我本人做了一个,供参考。都是用 Unity 本人的 IMGUI 最根本的接口去实现。
EditorWindows
GUI.Box
GUI.BeginGroup
GUI.Label
Handles.DrawBezier
Handles.DrawWireDisc
TreeView
基本上,组织好各个 AssetBundle 的依赖关系其实是很好出现的。

感激黄程 @UWA 问答社区提供了答复

A2:举荐一款比拟好用的插件,不止有依赖树,还有其余打包的资源数据可供剖析:
https://assetstore.unity.com/…

感激郑骁 @UWA 问答社区提供了答复


Q7:对于函数参数应用 Lambda 表达式的疑难

对于函数参数应用 Lambda 表达式的疑难:

写法一:_socket.BeginSend(data, offset, len, SocketFlags.None, out _socketError, new AsyncCallback(OnSendData), _socket);

写法二:_socket.BeginSend(data, offset, len, SocketFlags.None, out _socketError, OnSendData, _socket);

请问写法二实质同第一种是一样的?编译器会帮忙 new 一个 AsyncCallback?或者 OnSendData 指向的是函数的地址,没有 new 的开销?

A:我构筑了两个相似的办法(省略了前后实现)以验证两种写法是否有差异。

编译后,应用 dnSpy 工具查看 dll 文件,发现 IL 代码中都会有 new 的开销,即两种写法实质上是完全一致的。

感激 Faust@UWA 问答社区提供了答复


Q8:预制物嵌套导致 AssetBundleName 批改后对母预制物失落援用

Unity 2020.3.16 预制物嵌套时,子预制物援用的图片 AssetBundleName 批改后,母预制物会失落援用。

举例来说,预制物 A 中有个预制物 B,而后预制物 B 上的 RawImage 援用图片 C。ABC 三个打到不同 AssetBundle 中。

首次打包,加载全副 AssetBundle,实例化 A,A 显示失常。

批改图片 C 包名,再次打包,A 所在包不会有变动。然而加载全副 AssetBundle,实例化 A,A 会失落 C 的援用。

反编译 AssetBundle 会发现,理论预制物 A 所在资源包数据中有图片 C 的援用数据,然而因为二次打包 A 包无变动,就没有更新 C 所在包的数据。

这个问题降级 Unity 是否能够解决?或者在以后版本是否能够避开?

反编译 AssetBundle 会发现 A 所在 Bundle 会间接以 External References 模式关联到图片 C 的地址,并且 AssetBundle 也会依赖到图片 C 所在 Bundle(然而不依赖到嵌套 Prefab B 所在 Bundle)。

Prefab B 从新关联图片 D 再打包,A 援用会失常刷新。然而仅仅批改图片 C 的 BundleName 再打包,不会触发 A 从新打包。

AssetBundle 的 Manifest 显示的 A 和 B 依赖关系是不正确的,显示还是 A 依赖 B,B 依赖 C,和理论解包进去的不一样。

当初曾经用追踪 Prefab 嵌套树,外加资源 BundleName 监督的流程临时解决了打包问题。然而还是心愿能取得更标准的解决方案。

A1:Unity Prefab 嵌套目前只解决了 Editor 局部,打包 AssetBundle 时,会将 Subprefab 的序列化文件局部 copy 一份到 Rootprefab,其实就等于 AssetBundle 环境下,嵌套 Prefab 不失效。

感激郑骁 @UWA 问答社区提供了答复

A2:Unity 在增量打包的时候,会计算 AssetFileHash,在这里计算的时候,只思考了嵌套的 Prefab,然而理论打 AssetBundle 时,应用的是嵌套 Prefab 的援用项,咱们以后做法是批改 Unity 的源码,在计算 AssetFileHash 时就将嵌套 Prefab 开展。

感激顾中一 @UWA 问答社区提供了答复


Q9:multi_compile 的 Keyword 是不是须要被动退出到 SVC 外面去

multi_compile 的 Keyword 是不是须要被动退出到 SVC 外面去?

A:对于一个 Shader 资源来说,在我的项目进行打包构建时,multi_compile 定义的关键字会把 Shader 中含有该关键字但理论未应用的变体也进行构建,而 shader_feature 定义的关键字则不会。

但当咱们我的项目中应用 SVC 收集变体时,并不是所有 multi_compile 定义的变体都须要被动退出到 SVC 中,只有咱们理论用到的须要收集。

进行试验如下:
试验构建场景,通过 SVC 收集变体、打成 AssetBundle 包。在场景中提前加载并 Warmup,再实例化一个用到相干 Shader 中变体“FOG_EXP2”的预制体。(变体“FOG_EXP2”是 multi_compile 关键字定义的。)

状况一:SVC 中没有蕴含变体“FOG_EXP2”。此时会在实例化时触发 Shader.CreateGPUProgram(相当于回到该 SVC 所援用的 Shader 中去加载了),不满足咱们收集变体并预热、从而升高游戏过程中 Shader 加载耗时的需要。

状况二:SVC 中收集了变体“FOG_EXP2”。实例化时没有触发 Shader.CreateGPUProgram,阐明该变体被失常 Warmup 了。

论断是,对于包体构建是没有区别的,SVC 打包时会依赖对应的 Shader,multi_compile 定义的关键字天然都会参加构建;对于变体预热,只有是须要用到的变体,必须收集到 SVC 中并 Warmup 后,才不会在实例化渲染时触发 Shader.CreateGPUProgram。

感激 Faust@UWA 问答社区提供了答复


Q10:Xcode 工程中,如何通过 Object- C 代码反调 Unity 侧的 C# 代码

在 iOS 平台下,IL2CPP 导出的 Xcode 工程中 Object- C 调用 Unity 办法是通过 SendMessage 实现的:

请问在 Mac 平台下 IL2CPP 形式导出的 Mac 工程,如何通过 Object- C 代码反调 Unity 侧的 C# 代码?也是通过 SendMessage 的形式吗?然而我没找到相干的接口。

A:用 SendMessage 是能够实现的,然而效率不好。能够参考我这个 Object- C 回调 Unity。把你须要的接口,写成函数指针,在 Object- C 里注册,须要时做回调。
能够参考《Unity 与 Object- C 交互》。

感激廖武兴 @UWA 问答社区提供了答复


明天的分享就到这里。当然,生有涯而知无涯。在漫漫的开发周期中,您看到的这些问题兴许都只是冰山一角,咱们早已在 UWA 问答网站上筹备了更多的技术话题等你一起来摸索和分享。欢送酷爱提高的你退出,兴许你的办法恰能解他人的当务之急;而他山之“石”,也能攻你之“玉”。

官网:www.uwa4d.com
官网技术博客:blog.uwa4d.com
官网问答社区:answer.uwa4d.com
UWA 学堂:edu.uwa4d.com
官网技术 QQ 群:465082844

正文完
 0