检查一个游戏对象的组件是否可以被销毁

在Unity开发一个游戏,我使用[RequireComponent(typeof(%ComponentType%))]来确保组件满足所有的依赖关系。

我现在正在实现一个教程系统,其中突出显示各种UI对象。 为了突出显示,我正在引用场景中的GameObject ,然后使用Instantiate()克隆它,然后recursion地去除所有不需要显示的组件。 问题在于,在这些组件中自由使用RequireComponent ,许多不能以任何顺序被简单地移除,因为它们依赖于尚未被删除的其他组件。

我的问题是:有没有一种方法来确定Component是否可以从它当前连接到的GameObject移除。

我发布的代码在技术上是可行的,但是它会抛出Unity错误(没有在try-catch块中捕获,如图所示

在这里输入图像描述

代码如下:

 public void StripFunctionality(RectTransform rt) { if (rt == null) { return; } int componentCount = rt.gameObject.GetComponents<Component>().Length; int safety = 10; int allowedComponents = 0; while (componentCount > allowedComponents && safety > 0) { Debug.Log("ITERATION ON "+rt.gameObject.name); safety--; allowedComponents = 0; foreach (Component c in rt.gameObject.GetComponents<Component>()) { //Disable clicking on graphics if (c is Graphic) { ((Graphic)c).raycastTarget = false; } //Remove components we don't want if ( !(c is Image) && !(c is TextMeshProUGUI) && !(c is RectTransform) && !(c is CanvasRenderer) ) { try { DestroyImmediate(c); } catch { //NoOp } } else { allowedComponents++; } } componentCount = rt.gameObject.GetComponents<Component>().Length; } //Recursive call to children foreach (RectTransform childRT in rt) { StripFunctionality(childRT); } } 

所以这段代码生成上面的debugging日志的最后三行的方式如下所示:它将两个组件作为一个PopupOpenerinput: ButtonPopupOpenerPopupOpener要求Button组件在同一个GameObject中存在:

 [RequireComponent(typeof(Button))] public class PopupOpener : MonoBehaviour { ... } 

while循环的第一次迭代(由文本“ITERATION ON Button Invite(clone)”表示)试图首先删除Button ,但不能像PopupOpener组件所依赖的PopupOpener 。 这抛出了一个错误。 然后它试图删除它所做的PopupOpener组件,因为它不依赖于其他任何东西。 在下一个迭代中(由第二个文本“ITERATION ON Button Invite(clone)”表示),它试图删除剩余的Button组件,现在它可以做,因为PopupOpener在第一次迭代中被删除(在错误之后)。

所以我的问题是,是否可以提前检查指定的组件是否可以从当前的GameObject移除,而不用调用Destroy()或者DestroyImmediate() 。 谢谢。

这肯定是可能的,但是它需要相当多的工作:你可以检查是否有一个附加到RequireComponent ,它有一个m_Type#types的Attribute ,它的m_Type#字段之一可以指定给你将要使用的组件types去掉。 全部用一种扩展方法包装:

 static class GameObjectExtensions { private static bool Requires(Type obj, Type requirement) { //also check for m_Type1 and m_Type2 if required return Attribute.IsDefined(obj, typeof(RequireComponent)) && Attribute.GetCustomAttributes(obj, typeof(RequireComponent)).OfType<RequireComponent>() .Any(rc => rc.m_Type0.IsAssignableFrom(requirement)); } internal static bool CanDestroy(this GameObject go, Type t) { return !go.GetComponents<Component>().Any(c => Requires(c.GetType(), t)); } } 

在将GameObjectExtensions放置到项目中GameObjectExtensions任何地方(或者将其作为脚本的常规方法)之后,可以检查是否可以移除组件,例如:

 rt.gameObject.CanDestroy(c.getType()); 

然而,还可以有其他方法来制作非交互式GUI对象 – 例如,将其渲染为纹理,用几乎透明的面板覆盖它,这将拦截点击或禁用 (而不是销毁)从MonoBehaviourIPointerClickHandler派生的所有Component

除了删除克隆上的组件,您可以通过设置.enabled = false来禁用它们。 这不会触发依赖性检查,因为不需要启用组件来实现依赖关系。

  • 当行为被禁用时,它仍然在对象上,你甚至可以改变它的属性并调用它的方法,但引擎将不再调用它的Update方法。
  • 当渲染器被禁用时,对象变为不可见。
  • 当碰撞器被禁用时,不会引起碰撞事件的发生。
  • 当UI组件被禁用时,玩家不能再与它交互,但是它仍然会被渲染,除非你也停用渲染器。