Tutorial 2026-05-05

FiveM Entity Management & Object Spawning

TDYSKY

TDYSKY

Founder & Lead Developer at Agency Scripts

Understanding Entities in FiveM

In FiveM, an entity is any object that exists in the game world: vehicles, peds, props, pickups, and even the player character. Every entity has a handle (an integer ID) that you use to interact with it through native functions. Entity management is one of the most fundamental skills in FiveM development because almost every script interacts with entities in some way. Poor entity management leads to memory leaks, ghost objects that persist after a resource restarts, desync between players, and ultimately server instability. This guide covers the correct patterns for creating, tracking, and cleaning up entities.

Spawning Objects and Props

The primary native for spawning props is CreateObject (or CreateObjectNoOffset). Before spawning any object, you must request the model and wait for it to load. Failing to request the model first will result in an invisible or missing entity. Always release the model after creating the object to free memory.

-- client/entity_spawner.lua
local function SpawnProp(modelName, coords, rotation, isNetwork)
    local model = joaat(modelName)

    -- Request the model and wait for it to load
    RequestModel(model)
    local timeout = 0
    while not HasModelLoaded(model) do
        Wait(10)
        timeout = timeout + 10
        if timeout > 5000 then
            print(('[ERROR] Model %s failed to load after 5s'):format(modelName))
            return nil
        end
    end

    local obj = CreateObject(model, coords.x, coords.y, coords.z, isNetwork, true, false)

    if rotation then
        SetEntityRotation(obj, rotation.x, rotation.y, rotation.z, 2, true)
    end

    -- Freeze the object so it does not fall through the ground
    FreezeEntityPosition(obj, true)

    -- Release the model from memory
    SetModelAsNoLongerNeeded(model)

    return obj
end

-- Usage
local chair = SpawnProp('prop_chair_01a', vector3(200.0, -800.0, 31.0), nil, false)

Spawning Vehicles

Vehicle spawning follows a similar pattern but uses CreateVehicle and has additional considerations for network ownership, fuel levels, and plate text. Always spawn vehicles on the server side when possible, or use server-side callbacks to validate vehicle creation requests from clients.

-- client/vehicle_spawner.lua
local function SpawnVehicle(modelName, coords, heading)
    local model = joaat(modelName)

    RequestModel(model)
    while not HasModelLoaded(model) do
        Wait(10)
    end

    local vehicle = CreateVehicle(model, coords.x, coords.y, coords.z, heading, true, false)

    -- Basic vehicle setup
    SetVehicleOnGroundProperly(vehicle)
    SetEntityAsMissionEntity(vehicle, true, true)
    SetVehicleHasBeenOwnedByPlayer(vehicle, true)
    SetVehicleNeedsToBeHotwired(vehicle, false)
    SetVehRadioStation(vehicle, 'OFF')

    -- Set fuel if using a fuel system
    Entity(vehicle).state:set('fuel', 100.0, true)

    SetModelAsNoLongerNeeded(model)

    return vehicle
end

Entity Tracking and Cleanup

The most critical aspect of entity management is tracking every entity your script creates and cleaning them up when the resource stops. Without proper cleanup, entities persist in the game world as orphans that waste memory and render resources. Use a tracking table to store all entity handles and register an onResourceStop handler that removes them.

-- client/entity_manager.lua
local ManagedEntities = {}

function RegisterEntity(entity, category)
    if not DoesEntityExist(entity) then return end

    ManagedEntities[entity] = {
        category = category or 'default',
        created = GetGameTimer(),
        model = GetEntityModel(entity),
    }
end

function UnregisterEntity(entity)
    if ManagedEntities[entity] then
        if DoesEntityExist(entity) then
            SetEntityAsMissionEntity(entity, false, true)
            DeleteEntity(entity)
        end
        ManagedEntities[entity] = nil
    end
end

function CleanupCategory(category)
    for entity, data in pairs(ManagedEntities) do
        if data.category == category then
            UnregisterEntity(entity)
        end
    end
end

function CleanupAllEntities()
    for entity, _ in pairs(ManagedEntities) do
        UnregisterEntity(entity)
    end
    ManagedEntities = {}
end

-- Critical: Clean up when resource stops
AddEventHandler('onResourceStop', function(resourceName)
    if GetCurrentResourceName() ~= resourceName then return end
    CleanupAllEntities()
end)

Network Entity Ownership

In FiveM's networked environment, every entity has an owner, the client responsible for simulating its physics and position. Network ownership determines which player's client controls the entity's movement and collision. Understanding and managing network ownership is essential for synchronized entity behavior, especially for scripts that need to control NPCs or vehicles remotely.

-- server/ownership.lua
-- Request control of a networked entity
local function RequestEntityOwnership(src, netId)
    local entity = NetworkGetEntityFromNetworkId(netId)
    if not DoesEntityExist(entity) then return false end

    -- Set the requesting player as the owner
    SetEntityRoutingBucket(entity, GetPlayerRoutingBucket(src))

    return true
end

-- client/ownership.lua
-- Request network control of an entity
local function TakeEntityControl(entity, timeout)
    timeout = timeout or 2000
    local start = GetGameTimer()

    NetworkRequestControlOfEntity(entity)

    while not NetworkHasControlOfEntity(entity) do
        Wait(100)
        NetworkRequestControlOfEntity(entity)
        if GetGameTimer() - start > timeout then
            return false
        end
    end

    return true
end

-- Usage: Move an entity you need control of
local function MoveEntity(entity, targetCoords)
    if TakeEntityControl(entity) then
        SetEntityCoords(entity, targetCoords.x, targetCoords.y, targetCoords.z, false, false, false, false)
        return true
    end

    print('[WARN] Could not get control of entity')
    return false
end

Spawning and Managing Peds

Ped (pedestrian) entities are used for NPCs, shop vendors, quest givers, and ambient characters. They require special handling because they have AI that can cause them to wander off, react to the environment, or attack players if not properly configured. Always freeze and disable AI for static NPCs.

-- client/ped_spawner.lua
local function SpawnStaticPed(modelName, coords, heading, scenario)
    local model = joaat(modelName)

    RequestModel(model)
    while not HasModelLoaded(model) do
        Wait(10)
    end

    local ped = CreatePed(0, model, coords.x, coords.y, coords.z - 1.0, heading, false, true)

    -- Make the ped static and non-interactive with ambient AI
    SetEntityAsMissionEntity(ped, true, true)
    SetBlockingOfNonTemporaryEvents(ped, true)
    SetPedFleeAttributes(ped, 0, false)
    SetPedCombatAttributes(ped, 17, true)
    SetPedDiesWhenInjured(ped, false)
    SetEntityInvincible(ped, true)
    FreezeEntityPosition(ped, true)

    -- Play a scenario animation if specified
    if scenario then
        TaskStartScenarioInPlace(ped, scenario, 0, true)
    end

    SetModelAsNoLongerNeeded(model)
    RegisterEntity(ped, 'npc')

    return ped
end

-- Spawn a shop vendor
local vendor = SpawnStaticPed(
    's_m_y_ammucity_01',
    vector3(22.0, -1105.0, 29.8),
    160.0,
    'WORLD_HUMAN_STAND_IMPATIENT'
)

Performance Tips for Entity Management

  • Limit active entities. Each entity consumes memory and render cycles. Use streaming systems to load entities when the player is nearby and unload them when they leave the area.
  • Use object pools. For frequently spawned and despawned entities like bullet casings or particle effects, reuse entity handles instead of creating and deleting new ones constantly.
  • Prefer client-side entities. If an entity only needs to be visible to one player (like a UI prop or preview object), create it as non-networked to reduce server load.
  • Always call SetModelAsNoLongerNeeded. Loaded models stay in memory until explicitly released. Forgetting this call is one of the most common causes of memory leaks in FiveM scripts.
  • Check DoesEntityExist before operations. Entities can be deleted by the game engine, other scripts, or desync. Always verify an entity still exists before trying to modify it.
  • Use entity state bags for metadata. Instead of maintaining separate lookup tables for entity data, use Entity(entity).state to store custom properties directly on the entity.

Share this article

Ready to upgrade your server?

Check out our premium FiveM scripts in the Agency Scripts store or join our Discord community for support and updates.