The Update method in Unity is a circle without beginning or end
Many times when we write code, we write it in a way that executes the statements in the order we have thought about them. If A has to happen before B, which in turn happens before C, it seems natural to write our code in the same way: A->B->C
Most of the time this is correct, but there is a case which this may create code that is harder to read, when in fact we could have written this code in different order, to make it more readable. This happens when we write code for our game loop, which is the Update method in Unity.
Our entry to the program is the Start method, after that, the update loops indefinitely. That means that the update loop can also be written like this: B->C->A without any change in the behaviour of our game, as long we have code in the Start method that executes A first.
Now that might not be a problem, in fact what does it matter if our code look like A->B->C or C->A->B or whatever?
Well that would be true, if those if statements didn’t exist in programming. By using if statements our Update method can have different exit points. That means it could execute like A->B->C1 or A->B->C2. If our code has nested if statements, that makes it harder to read. A common way to avoid this, is by extracting those statements in methods, but when we are inside our game loop, there is a better way. We can move those statements at the beginning. That doesn’t remove those if’s, but now they are not nested and that makes our code easier to understand.
Let’s see a simple example in code, which will make the above easier to understand.
The nested if’s problem
Let’s suppose that we have a character, that can only move left or right. When a condition is true, that character changes direction. For our example we will have an enum that represents the left and right directions, the character starts by moving left and our condition will be checking that if the key S has been pressed then the character moves right.
If the character is moving in the right direction then we will be checking if the key A has been pressed and then our character will start moving left again.
So if our enum is this:
enum Direction
{
Left,
Right
}
and in our Start method we start by moving left:
void Start()
{
_direction = Direction.Left;
}
our code will look something like this:
void Update()
{
if (_direction == Direction.Left)
{
// Left code
Debug.Log("Going left");
if (Input.GetKeyDown(KeyCode.S))
{
// lot of if key S got pressed code
_direction = Direction.Right;
}
}
else if (_direction == Direction.Right)
{
// Right code
Debug.Log("Going right");
if (Input.GetKeyDown(KeyCode.A))
{
// lot of if key A got pressed code
_direction = Direction.Left;
}
}
}
The above code is easy to understand, because it is small, but those nested if statements, can be a real problem when they get big. Imagine that we don’t have only two states left and right, but more like up down etc. Having nested if statements is never a good idea. A solution at first glance could be to create two methods and extract the code of each nested if, but there is a better way.
Changing the order that statements are executed
The above code has two main functions. The first is to execute the left, or right direction code and the second is to check a condition and update the variable that will execute the movement in the next loop. By changing the order that those two things happen, we can extract those two nested if statements at the beginning of our Update method.
We can write the above code like this:
void Update()
{
if (Input.GetKeyDown(KeyCode.S))
{
// lot of if key S got pressed code
_direction = Direction.Right;
}
else if (Input.GetKeyDown(KeyCode.A))
{
// lot of if key A got pressed code
_direction = Direction.Left;
}
if (_direction == Direction.Left)
{
// Left code
Debug.Log("Going left");
}
else if (_direction == Direction.Right)
{
// Right code
Debug.Log("Going right");
}
}
now that we have gotten rid of the nested if statements, our code has two parts that clearly do two different things, one checks for input and the other changes the direction. That makes it easy to understand, because each part of our code is responsible for only one thing and because of that, it can be extracted easily in a method, that has a clear purpose.
In code this looks like:
void Update()
{
_direction = GetDirection();
ChangeDirection(_direction);
}
Direction GetDirection()
{
if (Input.GetKeyDown(KeyCode.S))
{
// lot of if key S got pressed code
return Direction.Right;
}
if (Input.GetKeyDown(KeyCode.A))
{
// lot of if key A got pressed code
return Direction.Left;
}
return _direction;
}
void ChangeDirection(Direction direction)
{
if (_direction == Direction.Left)
{
// Left code
Debug.Log("Going left");
}
else if (_direction == Direction.Right)
{
// Right code
Debug.Log("Going right");
}
}
This might not seem a lot in this example, but imagine the first code before the refactoring when we have not only two, but many different states and inside those states exist nested if statements with a lot of code. Every change that we would have to make in the future would take long because there is not a separation of logic. We need to read the whole update method because the two parts of the code, the code that is responsible for checking the condition which choses the state and the code that executes the states are mingled together.
As always thank you for reading this article and if you have any questions or comments you can use the comment section or contact me directly via the contact form or email, also if you don’t want to miss any of the new articles, you can always subscribe to my newsletter or the RSS feed.