Wróć do bloga
Guide9 min czytania

System zwierząt i towarzyszy FiveM: Skryptowanie lojalnych kompanów

Dodaj zwierzaki i towarzyszy do swojego serwera FiveM. AI podążania, karmienie, sklepy, customowe modele i najlepsze skrypty pet dla konfiguracji QBCore i ESX.

Agency Scripts

Założyciel i główny deweloper w Agency Scripts

Zwierzęta jako żywi towarzysze roleplay

Zwierzęta wnoszą na serwery roleplay wyjątkowy wymiar emocjonalny, którego nie da się zastąpić żadnym innym systemem. Gracz spacerujący po mieście z truchtającym obok psem tworzy wizualną tożsamość, która wzbogaca każdą interakcję. Zwierzęcy towarzysze przekształcają zwykłe czynności, takie jak wyjście do sklepu, we wciągające chwile. Tworzą naturalne preteksty do rozmowy, gdy inni gracze zauważą Twojego owczarka niemieckiego lub zapytają o Twojego kota. Poza elementem społecznym zwierzęta mogą pełnić role użytkowe: psy stróżujące ostrzegające właścicieli przed intruzami, policyjne jednostki K-9 pomagające w przeszukaniach, zwierzęta wsparcia emocjonalnego w szpitalnym roleplay lub towarzysze polowań pomagający tropić zwierzynę. Zbudowanie systemu zwierząt wymaga obsługi spawnowania i despawnowania pedów, zachowania podążania AI, integracji animacji, symulacji potrzeb, takich jak głód i szczęście, trwałości między sesjami oraz komend interakcji, które pozwalają właścicielowi kontrolować swojego towarzysza za pomocą naturalnych gestów i komend głosowych.

Dostępne modele zwierząt i konfiguracja

GTA V zawiera kilka modeli pedów zwierząt, które świetnie sprawdzają się jako towarzysze. Najbardziej wszechstronne są psy z modelami takimi jak a_c_shepherd dla owczarków niemieckich, a_c_husky dla husky, a_c_poodle dla pudli, a_c_rottweiler dla rottweilerów, a_c_retriever dla retrieverów, a a_c_chop dla kultowego modelu Chopa. Koty używają a_c_cat_01. W przypadku opcji egzotycznych dostępne są króliki z a_c_rabbit_01 a nawet ptaki z a_c_crow. Każdy typ zwierzaka powinien mieć skonfigurowane różne parametry zachowania we wspólnym pliku. Psy trzymają się blisko i mogą biec obok pojazdów z umiarkowaną prędkością. Koty są bardziej niezależne i od czasu do czasu oddalają się na niewielkie odległości przed powrotem. Ptaki mogą siadać na ramieniu gracza przy użyciu przyczepiania bytów. Zdefiniuj dla każdego typu zwierzaka dystans podążania, mnożnik prędkości poruszania się, animacje bezczynności, animacje interakcji oraz umiejętności specjalne, aby stworzyć unikalny charakter dla każdego gatunku.

-- shared/config.lua
Config = {}

Config.PetTypes = {
    shepherd = {
        model       = 'a_c_shepherd',
        label       = 'German Shepherd',
        price       = 5000,
        followDist  = 2.5,
        runDist     = 8.0,
        maxSpeed    = 7.0,
        category    = 'dog',
        abilities   = {'guard', 'fetch', 'search'},
        idleAnims   = {'sit', 'lie_down', 'sniff'},
    },
    husky = {
        model       = 'a_c_husky',
        label       = 'Husky',
        price       = 6000,
        followDist  = 3.0,
        runDist     = 10.0,
        maxSpeed    = 8.0,
        category    = 'dog',
        abilities   = {'fetch', 'howl'},
        idleAnims   = {'sit', 'lie_down', 'play'},
    },
    cat = {
        model       = 'a_c_cat_01',
        label       = 'Cat',
        price       = 2000,
        followDist  = 4.0,
        runDist     = 6.0,
        maxSpeed    = 5.0,
        category    = 'cat',
        abilities   = {'purr', 'hunt_rodent'},
        idleAnims   = {'sit', 'groom', 'sleep'},
    },
    poodle = {
        model       = 'a_c_poodle',
        label       = 'Poodle',
        price       = 4000,
        followDist  = 2.0,
        runDist     = 6.0,
        maxSpeed    = 5.5,
        category    = 'dog',
        abilities   = {'fetch', 'trick'},
        idleAnims   = {'sit', 'beg', 'spin'},
    },
    rottweiler = {
        model       = 'a_c_rottweiler',
        label       = 'Rottweiler',
        price       = 7000,
        followDist  = 2.0,
        runDist     = 8.0,
        maxSpeed    = 7.5,
        category    = 'dog',
        abilities   = {'guard', 'attack', 'intimidate'},
        idleAnims   = {'sit', 'lie_down', 'bark'},
    },
}

Spawnowanie i zachowanie podążania

Rdzeniem każdego systemu zwierząt jest AI podążania, dzięki któremu zwierzę naturalnie idzie za swoim właścicielem. Gdy gracz spawnuje swojego zwierzaka, utwórz peda na jego pozycji za pomocą CreatePed z odpowiednim modelem. Ustaw ped jako non-persistent za pomocą SetEntityAsMissionEntity i skonfiguruj go tak, aby był przyjazny dla właściciela za pomocą relationship groups. Zachowanie podążania wykorzystuje pętlę po stronie klienta, która stale oblicza odległość między zwierzakiem a właścicielem. Gdy odległość przekroczy próg podążania, nakaż zwierzakowi biec w kierunku pozycji nieco z tyłu i z boku właściciela za pomocą TaskFollowToOffsetOfEntity. Ta funkcja natywna automatycznie obsługuje wyszukiwanie ścieżek, dzięki czemu zwierzak omija przeszkody, chodzi po chodnikach i unika ruchu ulicznego. Gdy właściciel się zatrzyma, a zwierzak osiągnie dystans podążania, odtwórz animację spoczynku, taką jak siedzenie lub leżenie. Jeśli właściciel wsiądzie do pojazdu i odjedzie, przeteleportuj zwierzaka do pojazdu, jeśli jest odpowiedniego typu, lub usuń zwierzaka i zrespawnuj go, gdy właściciel wyjdzie z pojazdu, aby zapobiec zgubieniu go na mapie.

-- client/pet_behavior.lua
local activePet = nil
local petData   = nil

function SpawnPet(petType, name)
    if activePet then DespawnPet() end

    local config = Config.PetTypes[petType]
    if not config then return end

    local ped = PlayerPedId()
    local coords = GetOffsetFromEntityInWorldCoords(ped, 1.5, 1.5, 0.0)

    RequestModel(config.model)
    while not HasModelLoaded(config.model) do Wait(10) end

    activePet = CreatePed(28, config.model,
        coords.x, coords.y, coords.z, 0.0, true, false)

    SetEntityAsMissionEntity(activePet, true, true)
    SetBlockingOfNonTemporaryEvents(activePet, true)
    SetPedCanBeTargetted(activePet, false)
    SetPedCanRagdoll(activePet, false)

    -- Set relationship so pet doesn't attack owner
    local playerGroup = GetHashKey('PLAYER')
    local petGroup = GetHashKey('PET_COMPANION')
    AddRelationshipGroup('PET_COMPANION')
    SetPedRelationshipGroupHash(activePet, petGroup)
    SetRelationshipBetweenGroups(0, petGroup, playerGroup)
    SetRelationshipBetweenGroups(0, playerGroup, petGroup)

    petData = {type = petType, name = name, config = config}

    -- Start follow loop
    CreateThread(PetFollowLoop)
end

function PetFollowLoop()
    while activePet and DoesEntityExist(activePet) do
        local ped = PlayerPedId()
        local petCoords = GetEntityCoords(activePet)
        local ownerCoords = GetEntityCoords(ped)
        local dist = #(petCoords - ownerCoords)

        if dist > 50.0 then
            -- Teleport if too far
            SetEntityCoords(activePet,
                ownerCoords.x, ownerCoords.y, ownerCoords.z)
        elseif dist > petData.config.runDist then
            -- Run to owner
            TaskFollowToOffsetOfEntity(activePet, ped,
                0.0, -1.5, 0.0, petData.config.maxSpeed,
                -1, 1.0, true)
        elseif dist > petData.config.followDist then
            -- Walk to owner
            TaskFollowToOffsetOfEntity(activePet, ped,
                0.0, -1.5, 0.0, 2.0, -1, 1.0, true)
        else
            -- Idle nearby
            if not IsEntityPlayingAnim(activePet, 'creatures@dog@amb@', 'sit_enter', 3) then
                if math.random() > 0.95 then
                    PlayIdleAnimation()
                end
            end
        end

        Wait(500)
    end
end

System potrzeb i więzi ze zwierzakiem

System potrzeb nadaje posiadaniu zwierzaka stałą odpowiedzialność i głębię. Śledź głód, szczęście i zdrowie jako wartości od 0 do 100, które spadają w czasie rzeczywistym. Głód spada o 1 punkt co 5 minut czasu rzeczywistego, gdy zwierzak jest zespawnowany. Szczęście spada wolniej, o 1 punkt co 10 minut, ale znacznie wzrasta, gdy właściciel wchodzi w interakcję ze zwierzakiem poprzez głaskanie, aportowanie lub dawanie przysmaków. Zdrowie pozostaje na poziomie 100, chyba że głód spadnie poniżej 20, wtedy zaczyna maleć. Gdy głód osiągnie zero, zwierzak staje się ospały, porusza się wolniej i odtwarza smutne animacje. Gdy zdrowie spadnie zbyt nisko, zwierzak potrzebuje pomocy weterynarza, aby dojść do siebie. Gracze karmią swoje zwierzaki, używając przedmiotów jedzenia ze swojego ekwipunku na encji zwierzaka. Różne rodzaje karmy odnawiają różne ilości głodu: podstawowa karma odnawia 30 punktów, karma premium 60, a przysmaki odnawiają 10, ale zwiększają szczęście o 15. Poziom więzi mierzy relację między właścicielem a zwierzakiem, rosnąc dzięki pozytywnym interakcjom i spadając w wyniku zaniedbania. Wyższe poziomy więzi odblokowują specjalne interakcje i poprawiają reakcję zwierzaka na komendy.

-- server/pet_needs.lua
local petStates = {}

function InitPetState(ownerId, petId)
    petStates[ownerId] = {
        petId     = petId,
        hunger    = 100,
        happiness = 80,
        health    = 100,
        bond      = 0,
        lastFed   = os.time(),
        lastPet   = os.time(),
    }
end

-- Decay loop runs every 60 seconds
CreateThread(function()
    while true do
        Wait(60000)
        local now = os.time()
        for ownerId, state in pairs(petStates) do
            -- Hunger decay: 1 point per 5 minutes
            local minutesSinceFed = (now - state.lastFed) / 60
            state.hunger = math.max(0,
                100 - math.floor(minutesSinceFed / 5))

            -- Happiness decay
            local minutesSincePet = (now - state.lastPet) / 60
            state.happiness = math.max(0,
                math.min(100, state.happiness -
                    math.floor(minutesSincePet / 10)))

            -- Health decays when starving
            if state.hunger < 20 then
                state.health = math.max(0,
                    state.health - 1)
            end

            -- Sync to client
            local player = GetPlayerFromIdentifier(ownerId)
            if player then
                TriggerClientEvent('pet:syncState', player, state)
            end
        end
    end
end)

RegisterNetEvent('pet:feed', function(foodItem)
    local src = source
    local id = GetPlayerIdentifier(src, 0)
    local state = petStates[id]
    if not state then return end

    local foodValues = {
        petfood        = {hunger = 30, happiness = 5},
        premium_food   = {hunger = 60, happiness = 10},
        pet_treat      = {hunger = 10, happiness = 15},
    }

    local food = foodValues[foodItem]
    if not food then return end

    -- Remove item from inventory
    local removed = exports['ox_inventory']:RemoveItem(src, foodItem, 1)
    if not removed then return end

    state.hunger = math.min(100, state.hunger + food.hunger)
    state.happiness = math.min(100, state.happiness + food.happiness)
    state.lastFed = os.time()
    state.bond = state.bond + 1

    TriggerClientEvent('pet:syncState', src, state)
    TriggerClientEvent('pet:feedAnimation', src)
end)

Komendy interakcji i animacje

Daj graczom bogaty zestaw komend do interakcji ze swoim zwierzakiem. Menu kołowe lub system komend powinien oferować opcje takie jak sit, stay, follow, come, lie down, shake, fetch oraz speak. Każda komenda wyzwala odpowiednią animację na pedzie zwierzaka przy użyciu słowników animacji stworzeń wbudowanych w GTA V. Komenda sit korzysta z animacji z creatures@dog@amb@ z klipami sit_enter oraz sit_loop. Kładzenie się korzysta z podobnych animacji otoczenia. W przypadku aportowania zespawnuj mały prop piłki, rzuć go po trajektorii parabolicznej i nakaż zwierzakowi biec do miejsca lądowania piłki, podnieść ją za pomocą attachmentu i odnieść właścicielowi. Komenda speak wywołuje dźwięk szczekania za pomocą PlayPedAmbientSpeechNative. Do głaskania odtwórz animację klękania na pedzie gracza, jednocześnie uruchamiając animację radosnego machania ogonem u zwierzaka. Te interakcje to nie tylko kosmetyka; każda z nich zwiększa statystyki szczęścia i więzi, nagradzając graczy, którzy aktywnie wchodzą w interakcję ze swoim towarzyszem, zamiast traktować go jako pasywnego naśladowcę.

Trwałość danych i integracja z Pet Shop

Zwierzęta muszą być zachowywane między sesjami serwera, aby gracze nie tracili swoich towarzyszy przy każdym restarcie. Zapisuj dane zwierząt w bazie danych, w tym identyfikator właściciela, typ zwierzęcia, własną nazwę, aktualne wartości potrzeb, poziom więzi oraz wszelkie odblokowane sztuczki lub umiejętności. Gdy właściciel się zaloguje, pobierz dane jego zwierzaka i zaoferuj jego zespawnowanie za pomocą komendy lub automatycznie, jeśli był aktywny podczas ostatniego rozłączenia. Zintegruj nabywanie zwierząt poprzez sklep zoologiczny w grze, umieszczony w logicznym punkcie mapy. NUI sklepu zoologicznego wyświetla dostępne typy zwierząt z ich modelami renderowanymi w oknie podglądu, cenami i opisami. Po zakupie gracz nadaje swojemu nowemu zwierzakowi imię, a ten pojawia się obok niego. Rozważ dodanie schroniska dla zwierząt jako alternatywy z niższymi cenami, ale losowymi typami zwierząt, co wprowadza charytatywny aspekt roleplay. W przypadku ekonomii serwera zwierzęta mogą również wymagać okresowych wydatków, takich jak wizyty u weterynarza w celu przywrócenia zdrowia, pielęgnacja dająca premie do wyglądu oraz sesje treningowe odblokowujące nowe animacje poleceń, co tworzy realistyczny koszt posiadania zwierzaka i integruje się z Twoim szerszym systemem ekonomii.

-- SQL schema
CREATE TABLE IF NOT EXISTS pets (
    id          INT AUTO_INCREMENT PRIMARY KEY,
    owner_id    VARCHAR(64) NOT NULL,
    char_slot   INT DEFAULT 1,
    pet_type    VARCHAR(32) NOT NULL,
    pet_name    VARCHAR(64) NOT NULL,
    hunger      INT DEFAULT 100,
    happiness   INT DEFAULT 80,
    health      INT DEFAULT 100,
    bond        INT DEFAULT 0,
    tricks      JSON DEFAULT '[]',
    is_active   TINYINT DEFAULT 0,
    created_at  TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at  TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_owner (owner_id, char_slot)
);

-- server/persistence.lua
function SavePet(ownerId, charSlot, state)
    MySQL.update([[
        UPDATE pets SET
            hunger = ?, happiness = ?, health = ?,
            bond = ?, is_active = 1, updated_at = NOW()
        WHERE owner_id = ? AND char_slot = ?
    ]], {
        state.hunger, state.happiness, state.health,
        state.bond, ownerId, charSlot
    })
end

function LoadPet(ownerId, charSlot)
    return MySQL.single.await([[
        SELECT * FROM pets
        WHERE owner_id = ? AND char_slot = ?
        AND is_active = 1
    ]], {ownerId, charSlot})
end

Integracja z jednostką K-9 policji i role specjalne

W przypadku roleplay frakcji policyjnych rozszerz system zwierzaków o pełną integrację jednostki K-9. Psy policyjne zyskują specjalne umiejętności niedostępne dla cywilnych pupili. Komenda search sprawia, że K-9 węszy przy pobliskich pojazdach lub budynkach i ostrzega o konfigurowalnych przedmiotach nielegalnych szczekaniem oraz siadaniem obok źródła. Zaimplementuj to, sprawdzając ekwipunki pobliskich graczy lub bagażniki pojazdów pod kątem oznaczonych przedmiotów po wydaniu komendy search. Komenda track sprawia, że K-9 podąża śladem zapachowym, co możesz wdrożyć, tworząc ścieżkę pozycji uciekającego podejrzanego i zlecając psu podążanie za nimi. Komenda apprehend sprawia, że K-9 biegnie w stronę celu i odtwarza animację ataku, dając funkcjonariuszom opcję obezwładnienia podejrzanego bez użycia broni palnej. Ogranicz te umiejętności do graczy z pracą policjanta i certyfikatem przewodnika K-9. K-9 powinien nosić kamizelkę policyjną za pomocą ped component variations lub prop attachments, aby wizualnie odróżniać się od cywilnych zwierzaków. Ta specjalizacja tworzy unikalną i bardzo pożądaną funkcję w hierarchii departamentu policji.

Gotowy, aby zacząć?

Kup skrypty w naszym sklepie lub dołącz do Discorda, aby uzyskać wsparcie, aktualizacje i sprawdzić nadchodzące nowości.