使用脚本在 Unity3D Editor 中完全删除 Prefab 中的 ParticleSystem 组件

今天碰到一个小问题需要通过脚本来批量将我们工程中的所有例子特效 Prefab 根节点的粒子发射器给干掉,那么好吧,开始动手吧。

ParticleSystem ps = go.GetComponent<ParticleSystem> ();
if (ps != null) {
    if (ps.renderer.sharedMaterial == null) {
        Debug.Log (string.Format ("{0} Render Has no Material", go.name));
    } else {
        var matName = ps.renderer.sharedMaterial.name;
        if (matName.StartsWith ("Default-Particle")) {
            Debug.Log (string.Format ("Prefab: {0}, Mat: {1}", go.name, matName));
            DestroyImmediate (ps, true);
        }
    }
}

执行一下,发现 ParticleSystem 组件确实木有了,但是留了一个小尾巴,就是我们在 Unity3D Editor 中查看被处理的 Prefab 时,还能在 Inspector 中看到一个 Shader 的小尾巴,如图:

default_particle_shader_tail

这个让我情何以堪啊,你知道的,我有轻微强迫症的,虽然貌似并不会影响程序的功能,但是我要是不知道原因,我岂不是很不爽,好吧,那么就对比一下手动将 ParticleSystem 组件 Remove 和通过我们这个脚本 Destroy 之后的 Prefab 文件之间究竟有甚么区别呢,这个时候就需要借助一下 Unity3D 提供的 binary2text 命令行工具了,Mac OSX 上这个命令还在这个位置 [Unity3D 安装目录]/Unity.app/Contents/Tools/binary2text,通常就是/Applications/Unity/Unity.app/Contents/Tools/binary2text。使用这个工具将两个 Prefab 文件转成 Text 文件之后我们就可以使用文件对比工具来进行对比了(对比二进制神马的,真的让人很蛋疼啊,幸好 Unity3D 还提供了这么个鬼啊),对比就会发现,这个 Shader 是因为啥而存在的了。

compare_1 compare_2

左侧有红色文本显示的是通过脚本 Destroy ParticleSystem 组件之后生成的 Prefab 的 Text 文件,右侧的是手动通过右键 Remove Component 删除 ParticleSystem 组件之后生成的 Prefab 的 Text 文件,对比之下我们能发现左侧文件中说明了一些问题:

  • 第一处红色文本表示多了一个内部资源的引用,看起来很像是 Shader;
  • 第二处红色文本更是直接说明了问题,左侧表示有两个组件,组件的 Id 是 19935810,刚好与第三处红色文本描述对应;
  • 第三处红色文本描述的是一个 ParticleSystemRender 组件的信息。

这么一看就知道原来还有一个隐藏的 ParticleSystemRender 组件没有被移除掉,因为这个 Render 是依附于 ParticleSystem 才存在的,所以不会在 Inspector 中单独显示,而是作为 ParticleSystem 的子组件 Render 进行显示的。那么我们就再修改一下我们的代码就好了。

ParticleSystem ps = go.GetComponent<ParticleSystem> ();
if (ps != null) {
    if (ps.renderer.sharedMaterial == null) {
        Debug.Log (string.Format ("{0} Render Has no Material", go.name));
    } else {
        var matName = ps.renderer.sharedMaterial.name;
        if (matName.StartsWith ("Default-Particle")) {
            Debug.Log (string.Format ("Prefab: {0}, Mat: {1}", go.name, matName));
            DestroyImmediate (ps, true);
        }
    }
}

ParticleSystemRenderer psr = go.GetComponent<ParticleSystemRenderer> ();
if (psr != null) {
    if (psr.sharedMaterial == null) {
        Debug.Log (string.Format ("{0} Render Has no Material", go.name));
        DestroyImmediate (psr, true);
    } else {
        var matName = psr.sharedMaterial.name;
        if (matName.StartsWith ("Default-Particle")) {
            Debug.Log (string.Format ("Prefab: {0}, Mat: {1}", go.name, matName));
            DestroyImmediate (psr, true);
        }
    }
}

也就是需要独立地将这个 ParticleSystemRender 组件也给 DestroyImmediate 一下就好了。搞定,洗洗睡了。