Skip to main content

Model loading

GTA V contains over ten thousand distinct 3D models: pedestrians, supercars, traffic lights, and trash cans. The engine cannot fit all of them into video RAM at the same time. Instead, it streams them from disk into memory only when they are needed near the player.

If your script attempts to spawn a character whose 3D model is not currently in memory, the engine will either spawn a glitchy invisible entity or crash immediately.

The streaming contract

Spawning an entity always follows a four-step ritual:

  1. Verify validity: Is the model name real and present in the game archives?
  2. Request into memory: Ask the streaming manager to load the mesh and textures, with a timeout.
  3. Spawn the entity: Create the ped or vehicle now that the asset is loaded.
  4. Release the model: Tell the streaming manager that your script no longer needs the raw asset pinned in RAM.

Here is the bulletproof pattern:

using GTA;
using GTA.Math;

public void SpawnCharacterSafely()
{
Model model = new Model(PedHash.Trevor);

// 1. Verify existence in the game's archive files
if (!model.IsValid || !model.IsInCdImage)
{
model.MarkAsNoLongerNeeded();
return;
}

// 2. Request with a hard timeout (2000 milliseconds)
if (!model.Request(2000))
{
// Timed out: asset could not be streamed in time
model.MarkAsNoLongerNeeded();
GTA.UI.Notification.PostTicker("Failed to stream character model.", false);
return;
}

// 3. Spawn while inside a try-finally block
try
{
Ped player = Game.Player.Character;
Vector3 spawnPoint = player.Position + player.ForwardVector * 2.5f;
World.CreatePed(model, spawnPoint, player.Heading);
}
finally
{
// 4. Always release the model, even if spawning threw an exception
model.MarkAsNoLongerNeeded();
}
}

Why Request(timeout) instead of an infinite loop

Old tutorials often show code like this:

// DANGEROUS: can hang your script forever
model.Request();
while (!model.IsLoaded)
{
Script.Yield();
}

If the model is corrupt, missing from an archive, or memory is full, model.IsLoaded never becomes true. Your script enters an infinite loop, hangs its fiber, and stops responding permanently.

By passing a timeout to model.Request(2000), SHVDN waits cooperatively up to 2 seconds. If the asset still is not ready, it returns false, allowing your code to fail gracefully, notify the player, and continue running.

The finally block is non-negotiable

Calling model.MarkAsNoLongerNeeded() tells the engine that your script is done holding a lock on that asset. Once the entity is created in the world, the entity itself keeps the model alive. Keeping the lock open consumes valuable streaming memory. Wrapping it in a finally block guarantees that the model is unlocked no matter what happens.

Now you understand coordinates, vectors, entities, validity, and models. You are ready for Project 02: Ped spawner.