Zurück zum Blog
Tutorial5 Min. Lesezeit

FiveM-Entitätenverwaltung: Peds, Fahrzeuge und Objekte verwalten

Lerne die Entitätenverwaltung in FiveM: Erzeugen, Besitz, Ausblenden und Bereinigen von Peds, Fahrzeugen und Objekten für einen stabilen, schnellen Server.

Agency Scripts

Gründer und leitender Entwickler bei Agency Scripts

Entities in FiveM verstehen

In FiveM ist ein Entity jedes Objekt, das in der Spielwelt existiert: Fahrzeuge, Peds, Props, Pickups und sogar der Spielercharakter. Jedes Entity hat einen Handle, eine ganzzahlige ID, mit der du über Native-Funktionen darauf zugreifst. Entity-Management gehört zu den grundlegendsten Fähigkeiten bei der Entwicklung für FiveM, denn fast jedes Script interagiert auf irgendeine Weise mit Entities. Schlechtes Entity-Management führt zu Speicherlecks, Geisterobjekten, die nach einem Neustart einer Resource bestehen bleiben, Desync zwischen Spielern und letztlich zu Instabilität des Servers. In diesem Leitfaden geht es um die richtigen Vorgehensweisen zum Erstellen, Erfassen und Bereinigen von Entities.

Objekte und Props spawnen

Das wichtigste Native zum Spawnen von Props ist CreateObject (oder CreateObjectNoOffset). Bevor du ein Objekt spawnst, musst du das Modell anfordern und warten, bis es geladen ist. Wenn du das Modell nicht zuerst anforderst, ist das Entity unsichtbar oder fehlt. Gib das Modell nach dem Erstellen des Objekts immer wieder frei, um Speicher freizugeben.

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

Fahrzeuge spawnen

Zum Spawnen von Fahrzeugen wird ähnlich vorgegangen, aber dafür kommt CreateVehicle zum Einsatz und es gibt zusätzliche Aspekte bei Netzwerkbesitz, Tankfüllstand und Kennzeichentext. Spawne Fahrzeuge nach Möglichkeit immer serverseitig oder verwende serverseitige Callbacks, um Anfragen von Clients zum Erstellen von Fahrzeugen zu validieren.

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

Entities erfassen und bereinigen

Der wichtigste Aspekt beim Entity-Management ist, jedes von deinem Script erstellte Entity zu erfassen und es beim Stoppen der Resource zu bereinigen. Ohne ordnungsgemäße Bereinigung bleiben Entities als verwaiste Objekte in der Spielwelt bestehen und verbrauchen Speicher sowie Render-Ressourcen. Verwende eine Tracking-Tabelle, um alle Entity-Handles zu speichern, und registriere einen onResourceStop Handler, der sie entfernt.

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

Netzwerkbesitz von Entities

In der vernetzten Umgebung von FiveM hat jedes Entity einen Besitzer, also den Client, der für die Simulation seiner Physik und Position zuständig ist. Der Netzwerkbesitz bestimmt, welcher Client die Bewegung und Kollision des Entities steuert. Netzwerkbesitz zu verstehen und zu verwalten ist für synchronisiertes Verhalten von Entities unverzichtbar, besonders bei Scripts, die NPCs oder Fahrzeuge aus der Ferne steuern müssen.

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

Peds spawnen und verwalten

Ped-Entities (Passanten) werden für NPCs, Händler, Questgeber und Umgebungscharaktere verwendet. Sie erfordern besondere Behandlung, weil ihre KI dazu führen kann, dass sie umherwandern, auf ihre Umgebung reagieren oder Spieler angreifen, wenn sie nicht richtig konfiguriert sind. Fixiere statische NPCs immer und deaktiviere ihre KI.

-- 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-Tipps für Entity-Management

  • Begrenze die Anzahl aktiver Entities. Jedes Entity verbraucht Speicher und Render-Ressourcen. Nutze Streaming-Systeme, um Entities zu laden, wenn sich der Spieler in der Nähe befindet, und sie zu entladen, wenn er das Gebiet verlässt.
  • Verwende Objekt-Pools. Bei häufig gespawnten und wieder entfernten Entities wie Patronenhülsen oder Partikeleffekten solltest du Entity-Handles wiederverwenden, statt ständig neue zu erstellen und zu löschen.
  • Bevorzuge clientseitige Entities. Wenn ein Entity nur für einen Spieler sichtbar sein muss, etwa ein UI-Prop oder ein Vorschauobjekt, erstelle es ohne Netzwerk, um die Serverlast zu reduzieren.
  • Rufe immer SetModelAsNoLongerNeeded auf. Geladene Modelle bleiben im Speicher, bis sie ausdrücklich freigegeben werden. Diesen Aufruf zu vergessen, ist eine der häufigsten Ursachen für Speicherlecks in FiveM-Scripts.
  • Prüfe vor Operationen DoesEntityExist. Entities können von der Spiel-Engine, anderen Scripts oder durch Desync gelöscht werden. Prüfe immer, ob ein Entity noch existiert, bevor du versuchst, es zu verändern.
  • Verwende Entity-State-Bags für Metadaten. Verwende statt separater Lookup-Tabellen für Entity-Daten Entity(entity).state und speichere benutzerdefinierte Eigenschaften direkt am Entity.

Bereit loszulegen?

Hol dir die Scripts in unserem Shop, oder komm auf den Discord für Support, Updates und einen Blick auf das, was kommt.