My factory creates instances based off ScriptableObject's ("SO") properties.
I'd like to not need to append to the Factory each new SO that I create. So ideally something like each SO registering itself in the factory on it's awake would be great.
I've explored two approaches, one is having the factory not be a MonoBehaviour:
public class ThingFactory
{
private static ThingFactory instance = null;
public static ThingFactory Instance
{
get
{
if (instance != null)
{
return instance;
}
else
{
instance = new ThingFactory();
return instance;
}
}
private set
{
instance = value;
}
}
public ThingFactory()
{
}
public void RegisterThing(ThingType thingType)
{
GameObject newThing = MonoBehaviour.Instantiate(thingType.prefab, Camera.main.transform);
newThing.SetActive(true);
newThing.GetComponentInChildren<Text>().text = thingType.name;
newThing.GetComponent<Image>().sprite = thingType.sprite;
}
}
public abstract class ThingType: ScriptableObject
{
public GameObject prefab;
public Sprite sprite;
#if UNITY_EDITOR
#else
public void OnEnable()
{
Debug.Log("Enabling ThingType of type: " + this.name);
ThingFactory.Instance.RegisterThing(this);
}
#endif
}
[CreateAssetMenu(menuName = "Things/ThingA")]
public class ThingAType : ThingType
{
}
Three problems with this approach.
The first is that OnEnable is called from the editor, I've gotten around that with the if UNITY_EDITOR.
The second problem is that OnEnable is called only when the scene has a MonoBehaviour referencing that ScriptableObject asset which isn't always the case. Even now when I'm reproducing the behaviour in a small project I can't put my finger on the exact requirements for OnEnable to be called.
The third problem is that I'm receiving this exception:
Some objects were not cleaned up when closing the scene. (Did you spawn new GameObjects from OnDestroy?) Image prefabOfThing(clone)
I don't understand this exception, it makes sense to me that if OnEnable is called before the scene loads, and then the SO is destroyed at scene load.
The second approach is to have the factory be a MonoBehaviour, and do reflection magic.
In it's awake have something like:
var thingTypes = Assembly.GetAssembly(typeof(ThingType)).GetTypes().Where(thingType => thingType.IsClass && !thingType.IsAbstract && thingType.IsSubclassOf(typeof(ThingType)));
But now I'm stuck - I have the classes for ThingTypes, like, ThingAType but not the individual SOs.
And an additional problem, is that OnEnable is called before my factory's Awake, so it is not initialized yet. Script execution order does not improve the situation.
I don't know of a good way of instantiating the factory pre-awake, I thought about using something like the following. But the issue with that approach is that the instance of the MonoBehaviourFactory would not have the references dropped in from the inspector such as someInspectorText.
public static MonoBehaviourFactory Instance
{
get
{
if (instance != null)
{
return instance;
}
else
{
instance = /*SomeRandomGameObject*/.AddComponent<MonoBehaviourFactory>();
return instance;
}
}
private set
{
instance = value;
}
}
private void Awake()
{
if (instance != null && instance != this)
{
Destroy(this.gameObject);
}
else
{
instance = this;
}
}
public Text someInspectorText = null;
How can I use OnEnable on ScriptableObjects to populate my factory with the types it will dispense?
#if !UNITY_EDITORif you only need the negative case? - derHugoOnEnablein aScriptableObjectis not only called when the Scene starts (since it lives in the assets) but also when Playmode is ended. Btw you don't need platform independent but can also check forif(Application.isEditor). For preventing the call after Playmode however you could try and add aif(!Application.isPlaying) return;since it is still in the UnityEditor actually. - derHugo