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