Anatomy of a script
Every scripted mod in GTA V with ScriptHookVDotNet is built around a single class that inherits from GTA.Script. Once you understand the four moving parts of this class, reading any mod in the wild becomes straightforward.
The minimal skeleton
Here is the cleanest structure of a functioning script:
using System;
using System.Windows.Forms;
using GTA;
public sealed class AnatomyScript : Script
{
public AnatomyScript()
{
Tick += OnTick;
KeyDown += OnKeyDown;
Aborted += OnAborted;
}
private void OnTick(object sender, EventArgs e)
{
}
private void OnKeyDown(object sender, KeyEventArgs e)
{
}
private void OnAborted(object sender, EventArgs e)
{
}
}
The class declaration
public sealed class AnatomyScript : Script
Three words matter here:
public: SHVDN uses reflection at startup to inspect your DLL. If your class is notpublic, the loader cannot see it and ignores it completely.sealed: Tells the compiler that no other class will inherit from this one. It is a good C# practice for performance and clarity.: Script: The inheritance. By inheriting fromGTA.Script, you register your class with the SHVDN fiber system. SHVDN instantiates your class automatically when the game or the reload key runs.
The constructor: where you wire events
public AnatomyScript()
{
Tick += OnTick;
KeyDown += OnKeyDown;
Aborted += OnAborted;
}
The constructor runs once, the moment SHVDN loads your script. Do not write heavy logic here. Its only job is to wire up events:
Tick: Runs repeatedly every frame (or at the cadence of the script fiber). This is where continuous checks live.KeyDown: Fires whenever the player presses a keyboard key. This is where manual triggers live.Aborted: Fires when the script is unloaded, whether by pressing the reload key (Insert) or closing the game. This is your cleanup contract.
The three primary events
Tick: continuous simulation
OnTick runs many times per second. If you put code here without conditions, it executes constantly. Always guard your logic:
private void OnTick(object sender, EventArgs e)
{
// Do not run until the player character is valid and loaded
if (Game.Player.Character == null || !Game.Player.Character.Exists())
{
return;
}
}
KeyDown: instantaneous reaction
OnKeyDown receives a KeyEventArgs containing e.KeyCode. It only runs when a key is pushed down:
private void OnKeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.F6)
{
GTA.UI.Notification.PostTicker("Key F6 pressed.", false);
}
}
Aborted: the cleanup guarantee
If your script spawns an NPC, creates a vehicle, or changes global game settings, OnAborted must clean up those resources. If you forget this, reloading your script leaves ghost entities behind every time you test:
private void OnAborted(object sender, EventArgs e)
{
// Delete created entities, restore default settings
}
Next, we look at the basic data types you will use inside these methods to track numbers, text, and state.