Compreendendo as entidades no FiveM
No FiveM, uma entidade é qualquer objeto que existe no mundo do jogo: veículos, pedestres, adereços, pickups e até mesmo o personagem do jogador. Cada entidade possui um identificador (um ID inteiro) que você usa para interagir com ela por meio de funções nativas. O gerenciamento de entidades é uma das habilidades mais fundamentais no desenvolvimento FiveM porque quase todos os scripts interagem com as entidades de alguma forma. O mau gerenciamento de entidades leva a vazamentos de memória, objetos fantasmas que persistem após a reinicialização de um recurso, dessincronização entre jogadores e, por fim, instabilidade do servidor. Este guia aborda os padrões corretos para criar, rastrear e limpar entidades.
Gerando objetos e acessórios
O principal nativo para adereços de spawn é CreateObject (ou CreateObjectNoOffset). Antes de gerar qualquer objeto, você deve solicitar o modelo e aguardar seu carregamento. Deixar de solicitar o modelo primeiro resultará em uma entidade invisível ou ausente. Sempre libere o modelo após criar o objeto para liberar memória.
-- 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)
Desova de Veículos
A geração de veículos segue um padrão semelhante, mas usa CreateVehicle e tem considerações adicionais sobre propriedade da rede, níveis de combustível e texto da placa. Sempre gere veículos no lado do servidor quando possível ou use retornos de chamada do lado do servidor para validar solicitações de criação de veículos dos 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
Rastreamento e limpeza de entidades
O aspecto mais crítico do gerenciamento de entidades é rastrear cada entidade que seu script cria e limpá-las quando o recurso é interrompido. Sem a limpeza adequada, as entidades persistem no mundo do jogo como órfãs que desperdiçam memória e renderizam recursos. Use uma tabela de rastreamento para armazenar todos os identificadores de entidade e registrar um manipulador onResourceStop que os remove.
-- 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)
Propriedade da entidade de rede
No ambiente de rede do FiveM, cada entidade possui um proprietário, o cliente responsável por simular sua física e posição. A propriedade da rede determina qual cliente do jogador controla o movimento e a colisão da entidade. Compreender e gerenciar a propriedade da rede é essencial para o comportamento sincronizado da entidade, especialmente para scripts que precisam controlar NPCs ou veículos remotamente.
-- 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
Gerando e Gerenciando Peds
Entidades Ped (pedestres) são usadas para NPCs, vendedores de lojas, entregadores de missões e personagens ambientais. Eles exigem tratamento especial porque possuem IA que pode fazer com que eles se desviem, reajam ao ambiente ou ataquem jogadores se não forem configurados corretamente. Sempre congele e desative a IA para NPCs estáticos.
-- 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'
)
Dicas de desempenho para gerenciamento de entidades
- Limitar entidades ativas. Cada entidade consome memória e ciclos de renderização. Use sistemas de streaming para carregar entidades quando o jogador estiver por perto e descarregá-las quando ele sair da área.
- Use pools de objetos. Para entidades geradas e desaparecidas com frequência, como invólucros de balas ou efeitos de partículas, reutilize identificadores de entidade em vez de criar e excluir novos constantemente.
- Prefira entidades do lado do cliente. Se uma entidade só precisa ser visível para um jogador (como um suporte de interface de usuário ou um objeto de visualização), crie-a como sem rede para reduzir a carga do servidor.
- Sempre chame SetModelAsNoLongerNeeded. Os modelos carregados permanecem na memória até serem liberados explicitamente. Esquecer esta chamada é uma das causas mais comuns de vazamentos de memória em scripts FiveM.
- Verifique DoesEntityExist antes das operações. As entidades podem ser excluídas pelo mecanismo de jogo, outros scripts ou dessincronização. Sempre verifique se uma entidade ainda existe antes de tentar modificá-la.
- Use pacotes de estado de entidade para metadados. Em vez de manter tabelas de pesquisa separadas para dados de entidade, use
Entity(entity).statepara armazenar propriedades personalizadas diretamente na entidade.
