The game loop
When you write a normal desktop program, execution starts at line one and runs until the end. A game does not work like that. A game is an endless simulation loop running sixty times per second, rendering images, updating physics, and checking player inputs. Understanding that heartbeat is fundamental to writing code that feels smooth rather than stuttery.
The sixty frames per second rhythm
At 60 frames per second, the engine has exactly 16.6 milliseconds to complete an entire cycle:
- Process input: Read keyboard, mouse, and gamepads.
- Script update: Call all running script fibers, including your mod's
Tickevent. - World physics: Advance ragdolls, vehicle suspensions, bullet trajectories, and collisions.
- Render frame: Draw geometry, lighting, shadows, and UI onto your monitor.
If your script takes 20 milliseconds to do some heavy search or complex calculation, the entire frame drops. Do that every tick, and the game stutters from 60 FPS down to 30 or lower.
How ScriptHookVDotNet runs your script
SHVDN does not call your script on a random background thread. It runs each script inside a fiber during step 2 (Script update).
When your OnTick handler starts, the game engine is literally paused, waiting for you to finish your pass and hand control back. Once your handler reaches its closing brace, SHVDN yields control back to the engine. The engine runs physics, draws the screen, and comes back to your script on the next pass.
private void OnTick(object sender, EventArgs e)
{
// Do one quick pass of work, then get out of the way
Ped player = Game.Player.Character;
if (player == null || !player.Exists())
{
return;
}
// Fast check: distance or state
if (player.IsInVehicle())
{
// One lightweight action
}
}
The golden rule: never sleep
In standard .NET development, if you want to wait three seconds, you might be tempted to call System.Threading.Thread.Sleep(3000). In a GTA V script, that blocks the main fiber. The entire game freezes solid for three seconds: audio cuts, visuals freeze, and Windows offers to terminate the application.
If you need a delay, measure time against Game.GameTime:
private int _nextCheckTime;
private void OnTick(object sender, EventArgs e)
{
if (Game.GameTime < _nextCheckTime)
{
return; // Not time yet, let the engine continue smoothly
}
_nextCheckTime = Game.GameTime + 3000;
// Do work once every 3 seconds without blocking a single frame
}
Next, we look at coordinates: how Los Santos is mapped in 3D space.