UWA问答 | 博客 | 游戏及VR应用性能优化记录分享 | 侑虎科技 - audio
1）如何计算弧线弹道的落地位置 2）Unity 2021 IL2CPP下使用Protobuf-net序列化报异常 3）编译问题，用Mono可以，但用IL2CPP就报错 4）Wwise的Bank在安卓上LoadBank之后，播放没有声音 这是第393篇UWA技术知识分享的推送，精选了UWA社区的热门话题，涵盖了UWA问答、社区帖子等技术知识点，助力大家更全面地掌握和学习。 UWA社区主页：community.uwa4d.com UWA QQ群：793972859 Physics Q：请教一个问题，现在我有一个可以打出弧线弹道的迫击炮，可以放到场景里任何位置，怎么能算出可以打到的区域和打不到的区域？要考虑场景内建筑物的遮挡问题。 然后，旋转一周的话以多少角度来计算呢？ A1：碰撞不只有地面的碰撞，所以高度图也不能解决。自己模拟弹道轨迹计算，然后进行分段逐个探测就好。要真说GPU来解决，可能更多是基于视觉表现上的，而且会把问题变得更加复杂，只是提升下数量非常多的时候的性能。 可以参考Niagara，使用深度图、SDF、或者新一代GPU的物理探测方案来做支持。 感谢听风辨雨@UWA问答社区提供了回答 A2：笨方法就是先旋转一周一层循环，再从0到90度仰角再一层循环，每个发射角用抛物线公式计算虚拟炮弹的运动轨迹，轨迹连线，中间有物理的射线检测判断有没有障碍，最后生成大量的抛物线的落点画出来。 感觉上SphereCast比射线检测更好，这个用Compute Shader算应该很合适。但是建筑物碰撞部分怎么在CS里面处理？是不是把建筑物的碰撞先预处理成体素还不确定。 感谢jump晃悠@UWA问答社区提供了回答 A3：微积分基础思想就能解决，小范围内以直代曲，也就是分段SphereCast即可。如果要更精确，就不要用均匀分段，而是根据曲率不同分段长度不同，曲率就是抛物线函数的二次导数。 另外，每个角度算一个曲线就好，一般不需要提前把所有可能都算出来。 感谢偶尔不帅@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/66823001682c7e5cd61bf831 Script Q：请问有遇到这个问题的嘛，在安卓上序列化报以下异常： NotSupportedException: D:/Unity2021/Unity 2021.3.4f1/Editor/Data/il2cpp/libil2cpp/icalls/mscorlib/System.Reflection/RuntimeParameterInfo.cpp(23) : Unsupported internal call for IL2CPP:RuntimeParameterInfo::GetTypeModifiers - "This icall is not supported by il2cpp." A：.NET功能可能不受IL2CPP支持，试着升级一下Unity或者绕过你要实现的功能。 感谢萧小俊@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/666ddfe2682c7e5cd61bf80e Script Q：请教个问题，使用IL2Cpp就报错，用Mono编译就可以过。 A：IL2CPP里代码太多也会这样，如果开了Script Debugging关掉试试。若还没解决，那把这里调高看看（如下图），让代码少一点。 不过调高了有可能有别的报错，如果Strip了需要用到的东西，还得自己加到link.xml里。 感谢deviljz@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/66822eaf682c7e5cd61bf82f Audio Q：请教个问题，Wwise的Bank在安卓上LoadBank之后，播放没有声音，请问有人遇到过吗？ A：连Wwise的Profiler，看下是Bank加载失败了，还是播放失败了，或者根本就没有Listener，都有可能的，Profiler都会告诉你。 感谢断弦的旋律@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/66822f33682c7e5cd61bf830 封面图来源于网络 今天的分享就到这里。生有涯而知无涯，在漫漫的开发周期中，我们遇到的问题只是冰山一角，UWA社区愿伴你同行，一起探索分享。欢迎更多的开发者加入UWA社区。 UWA官网：www.uwa4d.com UWA社区：community.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：793972859
1）在TMP中计算书名号《》高度的问题 2）FMOD设置中关于Virtual Channel Count&Real Channel Count的参数疑问 3）Unity 2021.3.18f1 ParticleSystemTrailGeometryJob粒子拖尾系统崩溃 4）XLua打包Lua文件粒度问题 这是第375篇UWA技术知识分享的推送，精选了UWA社区的热门话题，涵盖了UWA问答、社区帖子等技术知识点，助力大家更全面地掌握和学习。 UWA社区主页：community.uwa4d.com UWA QQ群：465082844 UI Q：如下图所示，输入的文字中有书名号，但使用ContentSIzeFitter计算得到的高度是错误的（貌似它计算的比实际少，导致没换行）： 而使用默认的Text就没这个问题（相同的字体）： 请问如何在TMP中计算书名号《》的高度？ A：好像是对东亚语言有特殊处理，都会换行。不确定是不是这儿的原因，输入“你好”两字也是一样的效果，可以在这儿改改试试。 把这行注释掉就没问题了，但我不保证没其他问题，因为要把TMP的包导入项目里，源码会少算了换行第一个字符的长度，GeneratedMesh和PreferedHeight是分开算的，所以显示和高度计算不一样，GeneratedMesh是对的，这是Unity自己的Bug，后面几个版本修复了，估计还是得想办法升级TMP的版本。 参考文档： https://docs.unity3d.com/Packages/com.unity.textmeshpro@3.2/changelog/CHANGELOG.html 感谢萌呆瞎@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/65c1d43b40a8d93b624afcce Audio Q：FMOD的设置中，我发现有两个设置选项：Virtual Channel Count和Real Channel Count。想请教一下，一般游戏项目内这两个值要设置多少才合适？我发现外网有人都是拉满的，但这会造成CPU负担。只知道Real Channel Count这个不能太高。所以想了解下通常这俩参数设置多少比较合理。 下图来自外网论坛，Virtual Channel Count设置为512、Real Channel Count设置为128，而我的项目中这俩设置为128、32。 A：Real Channel，如果设置成32对于PC来说是正好。对于移动平台，32应该也能跑。对于超古老的机器，可以考虑设置成16。 Real Channel是同时播放的音频Channel数，如果同时播放的Channel超过这个数字，就会根据优先级把低优先级的音频停掉。实际项目中，极少同时播放如此之多的Channel。如果真发生了，请仔细优化音频播放逻辑。 感谢Zhang Ce@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/65c0b00840a8d93b624afccd Crash Q：Unity 2021.3.18f1 ParticleSystemTrailGeometryJob粒子拖尾系统崩溃。 信息如下： 崩溃手机：Galaxy S24 Ultra 操作系统：Android14 崩溃堆栈： Crashed: Thread: SIGSEGV 0x0000007d2f34a900 #00 pc 0xb30240 libunity.so (ParticleSystemTrailGeometryJob::ConfigurePerParticleTrailParams(ParticleLineParameters&, ParticleSystemParticles const*, unsigned long, ParticleSystemTrailGeometryJob const&, float)) (BuildId: c89a8ec16cd55b42b7a6c100718f0e187c531fc3) #01 pc 0xb29b08 libunity.so (ParticleSystemTrailGeometryJob::ConfigurePerParticleTrailParams(ParticleLineParameters&, ParticleSystemParticles const*, unsigned long, ParticleSystemTrailGeometryJob const&, float)) (BuildId: c89a8ec16cd55b42b7a6c100718f0e187c531fc3) #02 pc 0xb136b8 libunity.so (ParticleSystemGeometryJob::RenderJobCommon(ParticleSystemGeometryJob&, void*, void*)) (BuildId: c89a8ec16cd55b42b7a6c100718f0e187c531fc3) #03 pc 0x5d084 libc.so (BuildId: 37f537c2ba9dcbb262a0a68f41a21da4) 针对以上问题，有经验的朋友欢迎转至社区交流分享： https://answer.uwa4d.com/question/65d87ea040a8d93b624afce9 Lua Q：目前项目中使用XLua，Lua文件一般是每个Lua文件都打一个AssetBundle包，还是所有Lua都打一个AssetBundle包里？直观感觉上每个Lua都单独打一个AssetBundle包比较方便热更新。 A：如果Lua文件数量很多（十万级别），建议不要打Bundle，因为Bundle Header中要存储、反序列化的Asset信息过多，内存会吃不消，所以最好换成自定义的文件方式。参考星铁的Lua。 如果数量少，直接一个Bundle就足够。这样加载也方便：从一个固定的Bundle里Load Asset。 感谢James@UWA问答社区提供了回答，欢迎大家转至社区交流分享： https://answer.uwa4d.com/question/659d108d3625c22cffcb1dce 封面图来源于网络 今天的分享就到这里。生有涯而知无涯，在漫漫的开发周期中，我们遇到的问题只是冰山一角，UWA社区愿伴你同行，一起探索分享。欢迎更多的开发者加入UWA社区。 UWA官网：www.uwa4d.com UWA社区：community.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：465082844
1）Unreadable-Mesh内存占用翻倍问题 2）在TMP中计算书名号《》高度的问题 3）Mipmap如何限定层级 4）FMOD设置中关于Virtual Channel Count&Real Channel Count的参数疑问 这是第374篇UWA技术知识分享的推送，精选了UWA社区的热门话题，涵盖了UWA问答、社区帖子等技术知识点，助力大家更全面地掌握和学习。 UWA社区主页：community.uwa4d.com UWA QQ群：465082844 Memory Q：最近在做性能分析时发现Mesh内存占用异常，部分Mesh在未开启Readable的情况下也不会卸载CPU部分的内存开销。 通过Mesh文件对比发现是m_KeepVertices与m_keepIndices参数差异导致： 查询到网络上的部分源码，发现该参数确实会影响内存释放。 已尝试通过修改文件的方式与SerializedObject的方式去修改这个值，都无法保存，Unity内部会自行修正。请问有谁知道它具体的实现细节，什么样的Mesh数据会导致它会保留CPU端所有数据，以及有何提前规避的方式？ A：可能是网格设置了静态合批导致的，可以试下关掉静态合批看下这部分内存能否被卸载。 感谢宗卉轩@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/65c327433625c22cffcb1e1d UI Q：如下图所示，输入的文字中有书名号，但使用ContentSIzeFitter计算得到的高度是错误的（貌似它计算的比实际少，导致没换行）： 而使用默认的Text就没这个问题（相同的字体）： 请问如何在TMP中计算书名号《》的高度？ 针对以上问题，有经验的朋友欢迎转至社区交流分享： https://answer.uwa4d.com/question/65c1d43b40a8d93b624afcce Asset Q：请问Mipmap如何限定层级？ 比如一张1024x1024纹理，共有Mipmap0~10，但是项目只用到Mipmap0~2，如何省略掉3~10？ 针对以上问题，有经验的朋友欢迎转至社区交流分享： https://answer.uwa4d.com/question/65c0a61340a8d93b624afccc Audio Q：FMOD的设置中，我发现有两个设置选项：Virtual Channel Count和Real Channel Count。想请教一下，一般游戏项目内这两个值要设置多少才合适？我发现外网有人都是拉满的，但这会造成CPU负担。只知道Real Channel Count这个不能太高。所以想了解下通常这俩参数设置多少比较合理。 下图来自外网论坛，Virtual Channel Count设置为512、Real Channel Count设置为128，而我的项目中这俩设置为128、32。 针对以上问题，有经验的朋友欢迎转至社区交流分享： https://answer.uwa4d.com/question/65c0b00840a8d93b624afccd 封面图来源于网络 今天的分享就到这里。生有涯而知无涯，在漫漫的开发周期中，我们遇到的问题只是冰山一角，UWA社区愿伴你同行，一起探索分享。欢迎更多的开发者加入UWA社区。 UWA官网：www.uwa4d.com UWA社区：community.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：465082844
1）Unity场景物体动态合批 ​2）Unity内置音频Android平台播放延迟问题 3）对Unity Package中的Shader打包避免冗余的方案 4）UnityEditor PropertyField并排显示错误 这是第307篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题，建议阅读时间10分钟，认真读完必有收获。 UWA 问答社区：answer.uwa4d.com UWA QQ群2：793972859（原群已满员） Rendering Q1：问题1：为了能使场景中物体合批（同材质球），场景在制作的时候相同的预制体放在了一起，但是FrameDebug看到的却是乱序。请问在这个Frame里的顺序是如何定义才能达到相同的合批？也就是如何能最少化的Objects have different materials？ 问题2：在FrameDebug上下排列且相同物体，依然没有合批。给出的原因是“Objects are lightmapped”。网上给出的解释是使用了不同的光照贴图（这里是一样的），或在相同的光照贴图中由不同的光照贴图UV转换关系。这个转换关系是什么意思？是UV对应的Offset几个参数必须要一样么？但是物体在不同的位置，理论上一定会不一样。请问这个要怎么解决？目前场景中物体占用了120个DrawCall，想尽可能优化。 A：第一个问题，针对不透明物体，Unity默认由近往远绘制（离相机的距离），所以如果想要相同的材质球是连续绘制的，可以通过调整RenderQueue来强行连续绘制，不过可能会导致渲染效果不符合预期（遮挡关系错乱），另外的坏处是会破坏Early-Z的功能，尤其在没有HSR功能的低端机上，Overdraw会造成高复杂度的Shader带来的GPU高压力，所以需要权衡CPU提交Batch的耗时和GPU的压力。 第二个问题，同类型材质球（Shader一样，变体也一样），如果Lightmap index不一样（也就是Lightmap不一样），肯定是不能合批的（SRP Batcher除外，SRP Batch无视材质球，只要Shader和变体一样就可以）。在Lightmap相同的情况下，如果对应的unity_LightmapST不一样，也是不能合批的。这里有两种方法解决，一种是用Static Batching，这样会让unity_LightmapST变成同一种，具体的UV会变成顶点数据（本来子Mesh的UV都是（0,1）的，合并Combined Mesh后会变成类似于（0.3,0.5）这样的区间），就是说顶点里面的UV本来存储的是Local空间的，变成Combined Mesh后，合体的Mesh数据里面存储的是World空间的，这样外在的unity_LightmapST对于不同的子Mesh就会变成共用的（1,1,0,0）这样的共同属性，也就可以合批了。另外一种是开启GPU Instancing，这样unity_LightmapST会变成CBuffer，这样也是会合批的。 如下图所示，Cube1和Cube2在FrameDebugger的唯一区别就是unity_LightmapST不一样，所以不开启GPU Instancing和Static Batching的情况下，是不能合批的，虽然它们是相邻的绘制顺序，Lightmap也是相同的。另外两个Cube(1)和Cube分别用的Lightmap2和Lightmap0。 开启静态合批后，中间的2个Cube合成了一个Batch，它们的unity_LightmapST变成了共同的（1,1,0,0），如下图： 材质球勾选GPU Instancing后，在FrameDebugger面板里面，已经看不到unity_LightmapST这个属性了。这两个Cube也是合成了一个Instanced Batch。如下图： 感谢Xuan@UWA问答社区提供了回答 Q2：对于第二个问题的解决方法。因为我们是动态创建物体。所以无法用静态合批。当我尝试使用GPU Instancing 后，批次几乎没变，一种是我们的不同的Mesh，使用同一个材质球，无法合批；其次是同一个Mesh也无法合批。如下提示： 另外依然还有“Objects are lightmaped”的提示，且已经没有Lightmap_ST，材质球都已勾选GPU： 还有Shader是有两个PASS的。是否另一个PASS也需要加入GPU Instancing 的代码？附Shader代码： Shader "WF/SceneObj" { Properties { [NoScaleOffset]_MainTex("主贴图 (RGBA)", 2D) = "white" {} [Space(20)] _OutlineWidth("描边宽度", Range(0, 0.5)) = 0.018 _OutlineColor("描边颜色", Color) = (0, 0, 0, 1) [Space(20)] _EmissionMask("自发光遮罩 (G)", Range(0, 5)) = 1 [HDR]_EmissionColor("自发光颜色", Color) = (1, 1, 1, 1) _Color("Color", Color) = (1, 1, 1, 1) } SubShader { Tags{ "RenderType" = "Opaque" "Queue" = "Geometry+1" } // lightmap Pass { Tags { "LightMode" = "ForwardBase" } CGPROGRAM #pragma multi_compile_fwdbase #pragma multi_compile_instancing #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include "Lighting.cginc" #include "AutoLight.cginc" #pragma multi_compile_fog #define USING_FOG (defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2)) struct a2v { float4 vertex : POSITION; float3 normal : NORMAL; float2 texcoord : TEXCOORD0; float3 uv1 : TEXCOORD1; // lightmap UNITY_VERTEX_INPUT_INSTANCE_ID }; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; float2 uv1 : TEXCOORD2; half3 worldlight : TEXCOORD1; #if USING_FOG fixed fog : TEXCOORD3; #endif UNITY_VERTEX_OUTPUT_STEREO UNITY_VERTEX_INPUT_INSTANCE_ID }; sampler2D _MainTex; float4 _MainTex_ST; float _EmissionMask; fixed4 _EmissionColor; UNITY_INSTANCING_BUFFER_START(Props) UNITY_DEFINE_INSTANCED_PROP(float4, _Color) UNITY_INSTANCING_BUFFER_END(Props) v2f vert(a2v v) { v2f o; UNITY_SETUP_INSTANCE_ID(v); UNITY_TRANSFER_INSTANCE_ID(v, o); o.pos = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); // o.worldlight = UnityObjectToViewPos(v.normal); o.uv1 = v.uv1.xy * unity_LightmapST.xy + unity_LightmapST.zw; // fog #if USING_FOG float3 eyePos = UnityObjectToViewPos(v.vertex); float fogCoord = length(eyePos.xyz); // radial fog distance UNITY_CALC_FOG_FACTOR_RAW(fogCoord); o.fog = saturate(unityFogFactor); #endif return o; } fixed4 frag(v2f i) : SV_Target { UNITY_SETUP_INSTANCE_ID(i); fixed4 originalCol = tex2D(_MainTex, i.uv) * UNITY_ACCESS_INSTANCED_PROP(Props, _Color); ////------------------------------漫反射------------------------------ //-----------------------------------自发光 //float emission = originalCol.a * _EmissionMask; //自发光 //float emisstionValue = emission < 0.5 ? 0 : 1; fixed3 emisstionCol = originalCol.rgb * _EmissionColor.rgb * _EmissionMask; // //叠加 fixed3 finnalCol = originalCol; fixed4 col = (fixed4)1; #if defined(LIGHTMAP_ON) // half4 bakedColorTex = UNITY_SAMPLE_TEX2D(unity_Lightmap, i.uv1.xy); col.rgb = DecodeLightmap(bakedColorTex); #endif finnalCol = lerp(finnalCol, emisstionCol, originalCol.a) * col.rgb; // fog #if USING_FOG finnalCol.rgb = lerp(unity_FogColor.rgb, finnalCol.rgb, i.fog); #endif return fixed4(finnalCol, 1); } ENDCG } Pass { NAME "OUTLINE" Cull Front CGPROGRAM #pragma multi_compile_fog #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct a2v { float4 vertex : POSITION; float3 normal : NORMAL; }; struct v2f { float4 pos : SV_POSITION; UNITY_FOG_COORDS(0) }; sampler2D _MainTex; float4 _MainTex_ST; fixed4 _OutlineColor; float _OutlineWidth; v2f vert(a2v v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); float3 viewNormal = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal); float2 ndcNormal = normalize(TransformViewToProjection(viewNormal.xy)) * pow(o.pos.w, 0.6) * 2; float4 nearUpperRight = mul(unity_CameraInvProjection, float4(1, 1, UNITY_NEAR_CLIP_VALUE, _ProjectionParams.y)); float aspect = abs(nearUpperRight.y / nearUpperRight.x); ndcNormal.x *= aspect; o.pos.xy += ndcNormal * _OutlineWidth * 0.1; UNITY_TRANSFER_FOG(o,o.pos); return o; } fixed4 frag(v2f i) : SV_Target { fixed3 col = _OutlineColor.rgb ; return fixed4(col, 0); } ENDCG } } Fallback "Diffuse" A：动态创建物体的静态合批的方法可以参考StaticBatchingUtility.Combine的API，这是一项运行时静态合批的做法；其次如果使用了多个Pass的Shader，想要使用GPU Instancing进行合批，每个Pass都要设置成GPU Instancing才能正常合批。 感谢宗卉轩@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/62f1c3fb3498a57bd799f8fe Audio Q：最近遇到Unity使用内置音频方案在Android平台音频延迟问题，而且是有时候会延迟。不知道各位使用的是什么方案来解决这个问题？ A：安卓延迟比较大是比较正常的，特别是在一些低端机上。 可以先试试Unity内置的延迟解决方案：ProjectSettings的Audio中的DSP Buffer Size设置为Best latency，然后短音效的AudioType改为Decomppress On Load。 如果无法解决 可以尝试一下官方提供的插件：NativeAudio SDK 如果不想用Unity内置的FMOD，可以尝试一下以下方案：Wwise、Criware 感谢萧小俊@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/62fe0ce92abe7a297c960fe2 AssetBundle Q：收集完Shader Variants后，URP/UberPost变成双份了，怎么删掉？ A：这个问题我的判断是UberPost没有被指定AssetBundle，所以在打包SVC文件时，增加了一个额外的引用。而此时SVC文件需要指定AssetBundle，就将其依赖的未指定AssetBundle文件额外添加了进来。 这个问题的难点是：UberPost是在Packages下的，而Packages文件无法指定AssetBundle。 解决方案1：将URP放到工程中，而非Packages下，这样就可以指定AssetBundle。 解决方案2：改用了可以修改AssetBundle关联性的ScriptableBuildPipeline，手动为UberPost指定打包到某个AssetBundle中，这样打包系统检测到UberPost已有AssetBundle，就不会重复打包了。 感谢小枫不会飞@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/62fc651808994e6b960553e4 Editor Q：我在制作一个工具，用来编辑很多内容，其中我有一个需求，就是在Editor窗口中去修改List的内容，比如添加新的项、删除和修改等。 我是用EditorGUILayout.PropertyField，效果如下： 但是我需要在若干列中展示多个PropertyField，此时效果成了这样子，可以明显看到第二个列表的文本框短了一大截： 如果是三个的话，根本就不显示： 我限制了前两个的长度，然后拉长界面，可以发现其实他可以显示，就是离得太远了： 求问这种情况怎么解决？以下是源码： public class TestWindow : EditorWindow { [MenuItem("test2/open")] private static void Test() { var s_CurrentWindow = GetWindow<TestWindow>("My Tool"); s_CurrentWindow.Show(); } XmlNode node; XmlNode node2; XmlNode node3; SerializedObject serObj; SerializedProperty serPty; SerializedObject serObj2; SerializedProperty serPty2; SerializedObject serObj3; SerializedProperty serPty3; private void OnGUI() { using (var scope2 = new EditorGUILayout.HorizontalScope()) { serObj.Update(); serObj2.Update(); serObj3.Update(); EditorGUI.BeginChangeCheck(); using (var scope = new EditorGUILayout.VerticalScope(GUILayout.Width(500))) { //temp = EditorGUILayout.TextField("What: ", temp); EditorGUILayout.PropertyField(serPty, true); } using (var scope = new EditorGUILayout.VerticalScope(GUILayout.Width(500))) { //temp = EditorGUILayout.TextField("What: ", temp); EditorGUILayout.PropertyField(serPty2, true); } using (var scope = new EditorGUILayout.VerticalScope()) { EditorGUILayout.PropertyField(serPty3, true, GUILayout.ExpandWidth(true)); } if (EditorGUI.EndChangeCheck()) { serObj.ApplyModifiedProperties(); serObj2.ApplyModifiedProperties(); serObj3.ApplyModifiedProperties(); } } } private void OnEnable() { //var titleRow = NewRow( // new Label() { text = "Old" }, // new Label() { text = "new" } // ); //titleRow.name = "title-compare-box"; //rootVisualElement.Add(titleRow); //rootVisualElement node = ScriptableObject.CreateInstance<XmlNode>(); node2 = ScriptableObject.CreateInstance<XmlNode>(); node3 = ScriptableObject.CreateInstance<XmlNode>(); node.InnerTextList.Add("QwQ"); node.CType = ContentType.List; node2.InnerTextList.Add("heiheihie"); node2.CType = ContentType.List; node3.InnerTextList.Add("WWW"); node3.CType = ContentType.List; serObj = new SerializedObject(node); serPty = serObj.FindProperty("InnerTextList"); serObj2 = new SerializedObject(node2); serPty2 = serObj2.FindProperty("InnerTextList"); serObj3 = new SerializedObject(node3); serPty3 = serObj3.FindProperty("InnerTextList"); } private class XmlNode : ScriptableObject { public string Path { get; set; } public string Name { get; set; } public ContentType CType { get; set; } public Dictionary<string, string> AttributeDict { get; set; } = new Dictionary<string, string>(); public List<string> InnerTextList = new List<string>(); public string InnerText { get; set; } //public XmlNode(string path, string name, ContentType cType) //{ // Path = path; // Name = name; // CType = cType; // AttributeDict = new Dictionary<string, string>(); // InnerTextList = new List<string>(); //} } private enum ContentType { String, // 单行文本 InnerText List // 多行的内容，用List存 } } A1：建议直接使用Odin插件。 感谢张迪@UWA问答社区提供了回答 A2：这种List在Editor下可以使用ReorderableList，供EditorGUI使用，表现看起来跟你的需求差不多。 感谢范世青@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/62fcf04e08994e6b9605e420 封面图来源于网络 今天的分享就到这里。当然，生有涯而知无涯。在漫漫的开发周期中，您看到的这些问题也许都只是冰山一角，我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入，也许你的方法恰能解别人的燃眉之急；而他山之“石”，也能攻你之“玉”。 官网：www.uwa4d.com 官方技术博客：blog.uwa4d.com 官方问答社区：answer.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：793972859（原群已满员）
1）TextMeshPro备用字体疑问 ​2）iOS的部分语言的float分隔符变成了逗号 3）ASTC格式支持与官方文档不符 4）关于音效背景音乐的音频文件的格式设置请教 5）LightProbe的信息位置 这是第269篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题，建议阅读时间10分钟，认真读完必有收获。 UWA 问答社区：answer.uwa4d.com UWA QQ群2：793972859（原群已满员） Text Q：TextMeshPro设置的Fallback “字体B”， 文本字体是“字体A”，如果文本显示的字符都是字体A的，CPU会不会收集字体B的贴图信息数据发送给GPU用来渲染？ 不是很理解这个Fallback字体，发现字体A、字体B都在内存中，也就是说其实都是关联上的。是不是所有的后备字体都会在内存中出现？ A：问题一：TMP中所谓Fallback，也就是后备字体，其含义在于当实际使用中出现了当前字体所不支持的字符时，按照：主字体（也就是当前字体）、主字体的后备字体、后备字体的后备字体、通用后备字体、通用后备字体的后备字体、通用默认字体、通用默认字体的后备字体等等的序列遍历所有设置的后备字体，直到找到能够支持的字体为止。 所以，引擎只有在内存中保留Fallback字体资源，才能在当前字体无法显示所有字符时采用Fallback字体。问题中提到的字体B也在内存中是合理的。 当然，如果开发者能确定字体A可以支持项目中出现的所有字体，那么完全可以检查Project Settings中全局的Default Font Asset、全局的Fallback Font Assets List、字体A的Fallback Font Assets，以上三处设置为空，则可以一定程度上节省内存。 问题二：我补充做了实验。对照情况是用某一种主字体显示“New Text”这一串字符，显然足够支持、而内存中也只有该主字体。然后在刚才提到的主字体的后备字体、后备字体的后备字体、通用后备字体、通用后备字体的后备字体、通用默认字体、通用默认字体的后备字体这个序列的每个环节加上不同的字体，显然它们没有被实际用到，但都进入内存了。List里面有几种字体也是一样。 感谢Faust@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/61455c7a8f8c834241ad6caf iOS Q：最近我们打包的时候发现，在iOS设备上，部分语言的小数的分隔符变成了逗号，比如俄罗斯语。后来发现是因为CurrentCulture变成了设备的语言，Android和之前的iOS设备都是en-US，也就导致了读取配置时用float.TryParse会转换失败。 我们的Unity版本是2018.4.33，目前还没有定位是什么原因导致的，不知道有没有人碰到过？如果在游戏启动时强制把CurrentCulture改成英语不知道可不可行？ A：我们项目踩过这个坑，原因就是一些语言里面小数点分隔符不一样，比如法语里分隔符是逗号，属于文化差异。解决方案是游戏启动时设置CurrentCulture为CultureInfo.InvariantCulture： CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture; CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.InvariantCulture; Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture; 参考链接：CultureInfo.InvariantCulture 感谢张迪@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/614bf82a8f8c834241b6484d Texture Q：ASTC格式支持与官方文档不符，根据官方文档（https://docs.unity3d.com/Manual/class-TextureImporterOverride.html）。理论上我们的测试机中，红米2是不支持的ASTC格式的。但实际测试中发现，它是支持的。这是怎么一回事？ A：之前做过相关的测试，红米2（Adren 302）是支持ASTC的，但红米Note2（PowerVR Rogue G6200）是不支持的。并且从Unity 2017.4的Profiler中可以看到纹理的大小是正确的。 这和官方提供的：tvOS (all), iOS (A8), Android (PowerVR 6XT, Mali T600 series, Adreno 400 series, Tegra K1)不符。 从目前的测试来看，可以认为实际支持情况和文档提供的GPU型号并不完全一致。在不支持的机型上会软解成RGBA32或RGB24，实际内存也是一样。 感谢OCEAN@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/613acc768f8c8342419e93fd Audio Q：在UWA的报告中看到要用Streaming的方式读取音频。想问一下关于音频格式，有优化角度比较全的建议吗？ 之前项目里的设置大概是这样子的，合适吗？大家常见的做法是怎样的？有没有一个相对合理的经验值，比如10s的音效大小在100KB这样的参考值？ A：并不推荐使用MP3播放音乐，原因是使用MP3制作素材会无法避免在循环点引入空白音频，参见：https://sound.stackexchange.com/questions/25846/is-it-possible-to-loop-mp3-without-gaps 之前使用过类似于MP3Loop的小工具，但是编码质量也无法控制，最后选择用Ogg（Vorbis）一了百了。 早一些的开发者（1990-2000年前后）会采用MIDI，但MIDI只是记录音乐信息用的格式，实际发声取决于所使用的音源，而音源设计通常会倾向于大而全所以为了有限的MIDI音乐加载完整的音源也会造成资源冗余。 关于PCM或者ADPCM，通常被称为WAV及“三分之一的WAV”，可以根据音效具体内容来决定优化策略，有一个概念需要了解——奈奎斯特采样定理。 简单讲，如果一个声音比较低沉，那么可以适当降低采样率来提升PCM或者ADPCM的“压缩比”，这样可以既保留PCM/ADPCM的低功耗和快速响应，又降低资源占用。假设一个声音比较明显的能量都分布在4000hz之下，那么只需要8000hz的采样率就可以非常好的记录及还原这个声音了。 事实上Unity原声音频最理想的音乐格式是Tracker Module，只是国内开发者了解Demo Scene的比较少，音乐创作者为此类格式创作的经验也非常稀少。 这里贴一个Tracker音乐的外网视频连接，大家可以听一下看看会不会唤起一些童年回忆：https://www.youtube.com/watch?v=gHQYYh7J1O4 翻译了Unity官方十一年前的一篇Blog：《.mod in Unity》 文章的中文翻译可以戳原问答查看。 几个常用Tracker音乐制作软件的网站链接：OpenMPT，MilkyTracker，schismtracker。有兴趣的朋友可以尝试一下用这种古老的格式创作音乐，可以做到既不牺牲音质也不占用多少空间。 关于进一步的音频优化技巧，个人比较推荐刷一遍Wwise的251认证课程内容，免费而且实际上都是些通用的技巧：https://www.audiokinetic.com/zh/courses/wwise251 感谢Icarus@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/5c189a63bf256b207515158b Rendering Q：场景中的LightProbe信息是保存在lightingData.asset文件中吗？ A1：如果要保存，可以考虑这样存： AssetDatabase.CreateAsset(Instantiate(LightmapSettings.lightProbes), “Assets/lightProbe.asset”); 感谢萧小俊@UWA问答社区提供了回答 A2：不确定，lightingData.asset中主要存放Lightmap的对应关系，但LightProbe是否也在里面就不确定了。 感谢OCEAN@UWA问答社区提供了回答 A3：对于LightProbes更多的操作可以参考这篇文章：《聊聊LightProbe原理实现以及对LightProbe数据的修改》 感谢Xuan@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/6139d52c8f8c8342419d3885 20210927 更多精彩问题等你回答～ Vulkan API的性能及兼容性 Unity TMP字体方案如何选择 如何实现AAB包的增量更新 封面图来源于网络 今天的分享就到这里。当然，生有涯而知无涯。在漫漫的开发周期中，您看到的这些问题也许都只是冰山一角，我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入，也许你的方法恰能解别人的燃眉之急；而他山之“石”，也能攻你之“玉”。 官网：www.uwa4d.com 官方技术博客：blog.uwa4d.com 官方问答社区：answer.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：793972859（原群已满员）
1）Wwise的内存问题 ​2）关于Planar Shadow的疑问 3）PPS的LDR纹理冗余 4）游戏第一次冷启动耗时长 5）Lua解析指令的虚拟机会占用内存吗 这是第268篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题，建议阅读时间10分钟，认真读完必有收获。 UWA 问答社区：answer.uwa4d.com UWA QQ群2：793972859（原群已满员） Audio Q：关于Wwise的内存问题： 1. Wwise加载一个bank，内存增长主要在哪？在profiler上主要体现在哪个参数？ 2. 加载bank之后，开始播放event，那么这个event声音是否有缓存？播放是否卡顿？ A：这个问题取决于你所使用的版本，以及你为资源设定的读取方式，还有你为资源采取的编解码。 版本方面自2019.2开始Wwise采取了动态内存管理，profiler显示的是不同用途的内存占用数据，而在这个大版本之前是需要设置固定大小的初始内存池的。 优化内存分配： https://www.audiokinetic.com/zh/library/edge/?source=SDK&id=goingfurther_optimizingmempools.html 优化内存池： https://www.audiokinetic.com/zh/library/2019.1.11_7296/?source=SDK&id=goingfurther_optimizingmempools.html 你可以比较一下2019.1之后和2019.1的内存管理文档。 你的资源采取读入内存的方式读取，就会进入内存，如果是stream流读取那就会占用io，这些都可以在Wwise的profiler里看，并不是给你隐藏起来了。 https://www.audiokinetic.com/zh/courses/wwise101/?source=wwise101&id=streaming 卡顿取决于你是从内存读取还是流播放，两者可能都会受编解码的影响。通常如果是触发频率高、追求快速响应可以用PCM或者ADPCM来播放，然后Vorbis可能会因为CPU处理时间产生延迟。如果是流播放，则可以通过启用Zero Latency特性，增加prefetch来降低延迟，但这个方法主要是用来处理播放长文件时的延迟。太短的文件如果prefetch太长可能还导致其他报错，短的就通过编解码和采样率来做空间时间平衡就可以了，音质别太差就行。 感谢Icarus@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/613f0d108f8c834241a47107#613f13828f8c834241a47cb3 Addressable Q：目前模型用到了Planar Shadow平面阴影，但是由于我们的模型是要投放到UI上，需要把阴影一起拍下来。但又不能把接收阴影的地板拍下来，求教如何让接收平面阴影的plane只显示阴影，其他地方为透明呢 ？ （相关Shader文件可戳原问答查看。） A1：你并没有理解Planar Shadow的原理，它的阴影本质就是一个Mesh，不需要接收物体，直接删掉地板即可。 如果你的项目没显示出来阴影，那应该是其他方面的原因，跟地板无关。 感谢Jee@UWA问答社区提供了回答 A2：Shader中的_WorldPos表示模型的世界坐标、WorldPos表示ShadowPos吗？如果是这样，这里写的似乎有些问题。 可以参考下这个URP Planar Shadow： https://zhuanlan.zhihu.com/p/266174847 Planar Shadow的实现一般就是在另一个Pass把顶点投到一个“平面”再画一遍，所以应该不会出现题主所描述的问题。 感谢羽飞@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/613735108f8c83424199732a Resources Q：开了PPS之后纹理出现了冗余？LDR之类的资源，这些应该是PPS自己的资源。我们把PPS的资源都打到了一个包里，但还是有冗余。 A：以下是APK解压后的资源。 通常是因为被场景（此场景没有打包AssetBundle）引用和从AssetBundle加载导致的。上图中的LDR开头的纹理是用来做随机用的noise图。在URP中，通常是被RenderPipelineAsset中引用的PostProcessData引用导致的。 当GraphicsSettings中设置了RenderPipelineAsset，那么打包APK的时候，这个PostProcessData.asset是最终被打包进globalmanagers.assets中，没有打包进AssetBundle中的场景如果也引用了RenderPipelineAsset，最终引用的是globalmanagers.assets的RenderPipelineAsset实例，这样是不会冗余的。 但是如果在AssetBundle中也有打包RenderPipelineAsset，比如通过AssetBundle来动态更改RenderPipelineAsset，那么就会产生多份LDR_LLL这样的纹理了。 感谢Xuan@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/6139c96b8f8c8342419d0fdf Loading Q：游戏第一次冷启动的耗时比较长，想分析一下具体的消耗在哪里？但是UWA的测试报告的数据是从进入游戏后开始的，不知道怎样才可以进行测试。 A1：大致归为3点： Resource文件夹下的文件建立索引，可以想办法改用AssetBundle； 预Shader解析，这个为了之后的流程性建议还是先预加载了，当然也可以用一些其他操作陆陆续续加载； 加载的第一个scene比较大，可以考虑搞个空的scene先加载上来，异步加载真正的第一个scene。 感谢萧小俊@UWA问答社区提供了回答 A2：可以看一下这个问答里的思路：https://answer.uwa4d.com/question/5bf764e8f0d8cc224aa7f4f4 感谢OCEAN@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/613ac38b8f8c8342419e7ee7 Memory Q：Lua解析指令的虚拟机会不会占用一部分内存？ A：几K还是有的，但应该很小的，主要还是对象占用的。虚拟机占用的内存主要是堆栈的空间，虚拟机还是得解释代码，执行API之类的，还是要点空间的，但应该是很少的。 感谢OCEAN@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/613aca3c8f8c8342419e8e0a 20210918 更多精彩问题等你回答～ Vulkan API的性能及兼容性 Unity TMP字体方案如何选择 如何实现AAB包的增量更新 封面图来源于：URP Iridescence Example URP中的虹彩着色器示例。 今天的分享就到这里。当然，生有涯而知无涯。在漫漫的开发周期中，您看到的这些问题也许都只是冰山一角，我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入，也许你的方法恰能解别人的燃眉之急；而他山之“石”，也能攻你之“玉”。 官网：www.uwa4d.com 官方技术博客：blog.uwa4d.com 官方问答社区：answer.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：793972859（原群已满员）
1）声音引擎Wwise与Criware的区别 ​2）Unity加载场景闪退问题 3）Animation Transition方案 4）高通GPU Adreno650手机的纹理显示异常问题 5）Live2D平滑渐变方案 这是第253篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题，建议阅读时间10分钟，认真读完必有收获。 UWA 问答社区：answer.uwa4d.com UWA QQ群2：793972859（原群已满员） Audio Q：现在有两个声音引擎可供选择，Wwise与Criware，请问在平时的开发、热更、效率、解耦上对比，有更推荐的使用的吗？还是说声音组擅长哪个就用哪个？ A1：其实音频中间件工具的选项非常多，并不只有这两个，比如《Apex Legends》使用了Rad Miles，《风之旅人和光遇》使用了FMOD，《半条命Alyx》则根本没有使用中间件，用的是自家引擎的音频模块。 Criware的历史相对比较长，跟著名游戏开发商世嘉有比较深的渊源，甚至可以说是曾经包含在世嘉的官方音视频开发工具集当中，鉴于世嘉的影响力，Criware至今在日本本土依然有着比较大的用户基数。 Wwise相对历史短一些，有趣的是其开发公司Audiokinetic于2019年成为了SIE（索尼互动娱乐）的子公司。采用Wwise的项目实际上非常多，而且每年在TGA获奖项目中都能找到约为半数的项目采用Wwise开发，只不过，他们不太强制客户给自己打Logo，造成存在感不是非常高。 随便列举几个使用了Wwise的例子（排名不分先后）：《死亡搁浅》、《生化危机123重制》、《生化危机8》、《怪物猎人Rise》、《对马岛之魂》、《战神》、《崩坏3》、《原神》、《王者荣耀》、《CODM》、《疑案追声》、《彩虹坠入》、《纪念碑谷2》、《Gris》、《Quantum Break》、《Control》、《巫师3》和《赛博朋克2077》等。 应该说，无论从开发的角度还是从音频组的角度，其实使用Wwise都是更好的选择。 开发 行业领先的空间音频工具集，最先支持广义Object Based音频管线的音频工具： https://www.audiokinetic.com/zh/library/edge/?source=SDK&id=spatial_audio.html https://www.audiokinetic.com/zh/products/plug-ins/reflect/ 行业领先的汽车引擎声音设计插件： https://www.audiokinetic.com/zh/products/plug-ins/crankcase-rev/ 强大的创作工具，几乎可实现音频设计师所有的疯狂设想，数量众多的成功用户案例及技术分享（如Wwise Tour，GDC）： https://www.bilibili.com/video/BV1RE411K7nT 官网Q&A及Launcher的Bug reporter，为官方支持的免费问题反馈提交渠道。技术支持与创意技术服务，满足项目的定制化需求。官方维护的Unity/Unreal集成，通过Launcher就可将Wwise SDK集成至您的项目当中，开箱即用。 全平台支持，丰富的音频编解码选项，内容丰富的SDK文档及清晰易学的Help文档及认证教程文档，用户可根据需要调节初始化设置获得更低的音频延迟。 热更 拥有File Packager工具集用于创建PCK管理通过热更发布的内容。Wwise 2021.1版本已支持Unity Addressables。 效率 对设计师而言，具备WAQL（Wwise创作工具查询语言），可在项目规模达到一定程度时方便设计师快速查询需要配置的资源或定位造成问题的资源： https://www.audiokinetic.com/zh/library/edge/?source=SDK&id=waql_introduction.html 强大灵活的声部限制功能与Profiling工具集，方便性能优化与问题排查，从而降低工程师负担，具备丰富的批量处理功能。 对于工程师而言，Wwise具备WAAPI（Wwise创作工具API），可用于创建开发工具，方便设计师快速处理巨量的数据配置，还可以帮助他们对资源进行批量打包以配合项目开发需要： https://www.audiokinetic.com/zh/library/edge/?source=SDK&id=waapi.html Wwise拥有动态内存管理，无需预先申请固定大小的内存池，做到用多少占多少。内存分类系统可以方便用户快速定位与内存消耗相关的问题。 解耦 Wwise API简单易用，事件后行为可由设计师在创作工具端进行设计，工程师无需为此操心，通常情况下，需要帮设计师调用的接口无非是： PostEvent SetState SetSwitch SetRTPCValue Load/Unload bank 更详细文档参见： https://www.audiokinetic.com/zh/library/edge/?source=SDK&id=workingwithsdks_integratingelements.html Wwise提供给了声音设计师极大的创作空间，许多关于空间音频设计的设置都放在了创作工具端，而无需程序介入进行调试，只需要像处理一般的发声体一样处理就好。 设计师也能够通过Profiling工具对性能损耗有清晰的认识，提前定位可能形成性能问题的设计从而采取重构处理。 感谢Icarus@UWA问答社区提供了回答 A2：楼上解答从技术层面确实非常好，我简单说下我的看法。 不论界面是否好看，不好看后期还能改。用任何技术，你都要考虑他的市场，现在Wwise的占有率必然是高的，就比如现在的音频工程师，会Wwise和会Criware哪个更容易被录取。 程序也一样，只接过Wwise和只接过Criware哪个更吃香。通过这个分析，你就知道你应该用什么软件了。 感谢夏霖锐@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/60b49de66bb31032f97919ec Crash Q：最近项目遇到一个很诡异的问题，从一个场景切换到另外一个场景的时候，在安卓64位真机会闪退，通过符号表查出来最终闪退在Unity的il2cpp/vm/liveness.cpp文件中。 在Unity，PC版本均无闪退现象。 引擎版本：Unity 2018.4.10 打包机：MacOS 为了查出闪退原因，做了以下测试： 1.使用Mono打包，无闪退； 2.删除V8类库，完全使用V7类库，无闪退； 3.使用V8库，切换到一个空的场景，闪退。 第3点中无论使用Application.LoadLevel，Applicaiton.LoadLevelAync，SceneManager.LoadScene还是SceneManager.LoadSceneAync都会闪退，这个问题现在已经没什么头绪了，不知道有没有大佬遇到过这个问题呢？ 闪退日志：il2cpp::vm::LivenessState::AddProcessObject(Il2CppObject, il2cpp::vm::LivenessState) 很不幸，升级完Unity以后，崩溃依旧没有解决： 参考网址： https://forum.unity.com/threads/il2cpp-crashes-at-garbagecollectsharedassets-and-loadlevel.316775/page-2 A：发现要升级到Unity 2018.4.35版本，在这个版本中修复了这个Bug： 感谢题主Lim@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/60b5f5e66bb31032f9791a26 Animation Q：请教一个Animation Transition的问题。目前对于一个State A，所有从A出去的过渡线是可以进行优先级排序的。那有没有办法对进入A的所有过渡线也进行优先级排序呢？ 比如对A来说，有AnyState -> A 和B -> A两条线，而且它们的条件一样。那么当条件成立时，有什么办法指定使用上述某一条线呢？ 目前官方文档没有找到对应的方法，论坛里有人说可以在进入B的时候设置一个状态来Disable AnyState，然后离开B的时候再设置回来。不知道有没有更好的方法。谢谢！ A1：“对进入A的所有过渡线也进行优先级排序”这个描述有点问题。根据状态机的定义，不存在当前处于两个State，都要进入State A的情况。 具体描述问题的时候也提到了，关键是存在AnyState这个节点。在Unity的动画状态机方案里，Entry、AnyState和Exit节点是类似于语法糖的东西，生成Runtime资产的时候有一系列节点展开的过程。 回到这个问题上，当处于State B -> State A的时候，并不是“对进入A的过渡线进行优先级排序”，还是“从B出去的过渡线进行优先级排序”。当你在B->A上设定了两条Transition线的时候，是可以做优先级排序的，而AnyState在展开后也会生成B->A的Transition线。但编辑器层面没有公开这层可视化，所以不可调整，并且展开过程不可见，不支持扩展（至少我没找到）。 在必须用AnimatorController和想办法实现功能的前提下： 如果只是想让B->A不走AnyState的Transition，写个Distable AnyState的状态机脚本，声明参数配到AnyState的条件里是最简单的。麻烦一点的就是不使用Anystate->A，活用Sub-Sm减少连线数量。 如果是想让AnyState里的Transition的优先级提高，在B-A上多加一条相同配置的等价Transition调整即可。 感谢陈xx@UWA问答社区提供了回答 A2：语义上AnyState->A 包含B->A（如果条件相同） ，即B->A本来在状态机里是多余的Transition。而当前状态只有一个，满足条件时转入下一个状态，也不存在优先问题。故，应把B->A设定为其它条件，使得B->A不是AnyState->A的子集。 感谢陈浩涛@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/60b8f2a7608bed3338d786ee Rendering Q：目前发现在高通GPU Adreno650设备上，纹理显示模糊，请问有遇到过的大佬有解决的办法吗？（疑似像是Mipmaps的level错误。） Unity版本：2019.4.10LTS， URP版本：7.5.3 图片设置 压缩格式：RGB(A) astc compressed 高通Adreno650： 其他设备: A：很可能是GPU的问题，调下Shader浮点精度试试，我们当时是这么解决的。一些华为手机也会有这种精度问题。 感谢HuangLingSheng@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/60b72cdb6bb31032f9791a2e Rendering Q：Live 2D官方提供的组件CubismRenderController通过设置Opacity可以设置Cubism的透明度，但是过渡很生硬，如下图： 目前的方案是把Cubism通过RT渲染到面板上，通过控制面板透明度来达到平滑过渡效果，请问大家还有什么好的解决方案？ 测试过，不能用缓动曲线函数来设置这个值，中间过程还是会穿帮。 A1：所谓透明度“不平滑过度”，本质原因在于原先的逻辑是身体的每一个小部分单独应用cubism_ModelOpacity之后，再将各个部分进行混合。核心代码如下： 身体是一部分一部分画的： 而题主要的效果是：在渲染出整体结果之后，再对整体应用透明效果。所以， “全身绘制之后，再使用透明度处理一次” 这一做法是必然的。本质是身体各部分绘制完成后，要取到FrameBuffer，然后使用透明度处理一次。因此“通过RT渲染到面板上，再控制面板的透明度的做法是合理”的。 所以题主的问题是，有没有更直接的方法，对这一过程进行优化。我这边还没有想到更好的优化方法，可能Builtin-RenderPipeline没有那么灵活，也或许可以考虑有没有优化的必要。 感谢Prin@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/60b9e8d8608bed3338d78717 封面图来源于网络 今天的分享就到这里。当然，生有涯而知无涯。在漫漫的开发周期中，您看到的这些问题也许都只是冰山一角，我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入，也许你的方法恰能解别人的燃眉之急；而他山之“石”，也能攻你之“玉”。 官网：www.uwa4d.com 官方技术博客：blog.uwa4d.com 官方问答社区：answer.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：793972859（原群已满员）
1）使用SBP后，如何查询Bundle的依赖关系 ​2）大量同样的怪物的骨骼动画如何优化 3）DynamicBone动态骨骼插件是否在项目里使用 4）音频换Wwise的成本 5）运行时如何保存Prefab 这是第225篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题，建议阅读时间10分钟，认真读完必有收获。 UWA 问答社区：answer.uwa4d.com UWA QQ群2：793972859（原群已满员） AssetBundle Q：使用ContentPipeline.BuildAssetBundles接口打出Bundle文件后，没有产生AssetBundleManifest文件，请问该如何查询某个Bundle所依赖的其他Bundle呢？ A：可以从ComatibilityBuildPipeline.cs的源码里面看到下面的代码， 用上图中的方法来生成Manifest对象，然后参考《使用SBP打Bundle如何读取AssetBundleManifest》生成资源文件和打包AssetBundle。 感谢Xuan@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/5f96c3965d9aea5aa971986e Animation Q：大量同样的怪物的骨骼动画应该怎么优化？ 1.如果有N个怪物，是否同样的骨骼动画会计算N遍来得到每个Mesh的新的顶点位置。 2.如果1是对的，能否计算完一个怪物在动画这一帧的顶点位置后，其他怪物都用这个计算好的结果进行渲染。 第二个问题的这个方法我认为是和GPU Instancing的贴图动画是有区别的。不知道第二个方法有没有实现的可能，或者已经有人实现了吗？ 就是这一群模型表现都一样，变换矩阵每一帧都一样。请问Shared SkinnedMesh方案是什么思路，或者有没有参考链接？ A：请参考以下答案： 1.不同模型的动画序列帧不同，骨骼的变换矩阵必然有差异，除非这一群模型表现都一样，可以自己写一套Shared SkinnedMesh的方案。如果需要在CPU端蒙皮把Shader中的蒙皮计算拿出来就好。 Shared SkinnedMesh的方案参考：https://lab.uwa4d.com/lab/5bc6f85504617c5805d4eb0a 2.GPU Instancing的方案也是通过将序列帧骨骼动画烘培到贴图中，然后在顶点着色器中实现蒙皮计算的过程。 具体细节可参考：《移动游戏开发核心技术讲解：3D UI、GPU Skinning和DOTS》中GPUSkinning相关内容。 感谢羽飞@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/5f9ab69edc477370c2c1efa6 Animation Q：DynamicBone动态骨骼插件有人测试过性能吗？是否建议在项目里使用？ A1：这插件的性能真费，项目里主角30根骨骼用上这个插件后，高端机每帧耗时1ms。2个人就是2ms了。 感谢剑影蒙残@UWA问答社区提供了回答 A2：可以用，但是不能大面积用，最好用在重点对象上，适合搞一些骚操作。 感谢与我相关@UWA问答社区提供了回答 A3：完全可以使用。 我们上一个项目中有大量的舞蹈动作，最多支持16人的同台表演。在服装制作上使用了和DynamicBone类似的Unity日本开源的UnityChan中的SpringBone。同时进行了魔改，加入了横向约束防止在抬腿等大幅度动作时的穿模。魔改后的版本计算量比原版更大。 这类弹簧骨骼系统的计算量主要在两方面，一方面是计算弹簧间的力传导，另一方面是计算碰撞，都是重CPU的计算，是非常适合放入子线程去做的。刚好可以应用Unity的ECS来进行优化。 目前网上开源的实现中，可以参考下https://github.com/EsProgram/uSpringBone的实现。但由于ECS目前还未正式Release，所以稳定性不是特别好。 前段时间在逛AssetStore时，发现一个新的插件，使用JobSystem+BurstCompiler，推荐你们去看看。 MagicaCloth 感谢范君@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/5f59e49d9424416784ef21ba Audio Q：我们现在Unity原生的音频，使用的是一个Resonance-Audio插件，可能我们自己也没用好，但能凑合着用。 听说Wwise不错，打算都换上。但昨天下载了一天没安装上，而且和现有音频不能一起用，不知大家是否也遇到过这样的问题，有没有什么好的思路？ 大家在项目中途转Wwise的成本大吗？ A1：我们也转了Wwise，与原生确实不能共用。需要把现有的音频放到Wwise工程之后，再分平台统一导出，Wwise导入导出以及管理还是蛮方便的。安装也是装了两三遍才装完。 Wwise Launcher软件和Unity Intergration似乎是分两次下的，集成入Unity工程需要等那个页面反应一下，否则一不小心又会点到下载。 问题确实在安装，设置，打包，打热更都会有一点。 链接里是我碰到过的几个问题，虽然不是很全，可以参考看看：https://kmageek.com/2020/06/23/wwiseUnity/ 感谢Maggie@UWA问答社区提供了回答 A2：做好音频文件和项目工程的解耦（音频播放配表，不是通过Prefab关联），转过去会很方便，Wwise通过Bank加载、ID播放，做好ID和音频名称的映射。一般会进行二次开发，在Editor做个Bank解析、播放、关联的小标签会方便策划配置。 感谢郑骁@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/5f9843355d9aea5aa97198ad Script Q：运行时代码克隆了一个Prefab，然后我改了坐标或者角度，如何再保存回Prefab？ 我这个保存的数据只是举了简单的例子，实际是运行时美术会改特效特效Particle system里面一堆参数的。 A1：建议使用ScriptableObject来实现相关功能。 不同对象的参数调整要分开考虑。如果是粒子系统，编辑模式下基本都可以调了。大部分参数在编辑模式下调好，非常少量的可以在运行模式下调。调好后拷贝一下，然后退回编辑模式粘贴一下应该也可以了。 当然也可以把一些参数提出到ScriptableObject，调这个序列化，每次运行启动的时候赋值给目标对象。如果有必要也可以做个按钮，运行时按后直接去修改Prefab内的值，只是这样处理起来比较麻烦。不是很大很复杂的需求还是尽量避免。 其他的一些Prefab内编辑器模式下不方便调的，还是建议提出到ScriptableObject来处理更好。 感谢黄程@UWA问答社区提供了回答 A2：PrefabUtility.SaveAsPrefabAssetAndConnect 感谢与我相关@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/5f991f285d9aea5aa97198c5 封面图来源于网络 今天的分享就到这里。当然，生有涯而知无涯。在漫漫的开发周期中，您看到的这些问题也许都只是冰山一角，我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入，也许你的方法恰能解别人的燃眉之急；而他山之“石”，也能攻你之“玉”。 官网：www.uwa4d.com 官方技术博客：blog.uwa4d.com 官方问答社区：answer.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：793972859（原群已满员）
1）如何在Editor中监听Prefab修改后Auto Save的事件 2）关于动画文件的Optimal选项的开启优点 3）一个大地形拆成多个Mesh Collider，对性能有帮助吗 4）Camera.SetReplacementShader和Projector显示问题 5）FMOD在安卓机上插拔耳机声音不会切换 这是第207篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题，建议阅读时间10分钟，认真读完必有收获。 UWA 问答社区：answer.uwa4d.com UWA QQ群2：793972859（原群已满员） Editor Q：如图，在新的Prefab机制中多了Auto Save，没找到监听方法。 原来的Apply是可以监听的，但是如果勾选了Auto Save，就不需要点击Apply，所以会监听不到。求问Auto Save的保存可以监听吗？ A：PrefabStage类负责管理Prefab的编辑操作，并提供了丰富的回调，参考PrefabStage源码可以轻松实现监听各个阶段： #if UNITY_EDITOR using UnityEngine; using UnityEditor; using UnityEditor.Experimental.SceneManagement; [InitializeOnLoad] public static class PrefabStageListener { static PrefabStageListener() { // 打开Prefab编辑界面回调 PrefabStage.prefabStageOpened += OnPrefabStageOpened; // Prefab被保存之前回调 PrefabStage.prefabSaving += OnPrefabSaving; // Prefab被保存之后回调 PrefabStage.prefabSaved += OnPrefabSaved; // 关闭Prefab编辑界面回调 PrefabStage.prefabStageClosing += OnPrefabStageClosing; } //.... } #endif PS：如果经常要查找一些编辑器私有的字段方法，建议下载dnSpy直接打开UnityEditor.dll搜索关键字进行查找，例如想通过代码实现界面按钮的某个功能却找不到API，这时候就可以查看相关DLL里的方法尝试反射调用。 感谢张迪@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/5ee0aecaa45e13073a5710ea Animation Q：项目中的动画文件开启了Optimal选项，会有哪些实质性的优点可以讲一下吗？开启Optimal选项是对项目中的哪一部分起到了作用？ A：Unity的官方文档对这个选项的描述确实并不清楚： Let Unity decide how to compress, either by keyframe reduction or by using dense format. If enabled, the Inspector displays Animation Compression Errors options. Only for Generic and Humanoid Animation Type rigs. 推荐看这篇文章，对于Unity动画文件里的曲线保存方式和相关选项做了细致的描述。总体来说，Optimal就是让Unity自动选择最优的曲线表达方式，从而占用最小的存储空间。 该回答由UWA提供，欢迎大家转至社区交流： https://answer.uwa4d.com/question/5eb905a04d93790618e0ed25 Physics Q：请问下大家的项目里，地形部分是直接用的MeshCollider吗？如果把一个大地形拆成多个MeshCollider性能上会不会好一些？不知道大家有没有这方面的优化经验？ A：按之前的优化经验来看，物理的contacts数（有接触的物体对）对于物理的开销影响会比较大。而计算MeshCollider与其他物体是否有接触的时候，是用的AABB包围盒，所以对于弯曲的、狭长的MeshCollider还是建议做一下拆分，避免其包围盒过大，导致过多的contacts产生。但对于方方正正的地形块来说，是否需要拆分、拆分到什么粒度就不太好判断了，这方面的文档或者经验分享确实不多。 该回答由UWA提供，欢迎大家转至社区交流： https://answer.uwa4d.com/question/5ec4d442979400061e54534a Rendering Q：如图，为了做雪地场景的效果，我使用Camera.SetReplacementShader这个方法来替换场景中的Shader，场景物体和粒子这些都正常的替换了，但是唯独Projector没有正常显示，不知道Projector的处理和其他Shader不一样（也给它写了个替换用的Shader，但是没有效果，也不知道是不是替换成功了，只是被覆盖了）。 为了测试，我做了一个更加简单的场景： 用FrameDebug可以看到它的渲染信息： 然后执行替换逻辑后： 相应的，它的渲染信息如下图： 可以看到好像替换成功了，但是Blend和ZWrite这些不对应，原始Projector Shader中 为Blend SrcAlpha OneMinusSrcAlpha，ZWrite Off，在替换的Shader中也是一样设置，但是在FrameDebug中看到的却是Blend One Zero，ZWrite On。 自己对这一块也不是很了解，希望有前辈可以指点指点，解答一下疑惑，哪里做的不对。附上我的测试脚本（简单点，替换的Projector Shader中就直接复制过去了）。 public class ReplaceShader : MonoBehaviour { public Camera Camera; public Shader replaceShader; bool isOn; private void Update() { if (Input.GetKeyDown(KeyCode.Space)) { ToggleEffect(); } } void ToggleEffect() { isOn = !isOn; if (isOn) { Camera.SetReplacementShader(replaceShader, "RenderType"); } else { Camera.ResetReplacementShader(); } } } 投影Shader： Shader "Custom/ProjectorFX_CharacterRing" { Properties { _MainColor ("Main Color", Color) = (1,1,1,1) _ProjectTex ("Shape", 2D) = "" {} } Subshader { Tags {"Queue"="Transparent+1" "RenderType"="Projector" "ProjectorFX"="Normal"} Pass { ZWrite Off AlphaTest Greater 0 ColorMask RGB Blend SrcAlpha OneMinusSrcAlpha Offset -1, -1 CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_fog #include "UnityCG.cginc" fixed4 _MainColor; fixed4 _MainTex_ST; float4x4 unity_Projector; sampler2D _ProjectTex; struct vInput { float4 vertex : POSITION; }; struct vOutput { float4 uvMain : TEXCOORD0; UNITY_FOG_COORDS(2) float4 pos : SV_POSITION; }; vOutput vert (vInput v) { vOutput o; o.pos = UnityObjectToClipPos(v.vertex); o.uvMain = mul(unity_Projector, v.vertex); UNITY_TRANSFER_FOG(o,o.pos); return o; } fixed4 frag (vOutput i) : SV_Target { fixed4 main = tex2Dproj(_ProjectTex, UNITY_PROJ_COORD(i.uvMain)); main *= _MainColor; UNITY_APPLY_FOG_COLOR(i.fogCoord, main, fixed4(0,0,0,0)); return main; } ENDCG } } } 使用的替换Shader： Shader "Unlit/TestRed" { Subshader { Tags {"Queue"="Transparent+1" "RenderType"="Projector"} Pass { ZWrite Off AlphaTest Greater 0 ColorMask RGB Blend SrcAlpha OneMinusSrcAlpha Offset -1, -1 CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_fog #include "UnityCG.cginc" fixed4 _MainColor; fixed4 _MainTex_ST; float4x4 unity_Projector; sampler2D _ProjectTex; struct vInput { float4 vertex : POSITION; }; struct vOutput { float4 uvMain : TEXCOORD0; UNITY_FOG_COORDS(2) float4 pos : SV_POSITION; }; vOutput vert (vInput v) { vOutput o; o.pos = UnityObjectToClipPos(v.vertex); o.uvMain = mul(unity_Projector, v.vertex); UNITY_TRANSFER_FOG(o,o.pos); return o; } fixed4 frag (vOutput i) : SV_Target { fixed4 main = tex2Dproj(_ProjectTex, UNITY_PROJ_COORD(i.uvMain)); main *= _MainColor; UNITY_APPLY_FOG_COLOR(i.fogCoord, main, fixed4(0,0,0,0)); return main; } ENDCG } } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag // make fog work #pragma multi_compile_fog #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; UNITY_FOG_COORDS(1) float4 vertex : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); UNITY_TRANSFER_FOG(o,o.vertex); return o; } fixed4 frag (v2f i) : SV_Target { // sample the texture fixed4 col = fixed4(1,0,0,1); // apply fog UNITY_APPLY_FOG(i.fogCoord, col); return col; } ENDCG } } } A：从题主的截图中可以看到，在没有使用Replace Shader的时候，Plane渲染了两次，一次是自身Shader，一次是Projector Shader。在使用了Replace Shader之后，FrameDebugger里面看到Plane只渲染了一次，应该是Projector没有生效。因为只有Opaque的那个SubShader起作用了，所以FrameDebugger里面看到的是“ Blend One Zero , ZWrite On”，这个是正常的，只是跑到半透明渲染队列里面去了，这个不太正常。我尝试把题主的Replace Shader里面的两个SubShader调换了一下顺序，结果又跑到不透明渲染队列里面去了。 在Google里面搜索了一下，发现Unity论坛里面有2个帖子跟题主的问题比较相关，这个帖子里有提到说Unity 5.6.4使用了Replace Shader就不支持Projector。 这个帖子说是因为之前有个Bug，Replace Shader会使受到Projector影响的物体即使Tag不匹配也进行渲染，所以Unity修Bug把Projector改成使用了Replace Shader就不起作用了。 感谢Xuan@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/5ed7c18e96525d41c9512b2e Audio Q：我们用FMOD播放的音乐和音效，在安卓设备上，手机功放BGM，这个时候插入耳机，手机依然在功放，并不能切换到耳机播放，另外切换到蓝牙也是同样的问题，有没有人遇到过，苹果设备是没问题的。 A：在RuntimeManager.cs的Initialize()方法里把Android平台的OutputType改成FMOD.OUTPUTTYPE.AUDIOTRACK就可以了。 感谢题主徐莲一蒙@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/5ed7730096525d41c9512b1f 封面图来源于网络 今天的分享就到这里。当然，生有涯而知无涯。在漫漫的开发周期中，您看到的这些问题也许都只是冰山一角，我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入，也许你的方法恰能解别人的燃眉之急；而他山之“石”，也能攻你之“玉”。 官网：www.uwa4d.com 官方技术博客：blog.uwa4d.com 官方问答社区：answer.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：793972859（原群已满员）