- Home /
Initialization of Classes Instantiated in an Array in the Inspector
I'm having some trouble with a custom inspector that I haven't been able find an answer to just yet. I have a script that takes references to multiple other GameObjects, and for each GameObject uses a custom class to store some settings for it. The setup for this is very simple. I have a DustSystem class:
public class DustSystem : MonoBehaviour
{
public DustSystemSettings[] dustSystems;
//Logic...
}
and the DustSystemSettings class:
[Serializable]
public class DustSystemSettings
{
public ParticleSystem particleSystem;
public AnimationCurve particleVelocityCurve = AnimationCurve.Linear(0, .3f, 150, 30);
public AnimationCurve particleRateCurve = AnimationCurve.Linear(0, 1, 150, 60);
public AnimationCurve pariticleLifetimeCurve = AnimationCurve.Linear(0, 12, 150, 1);
}
This is perfectly functional, but it has one really annoying drawback. When modifying the DustSystem from the inspector, I want to be able to add and remove multiple DustSystemSettings so a single script can control and many ParticleSystems as desired. The problem comes in when increasing the size of the array from the inspector. This creates a new instance of DustSystemSettings in the array, but instead of initializing it as I've described in the class definition, it is initialized with default/empty values.
The initial AnimationCurves are fairly well tuned and very valuable for quickly setting up new ParticleSystems. Losing those initial values is not feasible.
My question is, how can I get Unity to initialize new elements in the array properly? I know I can create a custom inspector and handle the adding and removing of array elements myself, but that takes a bunch of extra code and work, and I'm relatively confident there is a better way to accomplish my goal.
Thanks for you time.
Answer by Edy · Mar 06, 2015 at 04:38 PM
I've resolved this by monitoring the size of the array, then manually initialize new elements when size grows. Theres the full code for a example script. Save it to a script named ArrayTest.cs and try it in the inspector.
EDIT: Unfortunately this method doesn't work well with Undo/Redo. For example, reducing the size of the array and hitting Redo will revert the removed elements to the default values. If anyone knows a better solution, please and an answer or comment.
using UnityEngine;
using System;
[Serializable]
public class Test
{
public float value = 1.0f;
public string text = "Hello Text";
}
public class ArrayTest : MonoBehaviour
{
// Using Test[] instead of List<Test> allows to specify the default size for the array.
// Each element gets initialized properly when the object is created.
public Test[] array = new Test[2];
// OnValidate is called from the editor each time the component receives the values
// for its serialized properties.
//
// We keep track of the array size. Whenever it gets increased we initialize the new
// elements to the default values.
bool m_firstDeserialization = true;
int m_arrayLength = 0;
void OnValidate ()
{
if (m_firstDeserialization)
{
// This is the first time the editor properties have been deserialized in the object.
// We take the actual array size.
m_arrayLength = array.Length;
m_firstDeserialization = false;
}
else
{
// Something have changed in the object's properties. Verify whether the array size
// has changed. If it has been expanded, initialize the new elements.
//
// Without this, new elements would be initialized to zero / null (first new element)
// or to the value of the last element.
if (array.Length != m_arrayLength)
{
if (array.Length > m_arrayLength)
{
for (int i = m_arrayLength; i < array.Length; i++)
array[i] = new Test();
}
m_arrayLength = array.Length;
}
}
}
}
Thank you for the solution, this saved me untold hours of tedium! I found this so useful I made it into a convenient class for use with arrays and lists, and posted it on Github: https://github.com/lorenchorley/Unity3D-ArrayDefaultValuer There's a link back to this thread for credit. To be honest I'm surprised this isn't the default behaviour.
A very old post, but still an excellent solution by Edy. Thanx! If it's true that now (in 2018) such a workaround is still needed, this is really surprising. But it works like charm.