Wróć do bloga
Tutorial8 min czytania

Niestandardowe dodatki odzieżowe FiveM: streamuj własne kolekcje

Streamuj własne customowe ubrania w FiveM. Konfiguruj addony, zarządzaj slotami, rozwiązuj konflikty i poznaj workflow tworzenia ekskluzywnych dropów na serwerze.

Agency Scripts

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

Zrozumienie systemu ubrań w GTA V

GTA V korzysta z komponentowego systemu ubrań, w którym każdy element odzieży jest określany jako "drawable" w ramach konkretnego slotu komponentu. Na pedzie znajduje się 12 slotów komponentów, od głowy (komponent 0), przez buty (komponent 6), aż po torby i akcesoria. Każdy slot komponentu może pomieścić wiele drawable, a każdy drawable może mieć wiele wariantów tekstur. Na przykład komponent 11 (góra stroju) może mieć drawable 15 (skórzana kurtka) z teksturami od 0 do 3, odpowiadającymi wariantom kolorystycznym: czarnemu, brązowemu, czerwonemu i białemu. Zrozumienie tej hierarchii komponentów, drawable i tekstur jest kluczowe przed przystąpieniem do tworzenia niestandardowych ubrań, ponieważ każdy dodawany element musi wpasować się w tę istniejącą strukturę. Model peda dla trybu wieloosobowego (MP) dodaje kolejną warstwę z własnym mapowaniem drawable, które różni się od postaci z trybu jednoosobowego, a serwery FiveM korzystają wyłącznie z modeli MP freemode.

Struktura zasobu i streaming

Własne ubrania w FiveM działają za pośrednictwem systemu streamingu. Tworzysz resource, który nakazuje grze ładowanie dodatkowych plików drawable i tekstur obok podstawowych plików gry. Struktura zasobu opiera się na ścisłych konwencjach nazewnictwa, których silnik gry używa do określenia, do którego modelu peda, komponentu i indeksu drawable należy dany plik. Błędna struktura folderów to najczęstsza przyczyna niewyświetlania się ubrań w grze. Twój resource potrzebuje fxmanifest.lua deklarujący pliki streamingu, a właściwe dane ubrań trafiają do stream katalog z prawidłową hierarchią podfolderów.

-- fxmanifest.lua for a clothing addon
fx_version 'cerulean'
game 'gta5'

-- The resource name matters: it determines load priority
-- Prefix with zzz_ to ensure it loads after base game assets
this_is_a_map 'yes'
lua54 'yes'

-- All streaming assets inside /stream/ are auto-detected
-- No need to list individual files

Folder stream ma precyzyjną hierarchię. W przypadku ubrań męskich MP freemode pliki trafiają do stream/mp_m_freemode_01_mp_m_[dlcname]/. Dla postaci żeńskiej zastąp mp_m z mp_f. Wewnątrz tego folderu umieszczasz pliki YDD (drawable dictionary), YTD (texture dictionary) oraz YMT (metadata). Konwencja nazewnictwa określa, który komponent i indeks drawable reprezentuje dany plik. Plik o nazwie mp_m_freemode_01_mp_m_mypack_jbib_000_u.ydd to drawable 0 dla komponentu 11 (jbib = kurtka/góra) na męskim pedzie freemode z pakietu DLC "mypack".

-- Folder structure example
stream/
  mp_m_freemode_01_mp_m_mypack/
    mp_m_freemode_01_mp_m_mypack.ymt          -- metadata file
    mp_m_freemode_01_mp_m_mypack_jbib_000_u.ydd   -- tops drawable 0
    mp_m_freemode_01_mp_m_mypack_jbib_diff_000_a_uni.ytd  -- texture for tops 0
    mp_m_freemode_01_mp_m_mypack_jbib_001_u.ydd   -- tops drawable 1
    mp_m_freemode_01_mp_m_mypack_jbib_diff_001_a_uni.ytd  -- texture for tops 1
    mp_m_freemode_01_mp_m_mypack_lowr_000_u.ydd   -- pants drawable 0
    mp_m_freemode_01_mp_m_mypack_lowr_diff_000_a_uni.ytd  -- texture for pants 0

ID komponentów i konwencje nazewnictwa

Każdy element ubioru ma określony skrót używany w nazwach plików. Zapamiętanie ich jest kluczowe, ponieważ pojedyncza literówka oznacza, że gra nie znajdzie twojego zasobu. Mapa komponentów to: głowa (komponent 0, brak ubrania), berd (1, maski), włosy (2), uppr (3, ramiona/rękawice), lowr (4, spodnie/nogi), dłoń (5, torby/spadochrony), stopy (6, buty), teef (7, akcesoria, np. łańcuchy), akcesoria (8, podkoszulki/akcesoria ciała), zadanie (9, kamizelki kuloodporne), decl (10, kalkomanie/odznaki) oraz jbib (11, góry/kurtki). Rekwizyty używają innego systemu: p_head (czapki), p_eyes (okulary), p_ears (kolczyki) oraz p_lwrist/p_rwrist (zegarki/bransoletki).

-- Component ID reference for scripting
local ComponentNames = {
    [0]  = 'head',   -- Face (not clothing)
    [1]  = 'berd',   -- Masks
    [2]  = 'hair',   -- Hair styles
    [3]  = 'uppr',   -- Arms / Gloves
    [4]  = 'lowr',   -- Pants / Legs
    [5]  = 'hand',   -- Bags / Parachutes
    [6]  = 'feet',   -- Shoes
    [7]  = 'teef',   -- Accessories (chains, ties)
    [8]  = 'accs',   -- Undershirts
    [9]  = 'task',   -- Body Armor
    [10] = 'decl',   -- Decals / Badges
    [11] = 'jbib',   -- Tops / Jackets
}

-- Get total drawables for a component (useful for clothing menus)
local function GetMaxDrawables(ped, componentId)
    return GetNumberOfPedDrawableVariations(ped, componentId)
end

-- Get texture count for a specific drawable
local function GetMaxTextures(ped, componentId, drawableId)
    return GetNumberOfPedTextureVariations(ped, componentId, drawableId)
end

-- Apply clothing to ped
local function SetClothing(ped, componentId, drawableId, textureId)
    SetPedComponentVariation(ped, componentId, drawableId, textureId, 0)
end

Zrozumienie plików YMT

Plik YMT (Ped Metadata) to spoiwo łączące cały clothing pack. Jest to plik oparty na XML, który informuje silnik gry, ile drawables znajduje się w każdym komponencie, ile tekstur ma każdy drawable, jakie dźwięki odtwarzać podczas ruchu ubrań oraz o różnych właściwościach renderowania. Bez poprawnie skonfigurowanego YMT gra nie rozpozna twoich drawables, nawet jeśli pliki YDD i YTD są przygotowane perfekcyjnie. Plik YMT musi zawierać każdą kombinację drawable i tekstury w twoim packu. Pojedynczy brakujący wpis oznacza, że dany element będzie niewidoczny. Ręczna edycja plików YMT jest podatna na błędy, dlatego większość twórców korzysta z narzędzi takich jak CodeWalker lub dedykowanych generatorów YMT, które tworzą plik automatycznie na podstawie zawartości folderu z assetami.

<!-- Simplified YMT structure (XML representation) -->
<CPedVariationInfo>
  <availComp>
    <!-- Bitfield indicating which components have data -->
    <Item value="2048" />  <!-- Only jbib (component 11) -->
  </availComp>
  <compInfos>
    <Item> <!-- Component 11 (jbib / tops) -->
      <drawblDict>
        <Item>  <!-- Drawable 0 -->
          <numAlternatives value="0" />
          <numTexVariations value="3" />  <!-- 3 color variants -->
          <clothData>
            <ownsCloth value="false" />
          </clothData>
        </Item>
        <Item>  <!-- Drawable 1 -->
          <numAlternatives value="0" />
          <numTexVariations value="2" />  <!-- 2 color variants -->
          <clothData>
            <ownsCloth value="false" />
          </clothData>
        </Item>
      </drawblDict>
    </Item>
  </compInfos>
</CPedVariationInfo>

Warianty tekstur i jakość

Każdy drawable może mieć wiele wariantów tekstur, które reprezentują różne kolory lub wzory tego samego modelu ubioru. Bazowy plik tekstury używa schematu nazewnictwa [pack]_[component]_diff_[drawableIndex]_[textureIndex]_uni.ytd. Tekstury powinny być tworzone w potęgach dwójki (512x512, 1024x1024, 2048x2048) w celu optymalnej zgodności z GPU. Tekstury o wyższej rozdzielczości wyglądają lepiej, ale zużywają więcej pamięci VRAM, która jest ograniczonym zasobem zwłaszcza na serwerach, gdzie gracze ładują jednocześnie dziesiątki własnych paczek ubrań. Praktyczną wskazówką jest stosowanie 1024x1024 dla większości elementów odzieży i zarezerwowanie 2048x2048 dla przedmiotów z drobnymi detalami, takimi jak haftowane logo czy skomplikowane wzory. Każdy plik YTD może zawierać wiele warstw tekstur, w tym mapy diffuse (kolor), normal (szczegóły powierzchni) i specular (połysk), chociaż większość paczek ubrań FiveM używa tylko warstwy diffuse, aby utrzymać rozmiary plików pod kontrolą.

Tworzenie skryptu menu ubrań

Gdy Twój resource do streamingu działa, potrzebujesz sposobu, aby gracze mogli przeglądać i zakładać ubrania. Skrypt menu ubrań iteruje po wszystkich dostępnych drawables dla danego komponentu i pozwala graczom na ich podgląd na postaci w czasie rzeczywistym. Główna logika wykorzystuje GetNumberOfPedDrawableVariations aby ustalić, ile drawables istnieje dla każdego komponentu, a następnie SetPedComponentVariation aby zastosować wybrany element. Owiń to w menu ox_lib lub NUI, aby zapewnić dopracowane wrażenia użytkownika. Trudną częścią jest obsługa podglądu z pierwszej osoby i kątów kamery, tak aby gracze widzieli, co przymierzają, bez niezręcznego przenikania tekstur czy problemów z przybliżeniem.

-- client/clothing_menu.lua
local function OpenClothingMenu()
    local ped = PlayerPedId()
    local components = {}

    for compId = 0, 11 do
        local maxDrawables = GetNumberOfPedDrawableVariations(ped, compId)
        if maxDrawables > 0 then
            local currentDrawable = GetPedDrawableVariation(ped, compId)
            local currentTexture = GetPedTextureVariation(ped, compId)
            local maxTextures = GetNumberOfPedTextureVariations(ped, compId, currentDrawable)

            table.insert(components, {
                id = compId,
                label = ComponentNames[compId] or ('Component ' .. compId),
                drawable = currentDrawable,
                texture = currentTexture,
                maxDrawable = maxDrawables - 1,
                maxTexture = maxTextures - 1,
            })
        end
    end

    -- Build menu options
    local options = {}
    for _, comp in ipairs(components) do
        options[#options + 1] = {
            title = comp.label,
            description = ('Drawable: %d/%d | Texture: %d/%d'):format(
                comp.drawable, comp.maxDrawable, comp.texture, comp.maxTexture),
            onSelect = function()
                OpenComponentEditor(comp.id)
            end
        }
    end

    lib.registerContext({ id = 'clothing_menu', title = 'Wardrobe', options = options })
    lib.showContext('clothing_menu')
end

local function OpenComponentEditor(componentId)
    local ped = PlayerPedId()
    local maxDraw = GetNumberOfPedDrawableVariations(ped, componentId) - 1

    -- Interactive drawable browser with live preview
    local input = lib.inputDialog('Select Clothing', {
        { type = 'slider', label = 'Style', min = 0, max = maxDraw, default = GetPedDrawableVariation(ped, componentId) },
    })

    if input then
        local drawableId = input[1]
        local maxTex = GetNumberOfPedTextureVariations(ped, componentId, drawableId) - 1
        SetPedComponentVariation(ped, componentId, drawableId, 0, 0)

        if maxTex > 0 then
            local texInput = lib.inputDialog('Select Color', {
                { type = 'slider', label = 'Variant', min = 0, max = maxTex, default = 0 },
            })
            if texInput then
                SetPedComponentVariation(ped, componentId, drawableId, texInput[1], 0)
            end
        end
    end
end

Typowe problemy i rozwiązywanie problemów

Najczęstszym problemem są niewidoczne ubrania lub pojawianie się niewłaściwego elementu garderoby. Prawie zawsze wynika to z nieprawidłowego pliku YMT, który nie deklaruje poprawnie liczby modeli (drawable) lub tekstur. Sprawdź, czy twój plik YMT zawiera dokładną liczbę drawables i tekstur znajdujących się w folderze stream. Innym częstym problemem jest kolejność ładowania zasobów: jeśli twój pakiet ubrań ładuje się przed podstawowymi zasobami gry, indeksy drawables mogą kolidować i nadpisywać domyślne ubrania. Dodaj prefiks do nazwy swojego zasobu: zzz_ aby upewnić się, że ładuje się jako ostatni. Rozmycie tekstur zazwyczaj oznacza, że plik YTD używa zbyt niskiej rozdzielczości lub mipmapy nie zostały wygenerowane poprawnie. Zawsze generuj mipmapy podczas eksportowania plików YTD w CodeWalker. Ponadto, jeśli ubrania wyświetlają się na modelach męskich, ale nie na damskich lub odwrotnie, sprawdź dokładnie, czy masz osobne foldery dla mp_m_freemode_01 i mp_f_freemode_01 z plikami siatek dostosowanymi do płci, ponieważ męskie i żeńskie modele ped mają różne szkielety i wymagają osobnej geometrii.

Optymalizacja pod kątem wydajności serwera

Pakiety własnych ubrań w największym stopniu wpływają na rozmiar pobieranych danych serwera i zużycie VRAM u graczy. Pojedynczy, słabo zoptymalizowany pakiet ze 100 elementami w wysokiej rozdzielczości może dodać gigabajty do danych pobieranych z Twojego serwera. Aby utrzymać nad tym kontrolę, kompresuj tekstury YTD przy użyciu formatu DXT5 lub BC7, co zapewnia dobrą jakość wizualną przy ułamku pierwotnego rozmiaru pliku. Ogranicz każdy pakiet do rozsądnej liczby elementów, zazwyczaj od 20 do 50 sztuk, i podziel większe kolekcje na kilka zasobów, które można ładować niezależnie. Rozważ zaoferowanie wersji "lite" popularnych pakietów z teksturami 512x512 dla graczy ze słabszym sprzętem. Po stronie skryptów unikaj wielokrotnego wywoływania SetPedComponentVariation w pętli, ponieważ każde wywołanie wyzwala aktualizację modelu. Grupuj zmiany ubrań, zbierając wszystkie aktualizacje komponentów i aplikując je sekwencyjnie w ramach jednej klatki, aby zminimalizować migotanie wizualne i obciążenie renderowania.

Gotowy, aby zacząć?

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