Custom Editor : Call a function on multiple selected objects

Learning how to write custom editor scripts.

When I select multiple objects, I can modify a variable and this is assigned to all selected objects, but when I use a custom editor button to call a function, only the first selected object has the function called.

Test scene : Create a new scene, create 2 cubes, create and attach the TestButtons script to both. Now create the TestButtonsInspector and store it in the Editor folder. Symptoms are as above.

TestButtons

#pragma strict

public var myTransform : Transform;
public var targetPosition : Vector3;

function Update() 
{
	LookAtTarget();
}

function ButtonOne() 
{
	Debug.Log( "Button One was pressed" );
	
	LookAtTarget();
}

function ButtonTwo() 
{
	Debug.Log( "Button Two was pressed" );
	
	transform.LookAt( Vector3.forward );
}

function LookAtTarget() 
{
	transform.LookAt( targetPosition );
}

TestButtonsInspector

#pragma strict

@CustomEditor( TestButtons )
@CanEditMultipleObjects

class TestButtonsInspector extends Editor 
{
	var myTransform : SerializedProperty;
	var targetPosition : SerializedProperty;
	
	var obj : GameObject;
	var objScript : TestButtons;
	
	function OnEnable() 
	{
		obj = Selection.activeGameObject;
		objScript = obj.GetComponent( TestButtons );
		
		myTransform = serializedObject.FindProperty( "myTransform" );
		targetPosition = serializedObject.FindProperty( "targetPosition" );
	}
	
	function OnInspectorGUI() 
	{
		// Update the serializedProperty - always do this in the beginning of OnInspectorGUI.
		serializedObject.Update();
		
		
		// myTransform
		EditorGUILayout.BeginHorizontal();
		EditorGUILayout.PropertyField( myTransform );
		EditorGUILayout.EndHorizontal();
		
		// targetPosition
		EditorGUILayout.BeginHorizontal();
		EditorGUILayout.PropertyField( targetPosition );
		EditorGUILayout.EndHorizontal();
		
		
		// Editor GUI Buttons
		EditorGUILayout.BeginHorizontal();
		
		if ( GUILayout.Button( "Button One", GUILayout.MinWidth( 80 ), GUILayout.MaxWidth( 250 ) ) )
		{
			//target.ButtonOne();
			//serializedObject.ButtonOne();
			
			//var obj : TestButtons = serializedObject.TestButtons;
			//TestButtons.ButtonOne();
			
			//var go = serializedObject;
			//go.SendMessage( "ButtonOne" );
			
			//this.ButtonOne();
			
			//var obj : GameObject = Selection.activeGameObject;
			//var objScript : TestButtons = obj.GetComponent( TestButtons );
			objScript.ButtonOne();
		}
		
		if ( GUILayout.Button( "Button Two", GUILayout.MinWidth( 80 ), GUILayout.MaxWidth( 250 ) ) )
		{
			//var obj : GameObject = Selection.activeGameObject;
			//var objScript : TestButtons = obj.GetComponent( TestButtons );
			objScript.ButtonTwo();
		}
		
		EditorGUILayout.EndHorizontal();
		
        // Apply changes to the serializedProperty - always do this in the end of OnInspectorGUI.
        serializedObject.ApplyModifiedProperties();
	}
}

Your problem is that you mix two fundamental things together. The Selection class of course holds the actual selection, no matter if it’s a gameobject, a prefab, an asset, … So whatever you select in the Unity editor it will be referenced by the Selection class.

Selection.activeGameObject only holds a reference to the “last” object that has been selected. It works exactly like in Windows. When you select multiple files and grab one of them to drag the files around, the last one you touched is the active one.

Selection.gameObjects holds the list of gameobjects that are selected.


However since you use a custom editor you don’t need the selection class at all.

The Editor class has 3 properties that should be enough to work with:

target is actually kind of obsolete. It also holds the reference to the component on the active gameobject. target can be useful for single-component editors. For multiple-object editors you have to use the “targets” and the serializedObject which wraps the whole selection at once.