当我创建如下所示的GameObject
时,我希望我的组件具有/设置必填字段。
GameObject go = new GameObject("MyGameObject");
MyComponent myComponent = go.addComponent(MyComponent);
由于我不能使用构造函数来设置私有字段,因此我需要为这些字段设置setter或设置多个强制属性的公共Init
函数。但是,这允许在代码中的任何地方更改值,并且与带有重载的构造函数不同,在创建这些参数后Init
“需要”或“可以被调用”这一点并不明显。
那么还有其他选项可以让我在创建组件时设置必填字段并使用“适当的”封装吗?
答案 0 :(得分:3)
正如评论中所述,Unity为MonoBehaviour初始化逻辑实现了2种主要方法:Start和Awake。
这两种初始化方法都有缺点:
有一个标准的order of execution,从中可以确保在唤醒后执行启动。但是,如果您有多个相同类型的MonoBehaviours,那么跟踪哪个首先执行将不容易。
如果您的问题只是执行顺序,而您有不同的类型,则只需更新script execution order。
否则,还有其他方法可以通过在单行为内部使用“工厂方法”来避免这两个缺点。
请考虑在这种情况下执行顺序为:
唤醒 => OnEnable =>重置=> 开始 => 您的初始化方法
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 只会被调用一次。
您还可以通过扩展名创建工厂。在这种情况下,您从类中删除了工厂方法,将工厂逻辑与游戏逻辑分离可能会很有用。
但是在这种情况下,您将需要在内部使用 InitMonoBehaviour 并在同一名称空间中实现扩展。
这样一来,InitMonoBehaviour的访问性将有所提高(内部可以从同一名称空间访问),因此封装性较低。
[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;
}
}
}
这表示这两个选项也都有缺点:
因此,如果您希望这种方法是“必需的”而不是可选的,我建议添加一个布尔标志_isInitiated,以确保没有人忘记实现它。
我个人会使用Scriptable Object或Singleton来实现逻辑,我会在清醒时调用它以确保初始化始终被调用。
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;
}
使用最新版本,您将实现控制反转。
因此,不要将数据设置为单声道行为:
//SETTING DATA FROM CLASS TO MONOBEHAVIOUR
public class OtherScript : MonoBehaviour
{
private void CreateMonoBehaviour() => YourMono.AddWithInit(gameObject, 1, true);
}
您将从班级获得单项数据。
//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
}
}