在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日志的最后三行的方式如下所示:它将两个组件作为一个PopupOpener
input: Button
和PopupOpener
。 PopupOpener
要求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对象 – 例如,将其渲染为纹理,用几乎透明的面板覆盖它,这将拦截点击或禁用 (而不是销毁)从MonoBehaviour
或IPointerClickHandler
派生的所有Component
。
除了删除克隆上的组件,您可以通过设置.enabled = false
来禁用它们。 这不会触发依赖性检查,因为不需要启用组件来实现依赖关系。
Update
方法。