Entender el sistema de ropa de GTA V
GTA V usa un sistema de ropa basado en componentes donde cada prenda es un "drawable" dentro de un slot de componente específico. Hay 12 slots de componente en un ped, desde la cabeza (componente 0) pasando por los zapatos (componente 6) hasta bolsas y accesorios. Cada slot puede contener varios drawables y cada drawable puede tener varias variaciones de textura. Por ejemplo, el componente 11 (tops) puede tener el drawable 15 (una chaqueta de cuero) con texturas 0 a 3 como variantes de color (negro, marrón, rojo y blanco). Entender esta jerarquía de componentes, drawables y texturas es esencial antes de crear ropa personalizada, porque cada pieza que añadas debe encajar en esta estructura. El modelo multijugador (MP) añade otra capa con su propio mapeo de drawables que difiere del modo single-player, y los servidores de FiveM usan exclusivamente los modelos freemode de MP.
Estructura del recurso y streaming
La ropa personalizada en FiveM funciona a través del sistema de streaming. Creas un recurso que le dice al juego que cargue drawables y texturas adicionales junto a los assets base del juego. La estructura del recurso sigue convenciones de nombrado estrictas que el motor usa para identificar a qué modelo de ped, componente e índice de drawable pertenece cada archivo. Tener mal la estructura de carpetas es la causa más común de que la ropa no aparezca in-game. Tu recurso necesita un fxmanifest.lua que declare los archivos de streaming y los datos de ropa van en un directorio stream con la jerarquía de subcarpetas correcta.
-- fxmanifest.lua para un addon de ropa
fx_version 'cerulean'
game 'gta5'
-- El nombre del recurso importa: determina la prioridad de carga
-- Prefija con zzz_ para que cargue después de los assets base
this_is_a_map 'yes'
lua54 'yes'
-- Todos los assets de streaming en /stream/ se autodetectan
-- No hace falta listar archivos individualmente
La carpeta stream sigue una jerarquía precisa. Para ropa de freemode masculina MP, los archivos van en stream/mp_m_freemode_01_mp_m_[dlcname]/. Para femenino, sustituye mp_m por mp_f. Dentro de esa carpeta colocas los archivos YDD (drawable dictionary), YTD (texture dictionary) e YMT (metadatos). La convención de nombres codifica qué componente e índice de drawable representa cada archivo. Un fichero llamado mp_m_freemode_01_mp_m_mypack_jbib_000_u.ydd es el drawable 0 del componente 11 (jbib = jacket/tops) sobre el ped freemode masculino del pack DLC "mypack".
-- Ejemplo de estructura de carpetas
stream/
mp_m_freemode_01_mp_m_mypack/
mp_m_freemode_01_mp_m_mypack.ymt -- archivo de metadatos
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 -- textura para 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 -- textura para tops 1
mp_m_freemode_01_mp_m_mypack_lowr_000_u.ydd -- pantalones drawable 0
mp_m_freemode_01_mp_m_mypack_lowr_diff_000_a_uni.ytd -- textura para pantalones 0
IDs de componentes y convenciones de nombrado
Cada componente de ropa tiene una abreviatura específica en el nombrado de archivos. Memorizarlas es crítico, porque una única errata implica que el juego no encuentra tu asset. El mapa de componentes es: head (componente 0, sin ropa), berd (1, máscaras), hair (2), uppr (3, brazos/guantes), lowr (4, pantalones/piernas), hand (5, bolsas/paracaídas), feet (6, zapatos), teef (7, accesorios como cadenas), accs (8, camisetas interiores/accesorios corporales), task (9, chaleco antibalas), decl (10, calcomanías/insignias) y jbib (11, tops/chaquetas). Los props usan un sistema distinto: p_head (gorros), p_eyes (gafas), p_ears (pendientes) y p_lwrist/p_rwrist (relojes/pulseras).
-- Referencia de IDs de componentes para scripting
local ComponentNames = {
[0] = 'head', -- Cara (no es ropa)
[1] = 'berd', -- Máscaras
[2] = 'hair', -- Peinados
[3] = 'uppr', -- Brazos / Guantes
[4] = 'lowr', -- Pantalones / Piernas
[5] = 'hand', -- Bolsas / Paracaídas
[6] = 'feet', -- Zapatos
[7] = 'teef', -- Accesorios (cadenas, corbatas)
[8] = 'accs', -- Camisetas interiores
[9] = 'task', -- Chaleco antibalas
[10] = 'decl', -- Calcomanías / Insignias
[11] = 'jbib', -- Tops / Chaquetas
}
-- Obtiene drawables totales de un componente (útil para menús)
local function GetMaxDrawables(ped, componentId)
return GetNumberOfPedDrawableVariations(ped, componentId)
end
-- Obtiene el número de texturas de un drawable concreto
local function GetMaxTextures(ped, componentId, drawableId)
return GetNumberOfPedTextureVariations(ped, componentId, drawableId)
end
-- Aplica ropa al ped
local function SetClothing(ped, componentId, drawableId, textureId)
SetPedComponentVariation(ped, componentId, drawableId, textureId, 0)
end
Entender los archivos YMT
El archivo YMT (Ped Metadata) es el pegamento que mantiene unido un pack de ropa. Es un archivo basado en XML que dice al motor cuántos drawables hay en cada componente, cuántas texturas tiene cada drawable, qué audio reproducir cuando la ropa se mueve y varias propiedades de renderizado. Sin un YMT bien configurado, el juego no reconocerá tus drawables aunque los YDD e YTD sean perfectos. El YMT debe listar cada combinación de drawable y textura del pack. Una sola entrada olvidada hace que ese ítem sea invisible. Editar YMT a mano es propenso a errores, y por eso la mayoría de creadores usan herramientas como CodeWalker o generadores dedicados que construyen el archivo automáticamente a partir del contenido de la carpeta.
<!-- Estructura YMT simplificada (representación XML) -->
<CPedVariationInfo>
<availComp>
<!-- Campo de bits que indica qué componentes tienen datos -->
<Item value="2048" /> <!-- Solo jbib (componente 11) -->
</availComp>
<compInfos>
<Item> <!-- Componente 11 (jbib / tops) -->
<drawblDict>
<Item> <!-- Drawable 0 -->
<numAlternatives value="0" />
<numTexVariations value="3" /> <!-- 3 variantes de color -->
<clothData>
<ownsCloth value="false" />
</clothData>
</Item>
<Item> <!-- Drawable 1 -->
<numAlternatives value="0" />
<numTexVariations value="2" /> <!-- 2 variantes de color -->
<clothData>
<ownsCloth value="false" />
</clothData>
</Item>
</drawblDict>
</Item>
</compInfos>
</CPedVariationInfo>
Variantes de textura y calidad
Cada drawable puede tener varias variaciones de textura, que representan distintos colores o patrones del mismo modelo. El archivo base de textura usa el patrón [pack]_[component]_diff_[drawableIndex]_[textureIndex]_uni.ytd. Las texturas deberían crearse en potencias de dos (512x512, 1024x1024, 2048x2048) para compatibilidad óptima con la GPU. Las texturas de alta resolución se ven mejor pero consumen más VRAM, un recurso limitado sobre todo en servidores donde los jugadores cargan decenas de packs personalizados a la vez. Una pauta práctica es usar 1024x1024 para la mayoría de prendas y reservar 2048x2048 para ítems con detalles finos como logos bordados o patrones intrincados. Cada archivo YTD puede contener varias capas de textura como diffuse (color), normal (detalle superficial) y specular (brillo), aunque la mayoría de packs de FiveM usan solo la capa difusa para mantener tamaños manejables.
Crear un script de menú de ropa
Una vez que tu recurso de streaming funciona, necesitas una forma de que los jugadores naveguen y apliquen la ropa. Un script de menú recorre todos los drawables disponibles por componente y permite a los jugadores previsualizarlos en tiempo real sobre su personaje. La lógica central usa GetNumberOfPedDrawableVariations para saber cuántos drawables existen por componente y SetPedComponentVariation para aplicar el seleccionado. Envuélvelo en un menú de ox_lib o NUI para una experiencia cuidada. La parte complicada es gestionar previsualizaciones en primera persona y ángulos de cámara para que los jugadores vean lo que se prueban sin clippings extraños ni problemas de zoom.
-- 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
-- Construye opciones del menú
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
-- Navegador interactivo de drawable con preview en vivo
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
Problemas comunes y troubleshooting
El problema más frecuente es que la ropa aparezca invisible o como otra prenda. Esto casi siempre se debe a un YMT incorrecto que no declara bien el número de drawables o texturas. Verifica que tu YMT liste el número exacto de drawables y texturas que existen en tu carpeta stream. Otro problema común es el orden de carga: si tu pack carga antes que los assets base, los índices de drawable pueden colisionar y sobrescribir la ropa vanilla. Prefija el nombre del recurso con zzz_ para que cargue al final. Las texturas borrosas suelen indicar que el YTD usa una resolución muy baja o que los mipmaps no se generaron correctamente. Genera siempre los mipmaps al exportar tus YTD en CodeWalker. Por último, si la ropa se muestra en modelos masculinos pero no en femeninos o viceversa, revisa que tienes carpetas separadas para mp_m_freemode_01 y mp_f_freemode_01 con archivos de malla con el género correcto, ya que los modelos masculino y femenino tienen rigs de esqueleto distintos y requieren geometría separada.
Optimizar para el rendimiento del servidor
Los packs de ropa personalizada son de los que más contribuyen al tamaño de descarga del servidor y al uso de VRAM en el cliente. Un solo pack mal optimizado con 100 ítems de alta resolución puede añadir gigabytes al requisito de descarga. Para que sea manejable, comprime tus texturas YTD en formato DXT5 o BC7, que da buena calidad visual a una fracción del tamaño bruto. Limita cada pack a una cantidad razonable, de 20 a 50 piezas, y parte colecciones más grandes en varios recursos que se puedan cargar por separado. Plantéate ofrecer una versión "lite" de packs populares con texturas 512x512 para jugadores con hardware modesto. En el lado del scripting, evita llamar repetidamente a SetPedComponentVariation en un bucle porque cada llamada dispara una actualización de modelo. Agrupa los cambios recogiéndolos todos y aplicándolos en secuencia en un único frame para minimizar el popping visual y la sobrecarga de renderizado.

