- Home /
EndChangeCheck() not called by all [CanEditMultipleObjects] custom Inspector objects
Hello,
The purpose of the code below may seem a bit obtuse, so I'll describe it briefly, as I've omitted the portions not relevant to the issue.
I've created a custom inspector with a "dummy" EditorGUILayout.ObjectField that allows a user to drag any number of components onto it, then will add those components to various lists if it meets certain criteria. This field is contained in a static function, as this function can be called by a few different custom inspector scripts.
This particular functionality works perfectly.
The issue with which I seek assistance only occurs when attempting drag components onto the ObjectField when multiple objects that utilize the ReactorsArray() function are selected. In the example below, EndChangeCheck() is only called for the first selected object.
I seek to ensure that EndChangeCheck is called for each of the selected objects.
using UnityEditor;
using UnityEngine;
using System.Collections.Generic;
[CanEditMultipleObjects]
[CustomEditor(typeof(DSReaction))]
public class DSReactionEditor : Editor
{
private DSReaction T;
private void OnEnable()
{
T = target as DSReaction;
}
public override void OnInspectorGUI()
{
serializedObject.UpdateIfDirtyOrScript();
ReactorsArray(T);
serializedObject.ApplyModifiedProperties();
}
public static void ReactorsArray(Component T)
{
EditorGUI.BeginChangeCheck();
var obj = EditorGUILayout.ObjectField("Add New Reaction", default(Component), typeof(Component), true);
if (EditorGUI.EndChangeCheck())
{
Debug.Log(C.name + " changed!");
}
}
}
For further clarity, let's assume the scene has a number of GameObjects of the type DSReaction, which use the DSReactionEditor inspector script above, and that the GameObjects are named GameObject (1) to GameObject (5).
In the example above, if I select GameObject (1), then drag and drop a component onto the ObjectField in its inspector, it will debug log "GameObject (1) changed!", which is perfect. However, if I select all five GameObjects, then drag and drop a component onto the ObjectField, it still only outputs a single "GameObject (1) changed!" debug log.
How would I ensure that the Debug.Log fires for all of my selected GameObjects when the ObjectField has files dropped onto it?
Thank you so much in advance. All help is greatly appreciated! - S.
Answer by Bunny83 · Jul 02, 2016 at 06:26 PM
Your custom inspector does not handle multiple objects at all. You use the "target" property which is only used in single object editors. If you want to edit multiple objects you either should use the serializedObject property and it's SerializedProperties which can handle multiple objects at the same time.
If you don't want to use the SerializedObject you have to use the "targets" property and handle the multiple objects manually. Of course you have to handle the "merging" into single fields and the assignments to all targets yourself.
At the moment you don't edit any object at all. You randomly mix serializedProperty and direct access (using target) which doesn't make much sense.
Further more your Object field doesn't edit any value in any of the edited objects...
Wow! Believe it or not, I completely overlooked the existence of the "targets" variable, as opposed to "target". This should improve my code immensely, and could very well lead me to the solution for which I seek!
Yes, the code I posted is a bit odd out of context. I tried to make it as clear as possible, but I can see how it would still be misunderstood.
I use SerializedObject because most of the inspector (omitted for simplicity) is handled via SerializedProperties.
The ObjectField has code in the if (EditorGUI.EndChangeCheck()) that analyzes the provided GameObjects, and affects them directly based on whether or not they meet certain criteria. The provided objects themselves are not serialized, though -- ins$$anonymous$$d, scripts are added to them and objects added to their internal arrays. As such, I didn't see a purpose in having the ObjectField reference an external value, as its value would be discarded immediately after processing.
The built-in "targets" variable seems to be what I was missing, and although not a solution in and of itself, should lead me to the solution. As such, I'll vote this as answered! Thank you again!
Additionally, I'll post my code verbatim below, in case it helps clarify my initial use case.
public static void ReactorsArray(Component C, List myReactions )
{
// ADD REACTION FIELD
EditorGUI.BeginChangeCheck();
var obj = EditorGUILayout.ObjectField("Add New Reaction", default(Component), typeof(Component), true);
if (EditorGUI.EndChangeCheck())
{
var components = Selection.activeTransform == obj ? Selection.transforms : new [] {(Component)obj};
for (var i = 0; i < components.Length; i++)
{
var c = components[i];
if (myReactions.Contains(c.gameObject)) continue;
if (C.GetType() != typeof(DSTrigger) && IsReactionLoop(C.gameObject, c.gameObject))
{
Debug.LogError("Cannot add " + obj + ". It would cause an infinite Reaction loop. ");
}
else
{
var newReaction = c.GetComponent<DSReaction>();
if (!newReaction)
newReaction = Undo.AddComponent<DSReaction>(c.gameObject);
Undo.RecordObjects(new Object[] { C, newReaction }, "Add Reaction");
if (!newReaction.myTriggerObjs.Contains(C.gameObject))
newReaction.myTriggerObjs.Add(C.gameObject);
myReactions.Add(c.gameObject);
}
}
}
[...]
}