问题描述
如果我必须总结我的问题,我会说我想创建一个类似于 Unity 中的 requiredComponent
属性的属性。
[AttributeUsage(AttributeTargets.Class)]
public class ContainsDataToBeSaved : PropertyAttribute
{
}
我还有另一个组件。
[ContainsDataToBeSaved]
public class Tester : MonoBehavIoUr
{
}
这个 Tester
组件附加到一个游戏对象。我想在 SaveLoadComponent
属性附加到游戏对象时自动附加另一个名为 ContainsDatetoBeSaved
的组件。这不能在运行时发生,它必须在编辑器中发生。
我可以通过在测试脚本的顶部添加 [requiredComponent(typeof(SaveLoadComponent))]
轻松实现这一点,但这不是我想要做的。
所以我会回答一些问题。为什么不使用 RequireComponent 属性?我正在尝试制作一个易于使用的保存系统。我的目标是,当您附加属性 ContainsDataToBeSaved 并使用我创建的另一个自定义属性标记任何字段时,它会自动保存该字段并在运行时加载它。这个保存系统确实依赖于另一个组件,即 SaveLoadComponent。我发现每当有人使用保存系统时,他们都会忘记添加组件。如果该属性可以添加组件,则会更容易。 我的目标是使这个系统易于使用,并且不允许出现“人为错误”。我探索了其他途径,例如创建一个基类来处理 SaveLoadComponent 所做的一切。但我敢肯定,有些开发人员可能会忘记从该类继承。如果开发人员忘记将组件附加到游戏对象,他们也可以忘记添加 RequireComponent 属性。目标是可以附加属性,其余的将被处理。
解决方法
正如评论中所说,首先 PropertyAttribute
顾名思义不应该用在 class
上,而只能用在其中的字段和属性上。
您应该只从 Attribute
继承,然后通常将后缀 Attribute
添加到名称中,如
[AttributeUsage(AttributeTargets.Class)]
public class ContainsDataToBeSavedAttribute : Attribute { }
然后进一步提到,对于属性,很清楚何时检查和使用属性(通常用于检查器中的装饰器抽屉或用于 De/Serialization),但不清楚您要检查的确切时刻并应用类的属性。
我认为最接近您想要实现的目标是以下内容,但是,它需要覆盖从 MonoBehaviour
继承的任何内容的检查器
[CustomEditor(typeof(MonoBehaviour))]
public class ContainsDataToBeSavedEditor : Editor
{
MonoBehaviour _monoBehaviour;
Type _type;
Type _requireType = typeof(ContainsDataToBeSaved);
// Called when this instance Inspector is loaded
private void OnEnable()
{
_monoBehaviour = (MonoBehaviour) target;
_type = target.GetType();
CheckAndApplyAttribute();
}
// To check continously
// NOTE: as this is quite expensive I would not do this
//public override void OnInspectorGUI ()
//{
// DrawDefaultInspector();
//
// CheckAndApplyAttribute();
//}
private void CheckAndApplyAttribute()
{
// Try to get the attribute on given target
if (Attribute.IsDefined(_type,_requireType))
{
// So the attribute is present on the given target class
// Check if your component is attached
if(!(_monoBehaviour.TryGetComponent<SaveLoadComponent>(out _))
{
// Otherwise add it vis the Undo class enabling marking as dirty
// and thereby saving and correct handling of Undo/Redo
Undo.AddComponent<SaveLoadComponent>(_monoBehaviour.gameObject);
}
}
}
}
因为这非常昂贵,所以你应该避免这样做,例如在 OnInspectorGUI
或类似的东西中,每个事件在检查器中被多次调用。
请注意,这仍然不能完全复制 RequireComponent
的行为,因为后者还确保您无法删除相应的依赖项。
所以问题仍然存在:为什么不简单地使用 RequireComponent
?
注意:在智能手机上打字,但我希望思路清晰