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).stateund speichere benutzerdefinierte Eigenschaften direkt am Entity.