- Home /
Editor-Scripting : event for a serialized property changed
Hi guys,
so, I'm trying to make a script, an Editor, that will allow me to change a value - and see the result of the change immediately while in editor mode, e.g. not running the game.
I manage to this, it's working fine - but I cannot escape the feeling that it may be there a more elegant or simpler solution than the one I'm using. I searched extensively the net, but couldn't find a better solution. If I'm missing something, please help.
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(cardSuite);
serializedObject.ApplyModifiedProperties();
if (EditorGUI.EndChangeCheck())
{
_cardScript.ChangeSuite();
}
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(cardNumber);
serializedObject.ApplyModifiedProperties();
if (EditorGUI.EndChangeCheck())
{
_cardScript.ChangeNumber();
}
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(isFlipped);
serializedObject.ApplyModifiedProperties();
if (EditorGUI.EndChangeCheck())
{
_cardScript.Flip();
}
}
"cardNumber" and "cardSuite" are enums type, which will produce dropdown control in the Editor. isFliped is a Boolean value, which will produce a checkbox control in the Editor.
So, this is working fine - but is there a better solution? I imagine this will become a huge mess if there are 10+ properties...
Answer by JoeCross9 · Jul 16, 2018 at 02:12 AM
So I researched this a little more (for some reason, it didn't give me sleep at night :)) - and I found a better solution. Still, it's not perfect, but I believe this is what Unity devs use, too.
public SerializedProperty cardSuite;
public SerializedProperty cardNumber;
public SerializedProperty isFlipped;
private CardScript _cardScript;
void OnEnable()
{
cardSuite = serializedObject.FindProperty ("CardSuite");
cardNumber = serializedObject.FindProperty ("CardNumber");
isFlipped = serializedObject.FindProperty("IsFlipped");
}
public override void OnInspectorGUI()
{
// Update the serializedProperty - always do this in the beginning of OnInspectorGUI.
serializedObject.Update();
EditorGUILayout.PropertyField(cardSuite);
EditorGUILayout.PropertyField(cardNumber);
EditorGUILayout.PropertyField(isFlipped);
_cardScript = (CardScript)target;
string _enumVal = cardSuite.enumNames[cardSuite.enumValueIndex];
if(_cardScript.CardSuite.ToString() != _enumVal)
{
_cardScript.ChangeSuite(_enumVal);
}
_enumVal = cardNumber.enumNames[cardNumber.enumValueIndex];
if(_cardScript.CardNumber.ToString() != _enumVal)
{
_cardScript.ChangeNumber(_enumVal);
}
if(_cardScript.IsFlipped != isFlipped.boolValue)
{
_cardScript.Flip(isFlipped.boolValue);
}
serializedObject.ApplyModifiedProperties();
}
Basically, the solution is to compare the values of the serialized property against the value of the current target object.
Answer by JDelekto · Jun 27, 2018 at 01:47 AM
I'm not sure if you need to call the EditorGUI.BeginChangeCheck()
and EditorGUI.EndChangeCheck()
multiple times; but instead use one call; but if it is required, you can refactor your code to make it easier to handle new properties in the future.
First, define a struct which will hold a reference to your SerializedProperty and an Action delegate to call the method on your _cardScript instance. For the sake of the code below, I'll assume that the type of _cardScript is a class named "CardScript".
public struct PropertyAction
{
public PropertyAction(SerializedProperty property, Action<CardScript> action)
{
this.property = property;
this.action = action;
}
public SerializedProperty property;
public Action<CardScript> action;
}
Create a member in your class which is a list of these "PropertyActions", which you will initialize after you've initialized the SerializedProperty references to cardSuite, cardNumber and isFlipped, like so:
private List<PropertyAction> propertyActions;
At an appropriate place in your script, after you've retrieved your SerializedProperty references, initialize the list of PropertyAction structs (this only needs to be done once, and to handle new properties, all you need to do is add a new entry to the initializer list. It will look something like this:
private void Awake()
{
// Get references to SerializedProperties here first...
// Initialize property and method delegates
this.propertyActions = new List<PropertyAction>
{
new PropertyAction(cardSuite, (cs) => cs.ChangeSuite()),
new PropertyAction(cardNumber, (cs) => cs.ChangeNumber()),
new PropertyAction(isFlipped, (cs) => cs.Flip())
// Add new property/action combinations here....
};
}
Now, at this point, you add code to your OnInspectorGUI() which will iterate through this list and perform the same actions for each property in your list. It will check for the change, apply modified properties and if changed, call the appropriate action on your _cardScript instance. This code only needs to be written once!
public override void OnInspectorGUI()
{
serializedObject.Update();
this.propertyActions.ForEach(propertyAction =>
{
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(propertyAction.property);
base.serializedObject.ApplyModifiedProperties();
if (EditorGUI.EndChangeCheck())
{
propertyAction.action(this._cardScript);
}
});
}
If all of your properties follow the same pattern, then all you need to do is add one new entry to the list, which is the SerializedProperty reference and a delegate to call an action on your _cardScript instance. Aside from getting the property reference itself, it's just one new line of code to perform the same operations on that new property.
Thanks for the reply.
Yes, this is a good way to deal with repetitive code, basically in any program$$anonymous$$g language, thanks.
But, what I was looking for is more Unity oriented solution. Devs from Unity says they are using the same classes to develop editors for general use, so I was wondering how they are dealing with it.
$$anonymous$$aybe you are thinking of using a ScriptableObject?
Your answer
Follow this Question
Related Questions
Editor Window: Renaming an object using SerializedProperty 0 Answers
Calling ApplyModifiedProperties results in other variables resetting to 0? 0 Answers
If Check for serialized flag enums in Editor scripting 1 Answer
Get UnityEvent reference from SerializedProperty 1 Answer
How to initialize array[][] via SerializedProperty? 1 Answer