Unity3D防止添加某些内置组件

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity3D防止添加某些内置组件相关的知识,希望对你有一定的参考价值。

我创建了自己的组件,它与一些Unity内置组件(如Rigidbody与Rigidbody2D冲突)冲突。所以我需要确保这些组件不会在同一个GameObject中一起存在。有办法吗?似乎很容易检查我自己的组件何时被添加(通过Reset),但是如果添加了Unity的内置组件该怎么办?当新组件附加到GameObject时是否发送了一些回调,消息或事件?

精度

我不需要在编辑器中隐藏它的组件,或者阻止添加我自己的组件。我问的是在我的组件连接时阻止添加某些Unity的内置组件。来自Editor GUI(通过“add component”按钮)和Unity API(通过GameObject.AddComponent)。

答案

当新组件附加到GameObject时是否发送了一些回调,消息或事件?

没有。

有办法吗?

是的,但有点复杂。

如果你想阻止你的自定义脚本被添加,这本来很容易,this问题应该处理。

这很复杂,因为您希望防止由另一个人(内置)编写的组件被添加到GameObject,这意味着您首先需要一种方法来检测该组件何时被添加到GameObject然后销毁它。这必须在每一帧(编辑器和运行时)中完成。

您可以调用不希望添加到GameObject黑名单组件的组件。

以下是步骤:

1.将列入黑名单的组件存储在一个阵列中。

private static Type[] blacklistedComponents =
{
    typeof(Rigidbody),
    typeof(Rigidbody2D)
    //...
};

2.在场景中获取根GameObjects并将它们存储在List中。

private static List<GameObject> rootGameObjects = new List<GameObject>();
Scene.GetRootGameObjects(rootGameObjects);

3.通过每个根GameObject并使用GetComponentsInChildren获得所有组件附加到根GameObject下的每个GameObject

private static List<Component> allComponents = new List<Component>();
currentLoopRoot.GetComponentsInChildren<Component>(true, allComponents);

4.在#3循环期间,遍历检索到的组件并检查它是否有任何列入黑名单的组件。如果是,请销毁该列入黑名单的组件。

for (int i = 0; i < allComponents.Count; i++)
{
    //Loop through each blacklisted Component and see if it is present
    for (int j = 0; j < blacklistedComponents.Length; j++)
    {
        if (allComponents[i].GetType() == blacklistedComponents[j])
        {
            Debug.Log("Found Blacklisted Component: " + targetComponents[i].GetType().Name);
            Debug.Log("Removing Blacklisted Component");
            //Destroy Component
            DestroyImmediate(allComponents[i]);

            Debug.LogWarning("This component is now destroyed");
        }
    }
}

而已。您或其他人可能对此答案几乎没有疑问。

问1.为什么不使用FindObjectsOfTypeFindObjectsOfTypeAll

这些函数通常用于简化场景中的所有内容,但问题是它们返回数组。每帧调用这些函数会破坏游戏性能,因为它会分配内存并导致垃圾收集器更频繁地运行。

这就是使用Scene.GetRootGameObjects的原因,你可以在里面传递一个List,它将为你填写清单。它不返回数组。


问2.你为什么把List传给GetComponentsInChildren并且没有从中返回结果?

A 2.技术上与我上面解释的原因相同。我使用了一个不分配内存的GetComponentsInChildren函数版本。只需将List传递给它,它将填满它找到的每个组件。这可以防止它返回昂贵的阵列。


我在下面写了一个完整的工作代码,但你需要改进它。这就是我解释每个过程的原因,以便您可以自己改进或重写它。它目前阻止从编辑器或编辑器或构建中的代码添加RigidbodyRigidbody2D。您可以添加更多要阻止的组件到blacklistedComponents变量。它在运行时也在编辑器中运行。 UNITY_EDITOR用于删除编辑器代码并确保它为平台编译。

1.创建一个名为ComponentDetector的脚本,并将下面的每个代码复制到其中。

2.Save并回到编辑。而已。您不必将其附加到任何对象。您永远不能将RigidbodyRigidbody2D添加到任何GameObject。

using System.Collections.Generic;
using UnityEngine;
using System;
using UnityEngine.SceneManagement;

#if UNITY_EDITOR
using UnityEditor;
#endif

public class ComponentDetector : MonoBehaviour
{
    //Add the blacklisted Components here
    private static Type[] blacklistedComponents =
        {
        typeof(Rigidbody),
        typeof(Rigidbody2D)
        //...
    };

    private static List<Component> allComponents = new List<Component>();
    private static List<GameObject> rootGameObjects = new List<GameObject>();

    private static void GetAllRootObject()
    {
        Scene activeScene = SceneManager.GetActiveScene();
        activeScene.GetRootGameObjects(rootGameObjects);
    }

    private static void GetAllComponentsAndCheckIfBlacklisted()
    {
        for (int i = 0; i < rootGameObjects.Count; ++i)
        {
            GameObject obj = rootGameObjects[i];
            //Debug.Log(obj.name);

            //Get all child components attached to this GameObject
            obj.GetComponentsInChildren<Component>(true, allComponents);

            //Remove component if present in the blacklist array
            RemoveComponentIfBlacklisted(allComponents, blacklistedComponents);
        }


    }

    private static void RemoveComponentIfBlacklisted(List<Component> targetComponents, Type[] blacklistedList)
    {
        //Loop through each target Component
        for (int i = 0; i < targetComponents.Count; i++)
        {
            //Debug.Log(targetComponents[i].GetType());
            //Loop through each blacklisted Component and see if it is present
            for (int j = 0; j < blacklistedList.Length; j++)
            {
                if (targetComponents[i].GetType() == blacklistedList[j])
                {
                    Debug.Log("Found Blacklisted Component: " + targetComponents[i].GetType().Name);
                    Debug.LogError("You are not allowed to add the " + targetComponents[i].GetType().Name + " component to a GameObject");
                    Debug.Log("Removing Blacklisted Component");
                    //Destroy Component
                    DestroyImmediate(targetComponents[i]);

                    Debug.LogWarning("This component is now destroyed");
                }
            }
        }
    }

    public static void SearchAndRemoveblacklistedComponents()
    {
        //Get all root GameObjects
        GetAllRootObject();

        //Get all child components attached to each GameObject and remove them
        GetAllComponentsAndCheckIfBlacklisted();
    }

    void Awake()
    {
        DontDestroyOnLoad(this.gameObject);
    }

    // Update is called once per frame
    void Update()
    {
        //Debug.Log("Update: Run-time");
        SearchAndRemoveblacklistedComponents();
    }
}

#if UNITY_EDITOR
[InitializeOnLoad]
class ComponentDetectorEditor
{
    static ComponentDetectorEditor()
    {
        createComponentDetector();
        EditorApplication.update += Update;
    }

    static void Update()
    {
        //Debug.Log("Update: Editor");
        ComponentDetector.SearchAndRemoveblacklistedComponents();
    }

    static void createComponentDetector()
    {
        GameObject obj = GameObject.Find("___CDetector___");
        if (obj == null)
        {
            obj = new GameObject("___CDetector___");
        }

        //Hide from the Editor
        obj.hideFlags = HideFlags.HideInHierarchy;
        obj.hideFlags = HideFlags.HideInInspector;

        ComponentDetector cd = obj.GetComponent<ComponentDetector>();
        if (cd == null)
        {
            cd = obj.AddComponent<ComponentDetector>();
        }

    }
}
#endif
另一答案

[DisallowMultipleComponent]属性可以防止两个相同类型被添加到同一个游戏对象中。这也适用于子类型(这是Rigidbody和Rigidbody2d的处理方式)。

我不确定这是否适合你,因为你没有说你的组件彼此相关,但这是我能找到的。

另一答案

如果您在运行时尝试确定组件是否存在(我假设您已经知道),则可以使用Start()方法。但是,就像我说的那样,我假设你已经知道了,所以在运行时检查这样的东西的唯一其他方法是在Update()方法中每一帧不断检查它。虽然,我不确定为什么在运行时可能会将Unity组件添加到游戏对象中。如果这是一个问题,可能会将相关组件添加到子游戏或父游戏对象中吗?

如果你真的想做一些改变,这可能需要对你的项目进行大量的重构,你总是可以创建一个ComponentManager类来处理向GameObjects添加和删除组件并创建自己的回调。

以上是关于Unity3D防止添加某些内置组件的主要内容,如果未能解决你的问题,请参考以下文章

导航组件防止在后按时重新创建片段

使用 Unity3D 进行 Ninject

Unity3D 在自定义脚本中实现Button组件上的OnClick面板

如何防止 ReSharper 在生成代码时为内置类型添加“@”符号?

使用导航从一个片段导航到另一个片段后,防止后按工作

Unity3D代码加密防止反编译及资源加密