问答 如何在 Editor 中监听 Prefab 修改后 Auto Save 的事件

侑虎科技 · 2020年06月17日 · 1035 次阅读

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问答社区提供了回答。


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 提供。


Physics

Q:请问下大家的项目里,地形部分是直接用的 MeshCollider 吗?如果把一个大地形拆成多个 MeshCollider 性能上会不会好一些?不知道大家有没有这方面的优化经验?

A:按之前的优化经验来看,物理的 contacts 数(有接触的物体对)对于物理的开销影响会比较大。而计算 MeshCollider 与其他物体是否有接触的时候,是用的 AABB 包围盒,所以对于弯曲的、狭长的 MeshCollider 还是建议做一下拆分,避免其包围盒过大,导致过多的 contacts 产生。但对于方方正正的地形块来说,是否需要拆分、拆分到什么粒度就不太好判断了,这方面的文档或者经验分享确实不多。

该回答由 UWA 提供。


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 问答社区提供了回答。


Audio

Q:我们用 FMOD 播放的音乐和音效,在安卓设备上,手机功放 BGM,这个时候插入耳机,手机依然在功放,并不能切换到耳机播放,另外切换到蓝牙也是同样的问题,有没有人遇到过,苹果设备是没问题的。

A:在 RuntimeManager.cs 的 Initialize() 方法里把 Android 平台的 OutputType 改成 FMOD.OUTPUTTYPE.AUDIOTRACK 就可以了。

感谢题主徐莲一蒙@UWA问答社区提供了回答。

封面图来源于网络

今天的分享就到这里。当然,生有涯而知无涯。在漫漫的开发周期中,您看到的这些问题也许都只是冰山一角,我们早已在 UWA 问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入,也许你的方法恰能解别人的燃眉之急;而他山之 “石”,也能攻你之 “玉”。

官网:www.uwa4d.com
官方技术博客:blog.uwa4d.com
官方问答社区:answer.uwa4d.com
UWA 学堂:edu.uwa4d.com
官方技术 QQ 群:793972859(原群已满员)

暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册