我创建了自己的组件,它与一些Unity内置组件(如Rigidbody与Rigidbody2D冲突)冲突。所以我需要确保这些组件不会在同一个GameObject中一起存在。有办法吗?似乎很容易检查添加我自己的组件的时间(Reset),但是如果添加Unity的内置组件该怎么办?当新组件附加到GameObject时会发送一些回调,消息或事件吗?
精密工业
我不需要在编辑器中隐藏它,也不需要添加我自己的组件。我问的是在我的组件被附加时阻止添加某些Unity'内置组件。从编辑器GUI(通过“add component”按钮)和Unity API(通过GameObject.AddComponent)。
答案 0 :(得分:1)
有[DisallowMultipleComponent]
属性可防止将同一类型的两个类型添加到同一个游戏对象中。这也适用于子类型(这是Rigidbody和Rigidbody2d的处理方式)。
我不确定这是否适合您,因为您没有说过您的组件彼此相关,但这是我能找到的。
答案 1 :(得分:1)
新组件时是否发送了一些回调,消息或事件 附加到GameObject?
没有。
有办法吗?
是的,但有点复杂。
如果您想阻止添加自定义脚本,那很容易,this问题应该处理。
这很复杂,因为您希望阻止将另一个人(内置)编写的组件添加到GameObject
,这意味着您首先需要一种方法来检测何时将该组件添加到GameObject
然后摧毁它。 必须每帧完成(在编辑器和运行时都有)。
您可以将您不想添加的组件调用GameObject
列入黑名单的组件。
以下是步骤:
1 。将列入黑名单的组件存储在一个数组中。
private static Type[] blacklistedComponents =
{
typeof(Rigidbody),
typeof(Rigidbody2D)
//...
};
2 。获取场景中的根GameObjects
并将其存储在列表中。
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 。想知道为什么不使用FindObjectsOfType
和FindObjectsOfTypeAll
?
A 1 。这些函数通常用于简化场景中的所有内容,但问题是它们返回数组。每帧调用这些函数会破坏游戏性能,因为它会分配内存并导致垃圾收集器更频繁地运行。
这就是使用Scene.GetRootGameObjects
的原因,您可以在其中传递List
并填写列表。它不会返回数组。
问题2 。为什么您将列表传递给GetComponentsInChildren
而不从中返回结果?
A 2 。技术上与我上面解释的原因相同。我使用了一个不分配内存的GetComponentsInChildren
函数版本。只需将List
传递给它,它就会填满它找到的每个组件。这可以防止它返回昂贵的阵列。
我为此编写了一个完整的工作代码,但您需要对其进行改进。这就是我解释每个过程的原因,以便您可以自己改进或重写它。它目前阻止从编辑器或编辑器或构建中的代码添加Rigidbody
和Rigidbody2D
。您可以添加更多要阻止的组件到blacklistedComponents
变量。它在运行时也在编辑器中运行。 UNITY_EDITOR
用于删除编辑器代码,并确保它为平台编译。
1 。创建一个名为ComponentDetector
的脚本,并将下面的每个代码复制到其中。
2 。保存并返回编辑器。就是这样。您不必将其附加到任何对象。 您永远不能将Rigidbody
和Rigidbody2D
添加到任何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
答案 2 :(得分:0)
如果您尝试在运行时之前确定组件是否存在(我假设您已经知道),则可以使用Start()
方法。但是,就像我说的那样,我假设你已经知道了,所以在运行时检查类似内容的唯一其他方法是在每个帧的Update()
方法中不断检查它。虽然,我不确定为什么在运行时可能会将Unity组件添加到游戏对象中。如果这是一个问题,可能会将相关组件添加到子游戏或父游戏对象中吗?
如果你真的想做一些改变,这可能需要对你的项目进行大量的重构,你总是可以创建一个ComponentManager
类来处理向GameObjects添加和删除组件并创建你自己的回调。