Comprendre les entités dans FiveM
Dans FiveM, une entité est n'importe quel objet qui existe dans le monde du jeu : véhicules, véhicules, accessoires, camionnettes et même le personnage du joueur. Chaque entité possède un handle (un identifiant entier) que tu utilises pour interagir avec elle via des fonctions natives. La gestion des entités est l'une des compétences les plus fondamentales du développement FiveM, car presque tous les scripts interagissent avec les entités d'une manière ou d'une autre. Une mauvaise gestion des entités entraîne des fuites de mémoire, des objets fantômes qui persistent après le redémarrage d'une ressource, une désynchronisation entre les joueurs et, finalement, une instabilité du serveur. Ce guide couvre les modèles corrects pour la création, le suivi et le nettoyage des entités.
Génération d'objets et d'accessoires
Le principal indigène pour les accessoires de frai est CreateObject (ou CreateObjectNoOffset). Avant de générer un objet, tu dois demander le modèle et attendre qu'il se charge. Ne pas demander le modèle au préalable entraînera une entité invisible ou manquante. Libérez toujours le modèle après avoir créé l'objet pour libérer de la mémoire.
-- 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)
Véhicules engendrés
L'apparition des véhicules suit un schéma similaire mais utilise CreateVehicle et comporte des considérations supplémentaires concernant la propriété du réseau, les niveaux de carburant et le texte de la plaque. Générez toujours des véhicules côté serveur lorsque cela est possible, ou utilisez des rappels côté serveur pour valider les demandes de création de véhicules des 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
Suivi et nettoyage des entités
L'aspect le plus critique de la gestion des entités consiste à suivre chaque entité créée par ton script et à les nettoyer lorsque la ressource s'arrête. Sans un nettoyage approprié, les entités persistent dans le monde du jeu comme des orphelines qui gaspillent de la mémoire et restituent des ressources. Utilisez une table de suivi pour stocker tous les descripteurs d'entité et enregistrer un onResourceStop gestionnaire qui les supprime.
-- 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)
Propriété de l'entité réseau
Dans l'environnement réseau du FiveM, chaque entité a un propriétaire, le client responsable de la simulation de sa physique et de sa position. La propriété du réseau détermine quel client du joueur contrôle le mouvement et la collision de l'entité. Comprendre et gérer la propriété du réseau est essentiel pour le comportement synchronisé des entités, en particulier pour les scripts qui doivent contrôler les PNJ ou les véhicules à distance.
-- 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
Apparition et gestion des Peds
Les entités Ped (piéton) sont utilisées pour les PNJ, les vendeurs de magasins, les donneurs de quêtes et les personnages ambiants. Ils nécessitent un traitement spécial car ils disposent d'une IA qui peut les faire s'éloigner, réagir à l'environnement ou attaquer les joueurs s'ils ne sont pas correctement configurés. Gèlez et désactivez toujours l’IA pour les PNJ statiques.
-- 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'
)
Conseils de performances pour la gestion des entités
- Limiter les entités actives. Chaque entité consomme de la mémoire et des cycles de rendu. Utilisez des systèmes de streaming pour charger des entités lorsque le joueur est à proximité et les décharger lorsqu'il quitte la zone.
- Utilisez des pools d'objets. Pour les entités fréquemment générées et supprimées, comme les douilles de balle ou les effets de particules, réutilisez les poignées d'entité au lieu d'en créer et d'en supprimer constamment de nouvelles.
- Préférez les entités côté client. Si une entité ne doit être visible que par un seul joueur (comme un accessoire d'interface utilisateur ou un objet d'aperçu), créez-la comme non en réseau pour réduire la charge du serveur.
- Appelez toujours SetModelAsNoLongerNeeded. Les modèles chargés restent en mémoire jusqu'à ce qu'ils soient explicitement publiés. L'oubli de cet appel est l'une des causes les plus courantes de fuites de mémoire dans les scripts FiveM.
- Vérifiez DoesEntityExist avant les opérations. Les entités peuvent être supprimées par le moteur de jeu, d'autres scripts ou par désynchronisation. Vérifiez toujours qu'une entité existe toujours avant d'essayer de la modifier.
- Utilisez des sacs d'état d'entité pour les métadonnées. Au lieu de conserver des tables de recherche distinctes pour les données d'entité, utilisez
Entity(entity).statepour stocker les propriétés personnalisées directement sur l'entité.
