- Home /
Adding/removing objects in editor mode
I create a game, where level design is done generating different level objects. So far I've been generating the level it when entering playmode, but I would like to change that, so it's done in editor mode.
I'm not that skilled in editor scripting, so I would like to know, if I'm doing it right.
My script (stripped down to the very basics of creating level objects):
using UnityEngine;
using System.Collections;
public class LevelElementGenerator : MonoBehaviour
{
public int amountOfLevelElements = 1;
public GameObject masterLevelElementGO;
private LevelElement[] levelElements = new LevelElement[0];
public void ChangeAmountOfLevelElements()
{
if (!masterLevelElementGO)
{
Debug.LogError("Master Level Element Game Object not assigned!");
return;
}
if (!masterLevelElementGO.GetComponent<LevelElement>())
{
Debug.LogError("Master Level Element Game Object haven't got a Level Element!");
return;
}
for (int i = 0; i < levelElements.Length; i++)
{
DestroyImmediate(levelElements[i].gameObject);
}
levelElements = new LevelElement[amountOfLevelElements];
for (int i = 0; i < levelElements.Length; i++)
{
GameObject go = Instantiate(masterLevelElementGO) as GameObject;
go.transform.parent = transform;
levelElements[i] = go.GetComponent<LevelElement>();
}
}
}
And the editor script:
using UnityEngine;
using System.Collections;
using UnityEditor;
[CustomEditor(typeof(LevelElementGenerator))]
public class LevelElementGeneratorEditor : Editor
{
private LevelElementGenerator levelElementGenerator;
private void OnEnable()
{
levelElementGenerator = (LevelElementGenerator)target;
}
public override void OnInspectorGUI()
{
DrawDefaultInspector();
if (GUILayout.Button("Change Amount"))
{
levelElementGenerator.ChangeAmountOfLevelElements();
}
}
}
It seems to work, but I'm very much in doubt, if it's the right way to do it...
Could it be done better/smarter?
Reading the DestroyImmediate documentation, I get worried. It states:
Also note that you should never iterate through arrays and destroy the elements you are iterating over. This will cause serious problems (as a general programming practice, not just in Unity).
Isn't that exactly what I'm doing? What "serious problems" does it refer to - do I face great dangers?
Any solutions or comments are most welcome! I highly value any thoughts that help me understand this better :)
Answer by VesuvianPrime · Feb 06, 2015 at 04:40 PM
I make a point of setting things to null after I have called destroy. I suspect the warning in the documentation is because you're leaving a bunch of bad references in the array.
Try this:
public static T SafeDestroy<T>(T obj) where T : Object
{
if (Application.isEditor)
Object.DestroyImmediate(obj);
else
Object.Destroy(obj);
return null;
}
public static T SafeDestroyGameObject<T>(T component) where T : Component
{
if (component != null)
SafeDestroy(component.gameObject);
return null;
}
Usage:
for (int index = 0; index < levelElements.Length; index++)
levelElements[index] = SafeDestroyGameObject(levelElements[index]);
If we were looping over a list I would then call List.Clear(), but since we're using an array:
System.Array.Resize(ref levelElements, 0);
Remember to clean up after yourself!
Thank you very, very much! The solution works like a charm - and I learned a lot as well. That’s brilliant.
Just to make sure I’ve understood it correctly:
SafeDestroyGameObject checks if the to-be-destroyed GameObject exists - avoiding nullrefs.
SafeDestroy chooses the right way to destroy, depending on unity-mode.
Setting levelElements[index] to null cleans up the ref.
Two small followup-questions - if you please have the time :)
1) Why are you passing down the return-value of null from SafeDestroy through SafeDestroyGameObject to levelElements[index]-assignment?
levelElements[index] = SafeDestroyGameObject(levelElements[index]);
…is a beautiful one-liner, but returning void + this would have done the same right:
SafeDestroyGameObject(levelElements[index]);
levelElements[index] = null;
2) The array should be used again immediately. Can I just use the following?
System.Array.Resize(ref levelElements, amountOfLevelElements);
Thanks again man - you’re a champ!
SafeDestroyGameObject is just a lazy way of passing a Component in order to destroy the GameObject (thus destroying the component as well).
This means that:
levelElements[index] = SafeDestroyGameObject(levelElements[index]);
Is actually the equivalent of:
// Will throw a null ref if level Elements contains null
SafeDestroy(levelElements[index].gameObject);
levelElements[index] = null;
Returning null for the one-liner is of course optional, I just find it makes complex code read a lot better.
Resizing the array afterwards is absolutely fine, especially now we are setting all the elements to null. Just make sure the array always makes sense in the places it is being used.
Brilliant! I'm so eager to learn all these important details. You've been a great help! Thanks man!
Your answer
Follow this Question
Related Questions
How can I disable the ability to delete gameobjects in scene view? 1 Answer
Get all gameObjects of unloading scene from editor Script 0 Answers
OnObjectCreate event 1 Answer
sorting game objects when placing them in editor 0 Answers
How to store, change and delete gameobject and it's list element? 0 Answers