用于在由代码创建的组件上设置必填字段的选项? [英] Options for setting mandatory fields on components created by code?

查看:214
本文介绍了用于在由代码创建的组件上设置必填字段的选项?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我创建如下所示的GameObject时,我希望我的组件具有/设置必填字段.

When I create a GameObject like below I want my component to have/set mandatory fields.

GameObject go = new GameObject("MyGameObject");
MyComponent myComponent = go.addComponent(MyComponent);

由于我不能使用构造函数来设置私有字段,因此我需要为这些字段设置setter或设置多个必需属性的公共Init函数.但是,这允许在代码中的任何位置更改值,并且与具有重载的构造函数不同,在创建这些参数后Init需要"或可以被调用"这一点并不明显.

Since I cannot use a constructor to set private fields I either need a setter for these fields or a public Init function that sets multiple mandatory properties. But this allows changing the values anywhere in the code and unlike a constructor with overloads it's not obvious that Init "needs to" or "can be" called after creation to set these parameters.

那么还有其他选项可以让我在创建组件时设置必填字段并使用正确"封装吗?

So are there other options that allow me to set mandatory fields when creating a Component and use "proper" encapsulation?

推荐答案

正如注释中所述,Unity为MonoBehaviour初始化逻辑实现了2种主要方法:醒着.

Just as stated in the comments, Unity implemented 2 main methods for MonoBehaviour initialization logic: Start and Awake.

这两种初始化方法都有缺点:

There are drawbacks of these 2 initialization methods:

  • 他们不能接受任何参数
  • 您不确定他们的执行顺序

有一个标准的执行顺序,因此您可以确定开始在唤醒后执行.但是,如果您有多个相同类型的MonoBehaviours,那么跟踪哪个首先执行是不容易的.

There's a standard order of execution and from this you can be sure that Start is executed after Awake. But if you have multiple MonoBehaviours of the same type then it's not easy to track which executes first.

如果您的问题只是执行顺序,并且您有不同的类型,则可以只更新

If your issue is just the execution order and you have have different types then you could just update the script execution order.

否则,还有其他方法可以通过在单行为内部使用工厂方法"来避免这两个缺点.

Otherwise there are further approaches to avoid both drawbacks by using a Factory Method inside the monobehaviour.

请考虑在这种情况下执行顺序为:
唤醒 => OnEnable =>重置=> 开始 => 您的初始化方法

Consider that in this case the order of execution will be:
Awake => OnEnable => Reset => Start => YOUR INIT METHOD

public class YourMono : MonoBehaviour
{
    //a factory method with the related gameobject and the init parameters
    public static YourMono AddWithInit(GameObject target, int yourInt, bool yourBool)
    {
        var yourMono = target.AddComponent<YourMono>();
        yourMono.InitMonoBehaviour(yourInt, yourBool);
        return yourMono;
    }

    private void InitMonoBehaviour(int yourInt, bool yourBool)
    {
        //init here
    }
}

此方法提供了更好的封装,因为我们可以确保只将 InitMonoBehaviour 调用一次.

This method offers the better encapsulation since we can be sure that InitMonoBehaviour wioll be called just once.

您还可以通过扩展名创建工厂.在这种情况下,您从类中删除了工厂方法,将工厂逻辑与游戏逻辑分离可能会很有用.

You can also create a factory from an extension. In this case you remove the factory method from the class, it might be useful to decouple factory logic from gameplay logic.

但是在这种情况下,您将需要在内部使用 InitMonoBehaviour 并在同一名称空间中实现扩展.
这样一来,InitMonoBehaviour将更易于访问(内部可以从同一名称空间访问),因此封装性较低.

But in this case you will need to make InitMonoBehaviour internal and implement the extension in the same namespace.
As a result InitMonoBehaviour will be a bit more accessible (internally from the same namespace) so it has lower encapsulation.

    [Serializable]
    public class YourData
    {
    }

    namespace YourMonoNameSpace.MonoBehaviourFactory
    {
        public static class MonoFactory
        {
            public static YourMono AddYourMono(this GameObject targetObject, YourData initData)
            {
                var yourMono = targetObject.AddComponent<YourMono>();
                yourMono.InitMonoBehaviour(initData);
                return yourMono;
            }
        }
    }

    namespace YourMonoNameSpace
    {
        public class YourMono : MonoBehaviour
        {
            private YourData _componentData= null;
            internal void InitMonoBehaviour(YourData initData)
            {
                if(_componentData !=  null ) return;
                _componentData = initData;
            }
        }
    }

这表示这两个选项也都有缺点:

That said both of these options has also a drawback:

  • 有人可能会忘记启动它们.

因此,如果您希望这种方法是必需的"而不是可选的,那么我建议添加一个布尔标志_isInitiated,以确保没有人忘记实现它.

So, if you want this method to be "required" and not optional, I suggest to add a bool flag _isInitiated to make sure no one forgot to implement it.

我个人会使用脚本对象 Singleton ,我会在清醒时调用它以确保初始化始终被调用.

Personally I would implement the logic with an Scriptable Object, or with a Singleton, and I would call it during awake to make sure the initialization is called all the times.

public class YourMonoAutoInit : MonoBehaviour
{
    public InitLogic _initializationLogic;

    //a factory method with the related gameobject and the init parameters
    public void Awake()
    {
        //make sure we not miss initialization logic
        Assert.IsNotNull(_initializationLogic);
        InitMonoBehaviour(_initializationLogic);

    }

    private void InitMonoBehaviour(InitLogic initializationLogic)
    {
        //init here using
        int request = initializationLogic.currentRequest;
        bool playerAlive = initializationLogic.playerIsAlive;
    }
}

public class InitLogic : ScriptableObject
{
    public int currentRequest = 1;
    public bool playerIsAlive = false;
}

//this is another monobehaviour that might access the player state and change it
public class KillPlayer : MonoBehaviour
{
    public InitLogic playerState;
    public void KillThisPlayer() => playerState.playerIsAlive = false;
}

使用此最新版本,您将实现控制反转.
因此,不要将数据设置为单声道行为:

With this last version you're achieving inversion of control.
So instead of SETTING DATA INTO THE MONOBEHAVIOUR:

//SETTING DATA FROM CLASS TO MONOBEHAVIOUR
public class OtherScript : MonoBehaviour
{
    private void CreateMonoBehaviour() => YourMono.AddWithInit(gameObject, 1, true);
}

您将从班级中获取数据.

You will GET DATA IN MONOBEHVARIOUS FROM CLASS.

//GETTING IN MONOBEHVARIOUS FROM CLASS
public class YourOtherMono : MonoBehaviour
{
    public YourData data;

    private void Awake()
    {
        (int yourInt, bool yourBool) = data.GetData();
        //do something with the data received
    }
}

这篇关于用于在由代码创建的组件上设置必填字段的选项?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆