Spotted that shinny little button in the lower right corner? That’s what we are aiming at: a single button for multiple purposes.
The easiest way to handle this, is using if conditional statements. You use the current situation as the input, and perform the respective action(s) with that situation.
using UnityEngine;
public enum Situation
{
Buy,
Equip,
Upgrade
}
public class MultiFunctionButton : MonoBehaviour
{
public Situation situation;
public void OnClick()
{
if (situation == Situation.Buy)
Buy();
if (situation == Situation.Equip)
Equip();
if (situation == Situation.Upgrade)
Upgrade();
}
private void Buy()
{
Debug.Log("Buy");
}
private void Equip()
{
Debug.Log("Equip");
}
private void Upgrade()
{
Debug.Log("Upgrade");
}
}
However, this solution is very inflexible. As you can see, the above code is mere functional, it deals nothing with the appearance of the button, such as changing the sprite or changing the text. You will have to handle these details in real life problems, it’s no doubt about that.
And what if, you need another type of function that the button has to perform, such as give it away, or decorate it with funny sticker? Iterating through every place to add new functionality is really the pain in the ass, and to be honest, very error-prone.
This leads us to the improved solution: State Pattern. Basically, it is the solution where you encapsulate the situation-specific details into objects. The button knows nothing of these details. Let the code speaks itself.
Firstly, I make a common interface for the situations, or state, which provides a method for receiving click event.
public interface IState
{
void OnClick();
}
public class EquipState : IState
{
public void OnClick()
{
Debug.Log("Equip");
}
}
public class BuyState : IState
{
public void OnClick()
{
Debug.Log("Buy");
}
}
public class UpgradeState : IState
{
public void OnClick()
{
Debug.Log("Upgrade");
}
}
Then, in the class that I attached to the button, I will only have a variable representing current state of the button (which can be “Buy”, “Upgrade” or “Equip”).
public class MultiFunctionButton : MonoBehaviour
{
public IState currentState;
public void OnClick()
{
currentState.OnClick();
}
}
Compare to the previous solution, this solution is nice, neat and clean. The MultiFunctionButton
class only needs to tell its current state that “Hey, I just have been clicked!”, and let the state object handles the work itself, with the details that only the state object knows.
When the needs of adding new state arrive, the MultiFunctionButton
class remains unchanged – you just need to add another state class derived from IState. Even better, the MultiFunctionButton
class doesn’t even notice that there is a change. This means this part of your code is well decoupled, clean, and easy to maintain.
I then assign the MultiFunctionButton
class’ OnClick method to the OnClick
event of the Canvas button, and let us see the result:
You might notice that I used a public variable in MultiFunctionButton
class. This is not a good practice at all – I don’t like my variables being exposed nakedly without any protection, but in the scope of this blog (which is only for demonstrating an cool idea in my opinion), this is not harmful to me. In real problems, however, I highly recommend using a layer of protection to the variable, specially when it affects directly to the functionality of the object it belongs.
Finally, I am curious to hear what you think about this. If you find there can be any improvement to my solution, please don’t hesitate, just put it in the comment box.
Cheers,