返回博客
Tutorial3 分钟阅读

FiveM 自定义服装插件:直播您的专属服装发布

在 FiveM 中流式传输您自己的定制服装。设置插件,管理槽位,解决冲突,了解构建独家服务器服装的工作流程。

Agency Scripts

Agency Scripts 创始人兼首席开发者

理解GTA V的服装系统

GTA V使用基于组件的服装系统,每件服装都是特定组件槽内的“drawable”。行人有12个组件槽,从头部(组件0)到鞋子(组件6)再到包和配饰。每个组件槽可以包含多个drawable,每个drawable可以有多个纹理变化。例如,组件11(上衣)可能有drawable 15(一件皮夹克),其纹理0到3分别代表黑色、棕色、红色和白色变体。理解组件、drawable和纹理的层级结构是创建自定义服装的基础,因为您添加的每件服装都必须插入此现有结构。多人(MP)行人模型增加了另一层drawable映射,与单人角色不同,FiveM服务器专用MP自由模式模型。

资源结构与流式传输

FiveM中的自定义服装通过流式系统工作。您创建一个资源,告诉游戏加载额外的drawable和纹理文件,附加于基础游戏资源。资源结构遵循严格的命名规范,游戏引擎用以识别每个文件所属的ped模型、组件和drawable索引。文件夹结构错误是服装无法在游戏中显示的最常见原因。您的资源需要一个 fxmanifest.lua 声明流媒体文件,实际服装数据存放于 stream 具有正确子文件夹层级的目录。

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

stream文件夹遵循精确层级。对于MP自由模式男性服装,文件放入 stream/mp_m_freemode_01_mp_m_[dlcname]/. 对于女性角色,替换为 mp_m 带有 mp_f. 在该文件夹内,放置 YDD(可绘制字典)、YTD(纹理字典)和 YMT(元数据)文件。命名规则编码了每个文件代表的组件和可绘制索引。名为 mp_m_freemode_01_mp_m_mypack_jbib_000_u.ydd 是DLC包“mypack”中男性自由模式角色的组件11(jbib = 夹克/上衣)的可绘制0。

-- 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 和命名规范

每个服装组件在文件命名中有特定缩写。记住这些缩写至关重要,因为一个拼写错误会导致游戏找不到资源。组件映射为: head (组件0,无服装), berd (1,面具), hair (2), uppr (3,手臂/手套), lowr (4,裤子/腿部), hand (5,包/降落伞), feet (6,鞋子), teef (7,配饰如链条), 账户 (8,内衬/身体配饰), 任务 (9,防弹衣), decl (10,贴花/徽章),和 jbib (11,上衣/夹克)。道具使用不同的系统: p_head (帽子), p_eyes (眼镜), p_ears (耳环), 和 p_lwrist/p_rwrist (手表/手链)。

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

理解YMT文件

YMT(Ped元数据)文件是服装包的粘合剂。它是基于XML的文件,告诉游戏引擎每个组件中有多少可绘制对象,每个可绘制对象有多少纹理,服装移动时播放什么音频,以及各种渲染属性。没有正确配置的YMT,即使YDD和YTD文件制作完美,游戏也无法识别你的可绘制对象。YMT必须列出包中每一个可绘制对象和纹理组合。缺少任何一项都会导致该物品不可见。手动编辑YMT文件容易出错,这就是大多数创作者使用CodeWalker或专用YMT生成器等工具自动根据资源文件夹内容生成文件的原因。

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

纹理变化和质量

每个可绘制物件可有多种纹理变化,代表同一服装模型的不同颜色或图案。基础纹理文件采用命名模式 [pack]_[component]_diff_[drawableIndex]_[textureIndex]_uni.ytd. 纹理应以 2 的幂次方创建(512x512、1024x1024、2048x2048),以实现最佳 GPU 兼容性。更高分辨率的纹理效果更好,但消耗更多 VRAM,VRAM 是有限资源,尤其是在玩家同时加载数十个自定义服装包的服务器上。实用指南是大多数服装使用 1024x1024,细节丰富如刺绣标志或复杂图案的使用 2048x2048。每个 YTD 文件可包含多层纹理,包括漫反射(颜色)、法线(表面细节)和高光(光泽)贴图,但大多数 FiveM 服装包仅使用漫反射层以保持文件大小可控。

构建服装菜单脚本

流媒体资源运行后,需要让玩家浏览和应用服装。服装菜单脚本遍历每个组件的所有可用可绘制项,允许玩家实时预览角色穿戴效果。核心逻辑使用 GetNumberOfPedDrawableVariations 确定每个组件存在多少可绘制对象,然后 SetPedComponentVariation 应用所选项。将其包装在 ox_lib 或 NUI 菜单中以获得精致的用户体验。难点在于处理第一人称预览和摄像机角度,使玩家能看到试戴效果而无尴尬的剪裁或缩放问题。

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

常见问题及故障排除

最常见问题是服装显示为隐形或错误物品。几乎总是由于YMT文件错误,未正确声明drawable数量或纹理数量。确认你的YMT列出了流媒体文件夹中存在的确切drawable和纹理数量。另一个常见问题是资源加载顺序:若服装包加载早于基础游戏资源,drawable索引可能冲突并覆盖原版服装。请在资源名前加前缀 zzz_ 确保它最后加载。纹理模糊通常表示 YTD 使用了过低的分辨率或未正确生成 mipmaps。导出 YTD 文件时,务必在 CodeWalker 中生成 mipmaps。最后,如果服装在男性模型上显示但女性不显示,或反之,请仔细检查你是否为 mp_m_freemode_01mp_f_freemode_01 带有正确性别的网格文件,因为男性和女性ped模型骨骼结构不同,需要分别的几何体。

为服务器性能优化

自定义服装包是服务器下载大小和客户端VRAM使用的最大贡献者之一。一个包含100件高分辨率物品的优化不良包可能会为服务器下载需求增加数GB。为保持可控,使用DXT5或BC7格式压缩YTD纹理,这能在极小的文件大小下提供良好的视觉质量。每个包限制在合理数量,通常为20到50件,并将较大集合拆分为多个可独立加载的资源。考虑为低端硬件玩家提供512x512纹理的“精简”版本。在脚本方面,避免反复调用 SetPedComponentVariation 在循环中,因为每次调用都会触发模型更新。通过收集所有组件更新并在单帧内依次应用,批量更改服装,以最小化视觉跳动和渲染开销。

准备好开始了吗?

在我们的商店获取脚本,或加入 Discord 获取支持、更新以及新功能预告。