It can be useful make ScriptableObject assets that are meant to be cloned at runtime with Instantiate(object). This prevents modification of the original asset in the Editor, and allows to create multiple copies. The problem is that ScriptableObjects can have considerable overhead when they are instantiated more than a few times. They also need to be destroyed to avoid potential leaks and performance problems.
PlainDataInstancer is a ScriptableObject that instantiates plain serializable objects or structs. We get the convenience of SO assets without the overhead. The instances are deep clones, which is good in these cases, and creation is surprisignly fast when compared to other solutions.
Here's an example of how to define a data instancer that can be edited in the inspector, just like normal SO assets:
using System.Collections.Generic;
using UnityEngine;
// A class to be instantiated. It must be a plain class or struct with [System.Serializable].
[System.Serializable]
public class ExampleData
{
// Only supported fields that are public or marked with [SerializeField] can be copied to new instances.
public List<string> someTexts;
[SerializeField]private Vector3 aVector;
// References to Unity Objects also work out of the box.
public GameObject obj;
}
// This is a ScriptableObject that creates instances of type ExampleData. Make sure to put it on a file with the same name.
[CreateAssetMenu]
public class DataInstancerExample : PlainDataInstancer<ExampleData> { }
Here's how you use a PlainDataInstancer asset:
using UnityEngine;
public class DataInstancerUsage : MonoBehaviour
{
// Create a DataInstancerExample asset in the project window, then drag it here in the inspector.
public DataInstancerExample instancer;
void Start()
{
if (instancer == null) return;
// CreateDataInstance returns a new object that's a copy of the instancer's PrototypeData.
// You can edit the PrototypeData in the instancer's inspector.
ExampleData instance = instancer.CreateDataInstance();
}
}