Skip to main content

Control flow

Every dynamic behavior in a game is a decision: If the player is in a car, change the radio. If health drops below twenty percent, trigger panic. If the destination is reached, complete the mission. Control flow is how you translate those rules into C#.

Guard clauses: avoid the pyramid of doom

Beginners often nest if statements inside if statements until the code marches off the right edge of the screen:

// Fragile and hard to read
private void OnTick(object sender, EventArgs e)
{
if (Game.Player.Character != null)
{
if (Game.Player.Character.Exists())
{
if (Game.Player.Character.IsAlive)
{
if (Game.Player.Character.IsInVehicle())
{
// Action buried under four layers of indentation
}
}
}
}
}

The professional pattern is the guard clause (early exit). Invert the condition and return immediately if the prerequisite is not met:

// Clean, flat, and defensive
private void OnTick(object sender, EventArgs e)
{
Ped player = Game.Player.Character;
if (player == null || !player.Exists() || !player.IsAlive)
{
return;
}

if (!player.IsInVehicle())
{
return;
}

// Main logic stays on the left margin
Vehicle car = player.CurrentVehicle;
}

Loops: doing repeated work

When you need to repeat an action, C# provides three main looping constructs:

The foreach loop

The most natural way to inspect a collection of items:

foreach (Ped ped in World.GetAllPeds())
{
if (ped.IsDead)
{
// Handle dead ped
}
}

The for loop

Used when you need an explicit numerical counter or index:

for (int i = 0; i < 5; i++)
{
// Repeat an action exactly 5 times
}

The golden rule of game loops: never block the frame

In regular desktop applications, you might write while (!fileDownloaded) { Thread.Sleep(100); }. In a game script, this will crash your game.

Remember the pipeline from Lesson 14: your script runs on a fiber that shares time with the game engine's main loop. If you write a while (true) loop without yielding, execution never returns to the engine. The game cannot render the next frame, physics cannot update, and Windows marks GTA5.exe as unresponsive.

If you need something to happen over time, do not loop inside a single frame. Instead, let OnTick do one small step each frame, check the elapsed time using Game.GameTime, and let the engine breathe.

In the next lesson, we see how to package these statements into reusable functions with clear responsibilities.