How do I build a custom editor that iterates over a scene hierarchy and presents all the controls for Springs in one UI?

Here’s the context:

I have a puppet with a set of multitouch controls that depend on a network of spring joints - visualised below with the colored handles and the lines. It’s working nicely, but I’d like a custom inspector that collects all the relevant springjoint2D settings into one UI (in the inspector and eventually in the scene and game GUI.

So: the problems at the moment are:

(1) the spring float fields are readonly and not editable.

(2) I get an error at line 30 in SpringScriptEditor.cs: NullReferenceException: Object reference not set to an instance of an object. This I’ve read about elsewhere and don’t know if it is related to (1).

80005-screen-640x480-2016-10-12-22-31-53.png

The hierarchy of controls looks like this (a springJoint is on each controller):

Scene
  |---PuppetRoot_Wayang
        |------Controller 1
                  |------Controller 2
                           |------RodController
                                    |------Controller 3
                                    |------Controller 3 a
                                    |------Controller 4
                                    |------Controller 4a
etc...

Here’s the result, followed by the code, which makes me feel I’m nearly there - though I don’t think I get SerializedProperties / Objects properly yet - and I’ve tried several of the approaches on ‘answers’.

(1) Here’s the basic object class: GetSprings.cs

using System.Collections;
using System.Collections.Generic;

[System.Serializable]
public class GetSprings : MonoBehaviour {

	public GameObject[] puppets;

	public GameObject[] myObject {
		get {return puppets;}
	}
}

And

(2) The Custom Editor Script (SpringsScriptEditor.cs)

using UnityEngine;
using System.Collections;
using UnityEditor;

[CustomEditor(typeof(GetSprings)),CanEditMultipleObjects]

public class SpringsScriptEditor : Editor {

	//SerializedProperty puppets;

	void OnEnable()
	{
		//puppets = serializedObject.FindProperty("puppets");
	}

	public override void OnInspectorGUI() {

		serializedObject.Update();
		var t = target as GetSprings;
		SerializedProperty tps = serializedObject.FindProperty ("puppets");
		EditorGUI.BeginChangeCheck();
		EditorGUILayout.PropertyField(tps, true);
		if(EditorGUI.EndChangeCheck())
		serializedObject.ApplyModifiedProperties();
		

		//NullReferenceException: Object reference not set to an instance of an object
		// error here when array doesnt exist?

		foreach (GameObject puppet in t.puppets) {
						
			Traverse(puppet);
			//Debug.Log(puppet.name);		
		}
	}

	void Traverse(GameObject obj) {

		if (obj != null) {
			foreach (Transform child in obj.transform) {

				Component[] springJoints;
				springJoints = child.gameObject.GetComponents( typeof(SpringJoint2D) );
				//Debug.Log("Parent Object: "+obj.name);
				foreach (SpringJoint2D spring in springJoints) {
					//EditorGUILayout.LabelField(obj.name, );
					if (spring) {
		EditorGUI.BeginChangeCheck();
		EditorGUILayout.LabelField(spring.gameObject.name,EditorStyles.boldLabel);
		EditorGUILayout.FloatField(spring.gameObject.name+ " Frequency", spring.frequency);
		//EditorGUILayout.Slider(spring.frequency,0.0f,200f);
		EditorGUILayout.FloatField(spring.gameObject.name+ " Damping", spring.dampingRatio);
		//EditorGUILayout.Slider(spring.dampingRatio,0.0f,200f);
		EditorGUILayout.FloatField(spring.gameObject.name+ " Distance", spring.distance);
	       //EditorGUILayout.Slider(spring.distance,0.0f,10f);
			if(EditorGUI.EndChangeCheck()) {
				serializedObject.ApplyModifiedProperties();
			}				
						//Debug.Log("Parent: "+spring.gameObject.name+"

"+
// "Connected Body: “+spring.connectedBody+”
"+
// "Distance: “+spring.distance+”
"+
// "Damping: “+spring.dampingRatio+”
"+
// "Frequency: “+spring.frequency+”
");
}
}
Traverse(child.gameObject);
}
}
}
}

I hope I’m missing something simple…

Thanks in advance.

Ian

That’s fantastic. Thank you for such a detailed answer. I was on the right track - and used lists in a more ‘manual’ solution. I was looking at a custom class, getters and setters etc. but your walk-though has drawn that all together and the principle of moving it into a custom editor / custom window is working too. I read the catlikecoding tutorial and found that helpful. Thank you for your help.

I collect all the puppet controls (for live performance) into a GUI window. And exposing the spring settings helps speed up setting the objects. I’ve adjusted this to work in the editor.

I needed to make public the refresh method. And in the non-editor version had to call refresh from update in order for the slider GUI changes to effect the springs. Shame there isn’t a method that updates on GUI change. In the editor script this works as expected.

void Refresh()

Changed:

public void Refresh()

Also, I added the [Range…] attributes. Thanks to you I get it a lot more! Cheers!

Ian

ps. If you promote the comment to an ‘answer’ - I’ll up-vote it and mark it as the answer… cheers.