Guia 2026-04-20

Sistemas Avançados de IA e Comportamento de NPCs para FiveM

TDYSKY

TDYSKY

Fundador & Lead Developer na Agency Scripts

Por que a IA personalizada do NPC é importante

A população ambiental padrão em GTA V foi projetada para uma experiência de jogador único, não para um RPG envolvente. Os NPCs andam sem rumo, reagem de forma imprevisível às ações dos jogadores e não têm noção do contexto do RPG ao seu redor. Um balconista fica imóvel, um barman não faz nada atrás do balcão e os pedestres ignoram cenas de crimes ativas. A IA personalizada do NPC transforma esses recortes de papelão em personagens verossímeis que melhoram a imersão. Um lojista bem preparado fica atrás da caixa registradora e cumprimenta os clientes. Os guardas de segurança patrulham rotas designadas e reagem aos tiros. Os membros de gangues defendem seu território e fogem quando estão em desvantagem. Neste guia, cobriremos as principais funções nativas do GTA V para controlar o comportamento dos NPCs, construir um sistema de sequência de tarefas, implementar rotas de patrulha, configurar grupos de relacionamento para dinâmicas de combate realistas e criar uma população ambiental baseada em cenários que dá vida ao mundo do seu servidor.

Sequências de tarefas: a base do comportamento do NPC

As sequências de tarefas são listas ordenadas de ações que um ped executa em sequência. Ao contrário das chamadas de tarefas individuais que se interrompem, uma sequência de tarefas garante que cada ação seja concluída antes do início da próxima. Isto é essencial para comportamentos complexos, como um guarda de segurança que caminha até uma porta, abre-a, entra numa sala e depois assume uma posição de guarda permanente. Sem sequências de tarefas, você precisaria de rastreamento manual de estado com temporizadores, que é frágil e difícil de manter. As funções nativas OpenSequenceTask e CloseSequenceTask criam um identificador de sequência que pode ser atribuído a qualquer ped via TaskPerformSequence.

-- Create a task sequence for a shopkeeper NPC
local function CreateShopkeeperRoutine(ped, counterCoords, counterHeading)
    local seq = 0
    -- Open a new sequence (pass 0 as reference, it returns a handle)
    OpenSequenceTask(seq)

    -- Walk to the counter position
    TaskGoStraightToCoord(0, counterCoords.x, counterCoords.y, counterCoords.z, 1.0, -1, counterHeading, 0.5)

    -- Turn to face customers
    TaskAchieveHeading(0, counterHeading, 2000)

    -- Play idle animation at counter
    TaskStartScenarioInPlace(0, 'PROP_HUMAN_SHOP_INTERACT', 0, true)

    CloseSequenceTask(seq)

    -- Assign sequence to the ped
    TaskPerformSequence(ped, seq)
    ClearSequenceTask(seq)
end

-- Usage
local shopkeeper = CreateNPC('s_m_m_shopkeep_01', vector3(25.7, -1347.3, 29.5))
CreateShopkeeperRoutine(shopkeeper, vector3(25.7, -1347.3, 29.5), 270.0)

O Sistema de Cenário

Cenários são loops de animação predefinidos que fazem com que os NPCs pareçam estar realizando atividades reais. GTA V inclui centenas de cenários integrados, desde fumar e beber até usar um caixa eletrônico, trabalhar em uma mesa ou fazer flexões em um parque. Usando TaskStartScenarioInPlace para atividades estacionárias ou TaskStartScenarioAtPosition para atividades específicas de posição, você pode fazer com que os NPCs executem ações contextualmente apropriadas. A chave para uma IA ambiental confiável é combinar cenários com locais. Um bartender deve usar WORLD_HUMAN_BARTENDER, um mecânico em uma garagem deve usar WORLD_HUMAN_WELDING e NPCs fora de uma boate devem usar WORLD_HUMAN_SMOKING ou WORLD_HUMAN_STAND_MOBILE.

-- Scenario-based ambient NPCs with location awareness
local AmbientNPCs = {
    {
        model = 'a_m_y_barman_01',
        coords = vector4(-561.2, 286.3, 82.2, 270.0),
        scenario = 'WORLD_HUMAN_BARTENDER',
        invincible = true,
    },
    {
        model = 's_m_y_doorman_01',
        coords = vector4(-560.8, 293.5, 82.2, 180.0),
        scenario = 'WORLD_HUMAN_GUARD_STAND',
        weapon = 'WEAPON_PISTOL',
    },
    {
        model = 'a_f_y_business_01',
        coords = vector4(-555.0, 290.1, 82.2, 90.0),
        scenario = 'WORLD_HUMAN_STAND_MOBILE',
    },
    {
        model = 's_m_y_ammucity_01',
        coords = vector4(22.0, -1105.0, 29.8, 160.0),
        scenario = 'PROP_HUMAN_SHOP_INTERACT',
        invincible = true,
    },
}

local spawnedPeds = {}

local function SpawnAmbientNPCs()
    for _, data in ipairs(AmbientNPCs) do
        local hash = GetHashKey(data.model)
        RequestModel(hash)
        while not HasModelLoaded(hash) do Wait(10) end

        local ped = CreatePed(4, hash, data.coords.x, data.coords.y, data.coords.z, data.coords.w, false, true)
        SetEntityInvincible(ped, data.invincible or false)
        SetBlockingOfNonTemporaryEvents(ped, true)
        FreezeEntityPosition(ped, false)

        if data.weapon then
            GiveWeaponToPed(ped, GetHashKey(data.weapon), 250, false, true)
        end

        TaskStartScenarioInPlace(ped, data.scenario, 0, true)
        SetModelAsNoLongerNeeded(hash)
        table.insert(spawnedPeds, ped)
    end
end

Rotas de patrulha e navegação por pontos de referência

As rotas de patrulha fazem com que os NPCs se movam entre pontos de referência predefinidos em um loop, o que é essencial para guardas de segurança, policiais e sentinelas de gangues. A abordagem mais simples usa uma sequência de tarefas repetida que leva o ped até cada waypoint, espera brevemente e depois passa para o próximo. Uma abordagem mais avançada usa uma máquina de estado que rastreia o índice do waypoint atual e lida com as interrupções normalmente. Quando um guarda patrulha detecta uma ameaça, ele deve sair da patrulha, lidar com a ameaça e então retomar a patrulha a partir do seu último waypoint conhecido, em vez de reiniciar desde o início. O sistema de patrulha também deve suportar diferentes velocidades de movimento, ações de cenário opcionais em cada waypoint e tempos de espera configuráveis.

-- Patrol route system with state management
local PatrolRoutes = {
    police_station_exterior = {
        speed = 1.0, -- walking speed
        waypoints = {
            { coords = vector3(441.0, -982.0, 30.7), heading = 90.0,  wait = 5000, scenario = 'WORLD_HUMAN_GUARD_STAND' },
            { coords = vector3(451.0, -982.0, 30.7), heading = 0.0,   wait = 3000 },
            { coords = vector3(451.0, -993.0, 30.7), heading = 270.0, wait = 5000, scenario = 'WORLD_HUMAN_COP_IDLES' },
            { coords = vector3(441.0, -993.0, 30.7), heading = 180.0, wait = 3000 },
        }
    },
}

local function StartPatrol(ped, routeName)
    local route = PatrolRoutes[routeName]
    if not route then return end

    CreateThread(function()
        local waypointIndex = 1
        while DoesEntityExist(ped) and not IsEntityDead(ped) do
            local wp = route.waypoints[waypointIndex]

            -- Walk to waypoint
            TaskGoStraightToCoord(ped, wp.coords.x, wp.coords.y, wp.coords.z, route.speed, -1, wp.heading, 0.5)

            -- Wait until ped reaches destination
            while DoesEntityExist(ped) and not IsEntityDead(ped) do
                local dist = #(GetEntityCoords(ped) - wp.coords)
                if dist < 1.5 then break end
                Wait(500)
            end

            -- Face the correct direction
            TaskAchieveHeading(ped, wp.heading, 1500)
            Wait(1500)

            -- Play scenario at waypoint if defined
            if wp.scenario then
                TaskStartScenarioInPlace(ped, wp.scenario, 0, false)
                Wait(wp.wait or 5000)
                ClearPedTasks(ped)
            else
                Wait(wp.wait or 3000)
            end

            -- Move to next waypoint (loop back to 1)
            waypointIndex = waypointIndex % #route.waypoints + 1
        end
    end)
end

Grupos de Relacionamento e Comportamento de Combate

Os grupos de relacionamento determinam como os NPCs reagem uns aos outros e aos jogadores. Por padrão, todos os NPCs estão em grupos de relacionamento genéricos com posição neutra, e é por isso que os membros de gangues não lutam entre si e os policiais não perseguem automaticamente os criminosos. Grupos de relacionamento personalizados permitem criar dinâmicas de facção onde a polícia é hostil a criminosos armados, gangues rivais atacam umas às outras e os civis fogem do combate. O nativo SetRelationshipBetweenGroups aceita níveis de relacionamento de 0 (companheiro) a 3 (neutro) a 5 (ódio), controlando se os peds vão brigar, fugir ou ignorar uns aos outros. Este sistema é a espinha dorsal da criação de guerras entre facções, disputas territoriais e respostas de aplicação da lei realistas.

-- Relationship group setup for faction-based AI
local RelGroups = {}

local function InitRelationshipGroups()
    -- Create custom groups
    AddRelationshipGroup('GANG_BALLAS', RelGroups)
    AddRelationshipGroup('GANG_FAMILIES', RelGroups)
    AddRelationshipGroup('GANG_VAGOS', RelGroups)
    AddRelationshipGroup('POLICE_CUSTOM', RelGroups)
    AddRelationshipGroup('CIVILIAN', RelGroups)

    -- Gangs hate rival gangs
    SetRelationshipBetweenGroups(5, GetHashKey('GANG_BALLAS'), GetHashKey('GANG_FAMILIES'))
    SetRelationshipBetweenGroups(5, GetHashKey('GANG_FAMILIES'), GetHashKey('GANG_BALLAS'))
    SetRelationshipBetweenGroups(5, GetHashKey('GANG_BALLAS'), GetHashKey('GANG_VAGOS'))
    SetRelationshipBetweenGroups(5, GetHashKey('GANG_VAGOS'), GetHashKey('GANG_BALLAS'))

    -- Police dislike all gangs
    SetRelationshipBetweenGroups(4, GetHashKey('POLICE_CUSTOM'), GetHashKey('GANG_BALLAS'))
    SetRelationshipBetweenGroups(4, GetHashKey('POLICE_CUSTOM'), GetHashKey('GANG_FAMILIES'))
    SetRelationshipBetweenGroups(4, GetHashKey('POLICE_CUSTOM'), GetHashKey('GANG_VAGOS'))

    -- Civilians flee from gangs
    SetRelationshipBetweenGroups(1, GetHashKey('CIVILIAN'), GetHashKey('GANG_BALLAS'))
    SetRelationshipBetweenGroups(1, GetHashKey('CIVILIAN'), GetHashKey('GANG_FAMILIES'))
end

-- Assign a ped to a relationship group
local function SetPedFaction(ped, faction)
    local groupHash = GetHashKey(faction)
    SetPedRelationshipGroupHash(ped, groupHash)
end

Comportamento de fuga e luta

Controlar como os NPCs respondem às ameaças é fundamental para cenários de combate envolventes. O comportamento padrão do GTA V é caótico: alguns NPCs correm, alguns se encolhem e NPCs armados podem ou não se envolver. O comportamento de combate personalizado usa atributos de combate e sinalizadores de configuração para criar respostas previsíveis e adequadas à função. Um membro de uma gangue deve ficar de pé e lutar com pouca precisão, um guarda de segurança treinado deve se proteger e atacar com precisão moderada, um civil deve fugir imediatamente e um personagem VIP deve se agachar e pedir ajuda. O nativo SetPedCombatAttributes controla comportamentos individuais, como se o ped pode usar cobertura, disparar às cegas, investigar sons ou fugir quando estiver desarmado.

-- Combat behavior presets
local CombatPresets = {
    gang_member = function(ped)
        SetPedCombatAttributes(ped, 46, true)   -- Can fight armed peds on foot
        SetPedCombatAttributes(ped, 5, true)    -- Can use cover
        SetPedCombatAttributes(ped, 2, true)    -- Can do drivebys
        SetPedCombatAbility(ped, 1)              -- Average combat ability
        SetPedCombatRange(ped, 1)                -- Medium range
        SetPedAccuracy(ped, 30)                  -- Poor accuracy
        SetPedFleeAttributes(ped, 0, false)      -- Don't flee
        SetPedCombatMovement(ped, 2)             -- Offensive movement
    end,

    security_guard = function(ped)
        SetPedCombatAttributes(ped, 46, true)
        SetPedCombatAttributes(ped, 5, true)    -- Use cover
        SetPedCombatAttributes(ped, 21, true)   -- Investigate dead peds
        SetPedCombatAbility(ped, 2)              -- Professional
        SetPedCombatRange(ped, 2)                -- Long range
        SetPedAccuracy(ped, 60)                  -- Good accuracy
        SetPedCombatMovement(ped, 1)             -- Defensive movement
    end,

    civilian = function(ped)
        SetPedFleeAttributes(ped, 2, true)       -- Flee immediately
        SetPedCombatAttributes(ped, 17, true)    -- Can be scared
        SetPedCombatAttributes(ped, 46, false)   -- Cannot fight
        SetPedCombatAbility(ped, 0)              -- Poor fighter
    end,
}

-- Apply a combat preset
local function ApplyCombatPreset(ped, presetName)
    local preset = CombatPresets[presetName]
    if preset then preset(ped) end
end

Gerenciando o ciclo de vida e o desempenho do NPC

A geração de NPCs tem um impacto direto no desempenho do servidor e do cliente. Cada ped ativo consome ciclos de CPU para processamento de IA, simulação física e sincronização de rede. Uma implementação ingênua que gera todos os NPCs na inicialização do servidor e os mantém vivos permanentemente prejudicará o desempenho à medida que a população cresce. A abordagem correta usa o spawn baseado em proximidade, onde os NPCs só são criados quando um jogador está dentro de um alcance configurável e desaparecem quando todos os jogadores deixam a área. Use um sistema de particionamento espacial que divida o mapa em zonas e processe apenas a lógica do NPC para zonas com jogadores próximos. Armazene em cache os identificadores e reutilize-os quando possível, em vez de excluir e recriar NPCs toda vez que um jogador entra e sai de uma zona. Defina SetEntityAsMissionEntity como falso e use sinalizadores de criação com reconhecimento de rede para que o mecanismo de jogo possa gerenciar naturalmente o ciclo de vida do ped dentro de seu próprio sistema de streaming.

-- Proximity-based NPC spawner with zone management
local SPAWN_RANGE = 80.0
local DESPAWN_RANGE = 120.0
local activeZones = {}

CreateThread(function()
    while true do
        local playerCoords = GetEntityCoords(PlayerPedId())

        for zoneName, zone in pairs(NPCZones) do
            local dist = #(playerCoords - zone.center)

            if dist < SPAWN_RANGE and not activeZones[zoneName] then
                -- Spawn zone NPCs
                activeZones[zoneName] = SpawnZoneNPCs(zone)
            elseif dist > DESPAWN_RANGE and activeZones[zoneName] then
                -- Despawn zone NPCs
                for _, ped in ipairs(activeZones[zoneName]) do
                    if DoesEntityExist(ped) then
                        DeleteEntity(ped)
                    end
                end
                activeZones[zoneName] = nil
            end
        end

        Wait(2000) -- Check every 2 seconds
    end
end)

Juntando tudo

Um sistema NPC AI de produção combina todos esses elementos em uma estrutura coesa orientada por dados de configuração. Cada definição de NPC em sua configuração especifica o modelo, local de spawn, tipo de comportamento (estacionário, patrulhamento, guarda), predefinição de combate, grupo de relacionamento e qualquer cenário ou sequência de tarefas a ser executada. A estrutura carrega essas definições, gerencia o surgimento e o desaparecimento com base na proximidade do jogador, lida com transições de estado quando os NPCs são interrompidos por combate ou interação do jogador e limpa recursos quando os NPCs não são mais necessários. O princípio de design mais importante é separar os dados da lógica: todo o comportamento do NPC deve ser configurável sem alterações de código, permitindo que os proprietários de servidores adicionem novos NPCs, modifiquem rotas de patrulha e ajustem parâmetros de combate apenas através de arquivos de configuração. Essa abordagem baseada em dados significa que seu sistema de IA pode escalar desde um punhado de NPCs de missão até centenas de personagens ambientais que povoam uma cidade inteira sem alterações arquitetônicas no código subjacente.

Partilhar este artigo

Pronto para melhorar o teu servidor?

Explora os nossos scripts FiveM premium na loja Agency Scripts ou junta-te à nossa comunidade no Discord para suporte e atualizações.