- Home /
Custom editor: How to initialise a new array element?
I'm trying to make a simple track editor that uses a Bezier Path to generate a 3d mesh for a friend's racing game. The track data is an array of custom TrackDataPoint objects which store the position, tangent and other properties of each point on the track. Here is the TrackData and TrackDataPoint class code (I removed update functions of TrackData and all the "using" stuff to keep it compact here):
[System.Serializable] public class TrackDataPoint : MonoBehaviour { Vector3 position = Vector3.zero; Vector3 tangentDir = Vector3.right; float tangentWeightIn = 1.0f; float tangentWeightOut = 1.0f; }
[System.Serializable] public class TrackData : MonoBehaviour { public TrackDataPoint[] points; }
In my editor class, I am able to get a custom editor displaying, get a the list of points and get each point into a SerializedProperty object, however, I cannot figure out how to get the data into the list of TrackDataPoints stored in TrackData (points). I can add a new point using the small editor interface I made, but it is not initialized, so just appears as an empty space when I try to display it.
[CustomEditor(typeof(TrackData))] public class TrackDataEditor : Editor { private SerializedObject trackData; private SerializedProperty points;
void OnEnable () {
trackData = new SerializedObject(target);
points = trackData.FindProperty("points");
}
public override void OnInspectorGUI () {
trackData.Update();
//draw GUI for each point
for (int i = 0; i < points.arraySize; ++i) {
EditorGUILayout.BeginHorizontal();
GUILayout.Label(i + ":", GUILayout.MaxWidth(20.0f));
SerializedProperty dataPoint = points.GetArrayElementAtIndex(i);
EditorGUILayout.PropertyField(dataPoint, true);
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("Add point")) {
points.array Size++;
// NOW WHAT!? :)
}
EditorGUILayout.EndHorizontal();
trackData.ApplyModifiedProperties();
}
}
How can I create a new TrackDataPoint and assign it to the empty array element from my editor script? If points happens to be a list of standard types, such as Vector3s, this happens automatically. As it is, I get new elements added to the array, but they are empty and display as a drop box for prefabs. If I remove the : MonoBehaviour from TrackDataPoint nothing shows up. Ideally I'd like to display a custom editor per point by having another editor for TrackDataPoint. Is this possible?
And there is some reason, in the future, why you need this custom GUI? - Because the standard GUI would edit your objects in an array without and allow you to add ones...
It's not actually something I desperately need to do, as displaying the points in the inspector is not something that will make for a nice editing experience. I will add an interactive editor in the scene view to make things easy. However, I don't like not knowing how to do things so I am trying to learn.
Also, the standard editor shows an empty array. I can't initialize new objects - I have to drag them in from a prefab, which is definitely NOT the way I want to add them.
Hi, I have question, at unity 4.3.4(newest), this work?
I applied this way to my script, but met error of
ArgumentOutOfRangeException: Argument is out of range. Parameter name: index
at this point,
showTrackDataPoint[i] = EditorGUILayout.Foldout(showTrackDataPoint[i],"Point #"+i); if(showTrackDataPoint[i]){
Answer by delstrega · Jun 23, 2012 at 12:34 PM
Here's the working version using List:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
[System.Serializable]
public class TrackDataPoint : Object{
public Vector3 position = Vector3.zero;
public Vector3 tangentDir = Vector3.right;
public float tangentWeightIn = 1.0f;
public float tangentWeightOut = 1.0f;
}
[System.Serializable]
public class TrackData : MonoBehaviour {
public List<TrackDataPoint> points;
}
using UnityEngine;
using System.Collections;
using UnityEditor;
using System.Collections.Generic;
[CustomEditor(typeof(TrackData))]
public class TrackEditor : Editor {
private SerializedObject trackDataSO;
private TrackData myTrackData;
private List<bool> showTrackDataPoint;
void OnEnable () {
trackDataSO = new SerializedObject(target);
myTrackData = (TrackData)target;
}
public override void OnInspectorGUI () {
trackDataSO.Update();
EditorGUILayout.BeginVertical();
int i = 0;
foreach(TrackDataPoint tp in myTrackData.points){
showTrackDataPoint[i] = EditorGUILayout.Foldout(showTrackDataPoint[i],"Point #"+i);
if(showTrackDataPoint[i]){
tp.position = EditorGUILayout.Vector3Field("Position:", tp.position);
tp.tangentDir = EditorGUILayout.Vector3Field("TangentDir:", tp.tangentDir);
tp.tangentWeightIn = EditorGUILayout.FloatField("Tangent Weight In:", tp.tangentWeightIn);
tp.tangentWeightOut = EditorGUILayout.FloatField("Tangent Weight Out:", tp.tangentWeightOut);
}
i++;
}
EditorGUILayout.EndVertical();
if (GUILayout.Button("Add point")) {
myTrackData.points.Add(new TrackDataPoint());
showTrackDataPoint.Add(false);
}
if(GUI.changed){
EditorUtility.SetDirty(target);
EditorUtility.SetDirty(myTrackData);
}
trackDataSO.ApplyModifiedProperties();
}
}
The basic idea is to get a reference to the object you are editing by using
myTrackData = (TrackData)target;
That way you can do all the stuff you would normally do with arrays and such and don't have to worry about SerializedProperty at all. So you can add an element by adjusting the array length and adding the new point like you would do outside of the editor. Since you are working on the "target" object, only casted differently, all changes should automatically reflect in the editor.
Hope that points you into the right direction,
delstrega
Wow, I had no idea that that would work! I'll give it a go. Seems kind of obvious now, but I thought target was a serializedObject and it wasn't possible to access the members at all. I was a little annoyed about that because it's normal to access the object directly in the other editor functions (OnSceneGUI etc.)
I'll convert the array to a list and add some code to make working with it from the editor class easier and then everything should be hunky dory.
Thanks!
Yea, you can access your class as normal just casting "target". But now that I tested the code I posted it must say that it won't work like that. I'll try to fix the bugs and edit my answers to reflect the changes. Stay tuned ;)
Tested it and you're right! It works perfectly. I spent so long looking at the docs for SerializedProperty and SerializedObject and completely overlooked the target parameter of Editor. DOH!
Haha, I'm glad it's working for you. I spent the last 30 $$anonymous$$utes trying to get the code I to work. Well I edited my above post in case anyone is interested. :)
You're welcome AndyP :D
I have a question. Who actually instanciateS the "points" List in TrackData?
Your answer
Follow this Question
Related Questions
Cannot convert serializedObject to float 1 Answer
Draw custom Property Field in Custom Editor 1 Answer
Incrementing a SerializedProperty on a per-object basis 1 Answer
Key Events in Hierarchy Window 1 Answer
How do I change inspector values of other game objects using a custom Editor Window 0 Answers