Observable values: Stop Checking a Value on Update
Today, I will write about a problem I have encountered lately and how I solved it. Maybe it will be useful for one of you, or maybe you will make it even better. Before showing how observable values can make our games more performant, let me introduce the problem to you.
As you all know, we have an Update() method in MonoBehavior class. We are using this method to update the GameObject. Unity calls this method at each frame. So, we are talking about 60 calls per second, if your game is running at 60 fps. Therefore we should use the Update() method as efficient as possible.
Problem?
Maybe it is not necessarily a big problem but I have realized something about my Update() method implementations. I update the components or game entities based on some primitive values. Like, I am setting a string to some UI component or update energy bars based on some integer value. However, I don’t have to update, execute or set some value, if that value remains as the same.
Let’s make an example;
internal class EnergyBar : MonoBehaviour { public int Energy { get; set; } private Image filler; void Start() { Energy = 0; filler = GetComponent<Image<(); } void Update() { filler.fillAmount = Energy / 100f; } }
In the code sample above, you see a simple usage of Update() method. It is dividing Energy value by 100, and sets the result as a fill amount to the image component. It does this operation at each frame, however, we don’t really need to set fillAmount, if the energy is the same.
Solution 1: Check If the Value is Changed
As a first sight solution to this waste, we can check if the Energy is changed. If so, we set the fillAmount with the new energy value. Here, you see how it can be done below.
internal class EnergyBar : MonoBehaviour { public int Energy { get; set; } private int oldEnergyValue; private Image filler; void Start() { Energy = 0; oldEnergyValue = 0; filler = GetComponent<Image<(); } void Update() { if (Energy != oldEnergyValue) { filler.fillAmount = Energy / 100f; oldEnergyValue = Energy; } } }
This can be seem like a solution to you, but now, we allocate twice as much as memory than we use the last time. So if we do solve it with this approach, we sacrifice the performance to the memory. However, as game developers we need both of them.
Can we do it better?
Let’s stop and do some elaboration. What is happening here is that some other class or the class itself sets a new energy value. Then in the update method, we check if the value is changed. If so, we update a component on this gameObject. Well! Why don’t we update the component when we set the new energy value?
Maybe you already solve this problem for you games. It seems to me that, we have a lot of usages similar to this usage. Then, why don’t we generalize this approach and make the code more readable and performant at the same time?
Solution 2: Observable Values
The better solution I could come up with is creating observable values. This is a simple wrapper class which contains a callback method in it. This callback will be called whenever the wrapped value is changed. For example, I present you the first draft of ObservableInteger.
public struct ObservableInteger { private readonly Action<int> valueHasChangedCallBack; private int value; public int Value { get => value; set { this.value = value; valueHasChangedCallBack?.Invoke(this.value); } } public ObservableInteger(int value, Action<int> callback = null) { this.value = value; valueHasChangedCallBack = callback; } public static ObservableInteger operator +(ObservableInteger value, int amount) { value.value += amount; value.valueHasChangedCallBack(value.value); return value; } public static ObservableInteger operator -(ObservableInteger value, int amount) { value.value -= amount; value.valueHasChangedCallBack(value.value); return value; } public static implicit operator int(ObservableInteger observableInteger) { return observableInteger.value; } public static bool operator ==(ObservableInteger value, int amount) { return value.value == amount; } public static bool operator !=(ObservableInteger value, int amount) { return value.value != amount; } public override bool Equals(object obj) { if (!(obj is ObservableInteger)) { return false; } var integer = (ObservableInteger)obj; return value == integer.value; } }
I have also tried to override the operators for convenience. So that, we can have a nice looking code. Now, we can define the energy as an ObservableInteger and give the code piece in the update method as a call back. Here the last state of the EnergyBar class.
internal class EnergyBar : MonoBehaviour { private ObservableInteger energy; private Image filler; void Start() { energy = new ObservableInteger(0, UpdateEnergy); filler = GetComponent<Image<(); } public void SetEnergy(int amount) { energy.Set(amount); } private void UpdateEnergy(int amount) { filler.fillAmount = amount / 100f; } }
As you could see, we don’t need Update() method now. We have SetEnergy(int) method and some another entity will call this method. Calling in will create a change and trigger a call to UpdateEnergy(int) method.
What we Have Gained?
As conclusion, we don’t make an unnecessary division or a lot of checking if the value has changed. We just update the Image component when the energy is changed out of the box.
It is possible to improve a lot of stuff here, so making a library of observable values is on my agenda. If I can make it, I would like to publish it as a free asset, so you can all use it. On the other hand, you can write some generalized solution as a separate module. If you are interested in modular game development, you can follow this post and read it.
In case you have more ideas about the problem or you want to criticize the solution, please comment it and let me hear you.
Nice solution. Thank you for sharing ^^
Your code is really awesome. Thank you soo much! đŸ™‚
but, there is no “Set()” method.
“filler = GetComponent<Image<();” change to “filler = GetComponent();”
Another great post, this one is easy to do and works out great. I’m going to be doing a similar thing but for updating UI when a value changes. So will probably use the same pattern but have a child that has a specific callback that will update the image or the text or whatever when it’s done.