API Unity often pushes programmers to poor practice of hardcore function names. One of such slippery ways is the usage of Invoke function for MonoBehaviour class: Invoke, CancelInvoke, IsInvoking, InvokeRepeating.
Invoke function and co. are useful when we need to call another function from the initial one with a delay of a few seconds. For instance, after start of the level, to show the panel with bonuses, not immediately, but in two seconds:
public void Start()
{
Invoke("ShowPanel", 2f);
}
private void ShowPanel()
{
//show panel there
}
Or if it’s necessary to create a game object (bullet from a gun, a monster, running from behind the corner, etc.) regularly with a certain periodicity. For example, one second after the start of the level for the first time and every three seconds each following time:
public void Start()
{
InvokeRepeating("SpawnMonster", 1f, 3f);
}
private void SpawnMonster()
{
//Spawn monster there
}
And it’s convenient, but the hardcore functions names lead to problems, forcing to keep in mind unnecessary information and complicating support and refactoring of the code.
Let’s try to solve the issue taking advantages of the C# language and write the wrappers for Invoke functions and co.:
For a start we have to write a method which gets the name of the function out of the lambda expression, calling this function:
private string GetMethodName(Expression<Action> expr)
{
return ((MethodCallExpression)expr.Body).Method.Name;
}
After that, write a wrapper for Invoke function, which takes on entry not the function name, but lambda, which calls the function, gets the name out of lambda and calls built-in Unity Invoke function:
protected void Invoke(Expression<Action> expr, float time)
{
Invoke(GetMethodName(expr), time);
}
By analogy we wrap all other functions, we place the wrappers into the basic class and use. The foregoing examples will now look as follows:
public void Start()
{
Invoke(() => ShowPanel(), 2f);
}
private void ShowPanel()
{
//show panel there
}
and
public void Start()
{
InvokeRepeating(() => SpawnMonster(), 1f, 3f);
}
private void SpawnMonster()
{
//Spawn monster there
}
Now, any renaming of the function during refactoring will result in a compilation error, or all function calls will be automatically renamed by refactoring tools, if any are used.
You can find wrappings for Invoke here: UtilsExtensions.cs
Thank you for attention!