Bloga Geri Dön
Guide8 dk. okuma

FiveM AI NPC Davranışı: Daha Akıllı NPC'ler Kodlama

FiveM NPC'lerinizin canlı hissettirmesini sağlayın. Daha sürükleyici rol yapma dünyaları oluşturmak için görev dizileri, yol bulma, tepkisel diyaloglar ve AI ipuçları.

Agency Scripts

Agency Scripts Kurucusu ve Baş Geliştiricisi

Özel NPC Yapay Zekası Neden Önemlidir?

GTA V'teki varsayılan çevre nüfusu tek oyunculu bir deneyim için tasarlanmıştır, sürükleyici roleplay için değil. NPC'ler amaçsızca yürür, oyuncu eylemlerine öngörülemez tepkiler verir ve çevrelerindeki roleplay bağlamından habersizdir. Bir mağaza tezgahtarı hareketsiz durur, bir barmen tezgahın arkasında hiçbir şey yapmaz ve yayalar aktif suç mahallerini görmezden gelir. Özel NPC yapay zekası, bu karton figürleri atmosferi zenginleştiren inandırıcı karakterlere dönüştürür. İyi scriptlenmiş bir dükkan sahibi kasanın arkasında durur ve müşterileri selamlar. Güvenlik görevlileri belirlenen rotalarda devriye gezer ve silah seslerine tepki verir. Çete üyeleri kendi bölgelerini savunur ve sayıca yetersiz kaldıklarında kaçar. Bu rehberde, NPC davranışını kontrol etmek için temel GTA V native fonksiyonlarını ele alacak, bir görev dizisi sistemi oluşturacak, devriye rotaları uygulayacak, gerçekçi çatışma dinamikleri için ilişki gruplarını yapılandıracak ve sunucunuzun dünyasına hayat veren senaryo tabanlı çevre nüfusu oluşturacağız.

Task Sequences: NPC Davranışının Temeli

Task sequence'lar, bir ped'in sırayla yürüttüğü eylemlerin sıralı listeleridir. Birbirini kesen tekil task çağrılarının aksine, bir task sequence sonraki başlamadan önce her eylemin tamamlanmasını garanti eder. Bu, bir kapıya yürüyen, kapıyı açan, odaya giren ve ardından ayakta nöbet tutan bir güvenlik görevlisi gibi karmaşık davranışlar için gereklidir. Task sequence'lar olmadan, zamanlayıcılarla manuel durum takibi yapmanız gerekir: bu da kırılgandır ve sürdürülmesi zordur. Native OpenSequenceTask ve CloseSequenceTask functions, şunun aracılığıyla herhangi bir ped'e atanabilen bir sequence handle oluşturur: 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)

Senaryo Sistemi

Senaryolar, NPC'lerin gerçek aktiviteler gerçekleştiriyormuş gibi görünmesini sağlayan önceden tanımlanmış animasyon döngüleridir. GTA V, sigara ve içki içmekten ATM kullanmaya, masada çalışmaya veya parkta şınav çekmeye kadar yüzlerce yerleşik senaryo içerir. Kullanımı TaskStartScenarioInPlace sabit aktiviteler için veya TaskStartScenarioAtPosition konuma özel olanlar içinse NPC'lerin bağlama uygun eylemler gerçekleştirmesini sağlayabilirsiniz. İnandırıcı bir ortam yapay zekasının anahtarı, senaryoları konumlarla eşleştirmektir. Bir barmen şunu kullanmalıdır: WORLD_HUMAN_BARTENDER, bir garajdaki mekanik şunu kullanmalıdır: WORLD_HUMAN_WELDING, ve bir gece kulübünün dışındaki NPC'ler şunu kullanmalıdır: WORLD_HUMAN_SMOKING veya 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

Devriye Rotaları ve Yol Noktası Navigasyonu

Devriye rotaları, NPC'lerin önceden tanımlanmış yol noktaları arasında döngü halinde hareket etmesini sağlar; bu durum güvenlik görevlileri, polis memurları ve çete nöbetçileri için vazgeçilmezdir. En basit yaklaşım, ped'i her yol noktasına yürüten, kısa bir süre bekleyen ve ardından bir sonrakine geçen tekrarlayan bir görev dizisi kullanır. Daha gelişmiş bir yaklaşım ise geçerli yol noktası dizinini takip eden ve kesintileri sorunsuz bir şekilde yöneten bir durum makinesi (state machine) kullanır. Devriye gezen bir muhafız bir tehdit algıladığında devriyeden ayrılmalı, tehdidi bertaraf etmeli ve en baştan başlamak yerine bilinen son yol noktasından devriyesine devam etmelidir. Devriye sistemi ayrıca farklı hareket hızlarını, her yol noktasında isteğe bağlı senaryo eylemlerini ve yapılandırılabilir bekleme sürelerini desteklemelidir.

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

İlişki Grupları ve Çatışma Davranışı

İlişki grupları, NPC'lerin birbirlerine ve oyunculara nasıl tepki vereceğini belirler. Varsayılan olarak tüm NPC'ler nötr duruşa sahip genel ilişki gruplarındadır, bu nedenle çete üyeleri birbirleriyle savaşmaz ve polisler suçluları otomatik olarak kovalamaz. Özel ilişki grupları, polisin silahlı suçlulara düşman olduğu, rakip çetelerin birbirini gördüğü anda saldırdığı ve sivillerin çatışmadan kaçtığı grup dinamikleri oluşturmanıza olanak tanır. Bu SetRelationshipBetweenGroups native'i, 0 (yoldaş), 3 (nötr) ve 5 (düşman) arasındaki ilişki seviyelerini kabul ederek ped'lerin birbirleriyle savaşıp savaşmayacağını, kaçacağını veya birbirlerini görmezden gelip gelmeyeceğini denetler. Bu sistem; gerçekçi grup çatışmaları, bölge anlaşmazlıkları ve kolluk kuvveti müdahaleleri oluşturmanın temel direğidir.

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

Kaçma ve Savaşma Davranışı

NPC'lerin tehditlere nasıl tepki vereceğini kontrol etmek, sürükleyici çatışma senaryoları için kritik öneme sahiptir. Varsayılan GTA V davranışı kaotiktir: bazı NPC'ler kaçar, bazıları siner ve silahlı NPC'ler çatışmaya girebilir veya girmeyebilir. Özel çatışma davranışı, öngörülebilir ve role uygun tepkiler oluşturmak için çatışma niteliklerini ve yapılandırma bayraklarını kullanır. Bir çete üyesi ayakta durup düşük isabetle savaşmalı, eğitimli bir güvenlik görevlisi siper alıp orta düzeyde isabetle çatışmalı, bir sivil hemen kaçmalı ve bir VIP karakteri çömelip yardım çağırmalıdır. Bu SetPedCombatAttributes native, ped'in siper alıp alamayacağı, kör atış yapıp yapamayacağı, sesleri araştırıp araştırmayacağı veya sayıca üstün düşman karşısında kaçıp kaçmayacağı gibi bireysel davranışları denetler.

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

NPC Yaşam Döngüsünü ve Performansını Yönetme

NPC'leri spawn etmek, sunucu ve istemci performansı üzerinde doğrudan bir etkiye sahiptir. Her aktif ped; yapay zeka işleme, fizik simülasyonu ve ağ senkronizasyonu için CPU döngüsü tüketir. Tüm NPC'leri sunucu başlangıcında spawn eden ve onları kalıcı olarak canlı tutan basit bir uygulama, oyuncu sayısı arttıkça performansı çökertecektir. Doğru yaklaşım, NPC'lerin yalnızca bir oyuncu yapılandırılabilir bir menzil içindeyken oluşturulduğu ve tüm oyuncular bölgeden ayrıldığında silindiği yakınlık tabanlı spawn yöntemini kullanır. Haritayı bölgelere ayıran ve yalnızca yakınında oyuncu bulunan bölgeler için NPC mantığını işleyen mekansal bir bölümlendirme sistemi kullanın. Bir oyuncu bir bölgeye her girip çıktığında NPC'leri silip yeniden oluşturmak yerine, ped handle değerlerini önbelleğe alın ve mümkün olduğunda yeniden kullanın. Şunu ayarlayın: SetEntityAsMissionEntity false olarak ayarlamak ve oyun motorunun ped yaşam döngüsünü kendi streaming sistemi içinde doğal olarak yönetebilmesi adına ağ duyarlı oluşturma bayrakları kullanmak için.

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

Tümünü Birleştirmek

Canlı kullanıma hazır bir NPC AI sistemi, tüm bu öğeleri yapılandırma verileriyle yönlendirilen uyumlu bir altyapıda birleştirir. Yapılandırmanızdaki her NPC tanımı modeli, spawn konumunu, davranış türünü (sabit, devriye gezen, koruma), çatışma ön ayarını, ilişki grubunu ve yürütülecek senaryo veya görev dizisini belirtir. Altyapı bu tanımları yükler, oyuncu yakınlığına göre spawn ve despawn işlemlerini yönetir, NPC'lerin çatışma veya oyuncu etkileşimiyle bölünmesi durumunda durum geçişlerini ele alır ve NPC'lere artık ihtiyaç kalmadığında kaynakları temizler. En önemli tasarım ilkesi veriyi mantıktan ayırmaktır: Tüm NPC davranışları kod değişikliği olmadan yapılandırılabilir olmalı, sunucu sahiplerinin yalnızca yapılandırma dosyaları aracılığıyla yeni NPC'ler eklemesine, devriye rotalarını değiştirmesine ve çatışma parametrelerini ayarlamasına olanak tanımalıdır. Bu veriye dayalı yaklaşım, AI sisteminizin temel kodda mimari değişiklikler yapmadan birkaç görev NPC'sinden tüm şehri dolduran yüzlerce ortam karakterine kadar ölçeklenebileceği anlamına gelir.

Başlamaya Hazır mısınız?

Scriptleri mağazamızdan edinin veya destek, güncellemeler ve gelecek yeniliklere göz atmak için Discord'a katılın.