【厚积薄发】如何在Editor中监听Prefab修改后Auto Save的事件

侑虎科技

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(原群已满员)

阅读 610
16 声望
4 粉丝
0 条评论
你知道吗?

16 声望
4 粉丝
宣传栏