Volver al blog
Tutorial6 min de lectura

Gestión de entidades en FiveM: controla peatones, vehículos y objetos

Domina la gestión de entidades en FiveM. Crea, asigna y elimina peatones, vehículos y objetos para mantener tu servidor estable y rápido.

Agency Scripts

Fundador y desarrollador principal en Agency Scripts

Entender las entidades en FiveM

En FiveM, una entidad es cualquier objeto que existe en el mundo del juego: vehículos, peds, objetos, objetos recogibles e incluso el personaje del jugador. Cada entidad tiene un identificador (un ID entero) que se usa para interactuar con ella mediante funciones nativas. La gestión de entidades es una de las habilidades más fundamentales para desarrollar en FiveM, porque casi todos los scripts interactúan con entidades de alguna forma. Una gestión deficiente de las entidades provoca fugas de memoria, objetos fantasma que permanecen tras reiniciar un recurso, desincronización entre jugadores y, en última instancia, inestabilidad del servidor. Esta guía cubre los patrones correctos para crear, registrar y eliminar entidades.

Generar objetos y props

La función nativa principal para generar props es CreateObject (o CreateObjectNoOffset). Antes de generar cualquier objeto, debes solicitar el modelo y esperar a que se cargue. Si no solicitas el modelo primero, la entidad será invisible o no aparecerá. Libera siempre el modelo después de crear el objeto para liberar memoria.

-- 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)

Generar vehículos

La generación de vehículos sigue un patrón similar, pero usa CreateVehicle y requiere tener en cuenta otros aspectos, como la propiedad de red, el nivel de combustible y el texto de la matrícula. Genera siempre los vehículos en el servidor cuando sea posible o usa callbacks del servidor para validar las solicitudes de creación de vehículos de los clientes.

-- 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

Seguimiento y limpieza de entidades

El aspecto más importante de la gestión de entidades es registrar todas las entidades que crea tu script y eliminarlas cuando el recurso se detenga. Sin una limpieza adecuada, las entidades permanecen en el mundo del juego como huérfanas y consumen memoria y recursos de renderizado. Usa una tabla de seguimiento para guardar todos los identificadores de entidades y registra un controlador de onResourceStop que las elimine.

-- 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)

Propiedad de las entidades de red

En el entorno de red de FiveM, cada entidad tiene un propietario: el cliente responsable de simular su física y posición. La propiedad de red determina qué cliente de jugador controla el movimiento y las colisiones de la entidad. Entender y gestionar la propiedad de red es esencial para sincronizar el comportamiento de las entidades, especialmente en scripts que necesitan controlar NPC o vehículos de forma remota.

-- 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

Generar y gestionar peds

Las entidades ped (peatón) se usan para NPC, vendedores, personajes que dan misiones y personajes ambientales. Requieren un tratamiento especial, porque su IA puede hacer que se alejen, reaccionen al entorno o ataquen a los jugadores si no se configuran correctamente. Para los NPC estáticos, congélalos y desactiva siempre su IA.

-- 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'
)

Consejos de rendimiento para gestionar entidades

  • Limita las entidades activas. Cada entidad consume memoria y ciclos de renderizado. Usa sistemas de streaming para cargar entidades cuando el jugador esté cerca y descargarlas cuando se aleje de la zona.
  • Usa pools de objetos. Para entidades que aparecen y desaparecen con frecuencia, como casquillos de bala o efectos de partículas, reutiliza los identificadores de entidad en lugar de crear y eliminar entidades nuevas constantemente.
  • Prefiere las entidades del lado del cliente. Si una entidad solo debe ser visible para un jugador, como un objeto de interfaz o de vista previa, créala sin conexión de red para reducir la carga del servidor.
  • Llama siempre a SetModelAsNoLongerNeeded. Los modelos cargados permanecen en la memoria hasta que se liberan explícitamente. Olvidar esta llamada es una de las causas más comunes de fugas de memoria en scripts de FiveM.
  • Comprueba DoesEntityExist antes de realizar operaciones. El motor del juego, otros scripts o la desincronización pueden eliminar entidades. Comprueba siempre que una entidad siga existiendo antes de intentar modificarla.
  • Usa state bags de entidades para los metadatos. En lugar de mantener tablas de búsqueda independientes para los datos de las entidades, usa Entity(entity).state para guardar propiedades personalizadas directamente en la entidad.

¿Listo para empezar?

Consigue los scripts en nuestra tienda, o únete al Discord para soporte, novedades y un adelanto de lo que viene.