- Home /
Callback on variable change inside editor
Hello, I'm already using [ExecuteInEditMode] for my C# script, but i cannot find answer for my problem. How to react (inside editor without having to pushing play) on change of exposed (public) variable in real-time without constantly loop-checking (like OnGUI)?
The example would be: 1. I defined 'public type Varname' in my script. 2. Developer changed value of 'Varname' 3. I want to react on that change.
I had this ideas but none of them appeared to working in Unity editor: 1) Define 'public void Set_Varname(type Varname)'. 2) Define variable as 'public type Varname {set {callback_method();}}'
If there is no good way to be called only on variable change in unity editor, which event is called rarely enough to minimize CPU wasting, but every time that developer will change variable from editor?
I'm using C# but question is (i believe) really language agnostic, so You're free to choose answer in any language.
Answer by iomac · Oct 22, 2015 at 08:12 AM
From the docs:
This function is called when the script is loaded or a value is changed in the inspector
In 2020 Unity, it works perfectly when in Editor-Play mode. So, in Editor, "Play" the app. While playing, if you change a value in the Inspector, OnValidate
will be called.
And when you are just editing (ie, Play mode off), again, if you change a value in the Inspector, OnValidate
will be called.
It also works perfectly if you open a prefab.
(In an actual build: OnValidate is never called for any reason at any time.)
this is the correct answer. additionally, this is all you need. you do not also need ExecuteInEdit$$anonymous$$ode.
This function is very useful combined with [ExecuteInEdit$$anonymous$$ode] for updating static fields, for example :
using UnityEngine;
[ExecuteInEdit$$anonymous$$ode]
public class Example : $$anonymous$$onoBehaviour
{
public static int Field { get; set; }
[SerializeField]
private int field; // Developer set value in the inspector, for testing purpose
void Start()
{
if (!Application.isEditor)
Field = 0; // Normal value, used for the final game
}
void OnValidate()
{
Field = field;
}
}
note that ExecuteInEdit$$anonymous$$ode has been REPLACED by [ExecuteAlways]
! thanks, Unity!
Be aware that OnValidate()
only runs in editor mode. When you make a build, OnValidate
will not run when the script loads.
Thanks a lot, @iomac !! I put the setting inside the Start function, so it works when playing too. Then, inside OnValidate, I put Start(); like this:
void OnValidate(){ Start(); }
(Y)
That's a very bad idea. Never manually call other Unity callback functions like Start, Update, Awake ... Those have a well defined execution behaviour. When you call those functions manually you actually break this behaviour. For example Awake and Start are by definition only called once within the life-cycle of a component. When you call it manually it will actually be called more often. If you have functionality that needs to run in Start as well as in OnValidate, create a seperate method which you can call from both callbacks.
OnValidate will also cause console warnings if you try to make changes to some(?) scene object values (apparently). The warning can be confusing: "SendMessage cannot be called during Awake, CheckConsistency, or OnValidate"
Answer by StephanK · Apr 22, 2011 at 02:16 PM
As Properties are not exposed to the Standard Editor you will need to write your own Inspector for the Component. To see how to do this look at the Editor class and the CustomEditor attribute.
Now you can write your own GUI inside OnInspectorGUI where you can access the property rather than the variable directly. However be aware, that only public variables get serialized by unity, so if you have a property for a private variable that variable won't get saved to the scene file. You can avoid that by combining the HideInInspector and SerializeField attributes.
It's not my perfect solution but from what I've searched it's currently the only one. Thanks.
Answer by supernat · Jul 27, 2012 at 04:38 AM
It requires more variables, but the way I do this is to take advantage of the OnDrawGizmos method.
Create a duplicate set of private variables that will be used to track the public variables shown in the inspector (maybe just prepend them with the word prev). Then, in the OnDrawGizmos method, test for prevVarname != Varname and perform whatever function you need to perform when that variable changes.
Just make sure you update prevVarname after that, and also handle conditions where the user may select invalid values (like a 0 if Varname is going to be a divisor).
You may also want to wrap the OnDrawGizmos prevVarname tests in a nice little timer wrapper if you don't want a heavy resource intensive function to be called very often.
Also note that the editor sometimes gets behind, and things won't catch up until you click off the object or change another value, so it's not a perfect solution.
While that is great for primitive values, it does not quite work all that well with arrays or other complex objects (without having to add and debug a whole lot of code).
But why? Not sure I see the purpose of doing extra work to bypass built in functionality for no justifiable reason. That's akin to cramming your entire block of Start()
logic into the Update()
loop with a _isFirstRun
bool.