Volver al blog
Tutorial12 min de lectura

Sistema de tatuajes y barberías para FiveM: scripts de personalización

Añade estudios de tatuajes y barberías a tu ciudad de FiveM. Descubre ángulos de cámara, interfaces, citas y los mejores scripts de personalización para el rol.

Agency Scripts

Fundador y desarrollador principal en Agency Scripts

Cómo funcionan los componentes de apariencia del ped

Crear un sistema de tatuajes y barbería en FiveM requiere entender bien cómo gestiona GTA V la apariencia del ped (personaje). El sistema de apariencia se divide en varias categorías de componentes: superposiciones de cabeza para elementos como imperfecciones, vello facial, cejas y maquillaje; componentes de ropa para prendas y peinados; y decoraciones de tatuajes que se aplican sobre el modelo del ped. Cada tipo de componente usa funciones nativas diferentes y su propio sistema de índices. Las superposiciones de cabeza usan los índices 0-12 para controlar elementos como imperfecciones (0), vello facial (1), cejas (2), envejecimiento (3), maquillaje (4), colorete (5) y pintalabios (8). Los peinados se configuran mediante el sistema de componentes de ropa en el índice 2 para el cabello. Los tatuajes usan un sistema de decoración completamente distinto, con nombres de colecciones y valores hash. Antes de crear la interfaz, tienes que identificar todas las opciones disponibles para cada componente, incluido el número de variaciones disponibles para el modelo de ped actual, ya que los personajes masculinos y femeninos tienen distintos estilos. Usa GetNumHeadOverlayValues(overlayIndex) para consultar en tiempo de ejecución cuántas opciones hay para cada tipo de superposición.

Sistema de cámara para previsualizar al personaje

Una buena experiencia de barbería y tatuajes depende de que el jugador pueda ver en tiempo real lo que está seleccionando. Crea un sistema de cámara personalizado que enfoque la parte del cuerpo correspondiente mientras el jugador explora las opciones. Al editar el cabello, acerca la cámara a la cabeza. Al seleccionar tatuajes para el torso, aleja la cámara para mostrar la parte superior del cuerpo. Al trabajar en tatuajes de las piernas, coloca la cámara para encuadrar la parte inferior del cuerpo. Usa funciones nativas de cámara programada para crear transiciones suaves entre distintas perspectivas al cambiar de categoría. Aquí tienes un sistema de gestión de cámara que controla varios niveles de zoom y zonas del cuerpo:

local customCam = nil
local camActive = false

local CameraPresets = {
    head = {offset = vector3(0.0, 0.65, 0.65), fov = 35.0},
    face = {offset = vector3(0.0, 0.45, 0.62), fov = 25.0},
    torso = {offset = vector3(0.0, 1.2, 0.2), fov = 45.0},
    legs = {offset = vector3(0.0, 1.5, -0.5), fov = 50.0},
    full = {offset = vector3(0.0, 2.5, 0.2), fov = 50.0},
}

function SetupPreviewCamera(preset)
    local ped = PlayerPedId()
    local pedCoords = GetEntityCoords(ped)
    local pedHeading = GetEntityHeading(ped)
    local headingRad = math.rad(pedHeading)

    local cam = CameraPresets[preset] or CameraPresets.full
    local camX = pedCoords.x + cam.offset.y * math.sin(-headingRad)
    local camY = pedCoords.y + cam.offset.y * math.cos(-headingRad)
    local camZ = pedCoords.z + cam.offset.z

    if customCam then
        local newCam = CreateCam('DEFAULT_SCRIPTED_CAMERA', true)
        SetCamCoord(newCam, camX, camY, camZ)
        PointCamAtCoord(newCam, pedCoords.x, pedCoords.y, pedCoords.z + cam.offset.z)
        SetCamFov(newCam, cam.fov)
        SetCamActiveWithInterp(newCam, customCam, 800, 1, 1)

        Wait(800)
        DestroyCam(customCam, false)
        customCam = newCam
    else
        customCam = CreateCam('DEFAULT_SCRIPTED_CAMERA', true)
        SetCamCoord(customCam, camX, camY, camZ)
        PointCamAtCoord(customCam, pedCoords.x, pedCoords.y, pedCoords.z + cam.offset.z)
        SetCamFov(customCam, cam.fov)
        SetCamActive(customCam, true)
        RenderScriptCams(true, true, 500, true, false)
    end
    camActive = true
end

function DestroyPreviewCamera()
    if customCam then
        RenderScriptCams(false, true, 500, true, false)
        DestroyCam(customCam, false)
        customCam = nil
        camActive = false
    end
end

Añade compatibilidad con la rotación mediante el ratón o el stick analógico para que los jugadores puedan girar a su personaje mientras previsualizan los cambios. Captura el movimiento del ratón cuando la NUI no lo esté usando y aplícalo como rotación del rumbo del ped del jugador. Así los jugadores pueden examinar su tatuaje desde varios ángulos antes de confirmar la compra, lo que mejora considerablemente la experiencia de compra. Inmoviliza al ped del jugador durante la sesión de personalización y reproduce una animación de reposo para que el personaje se vea natural mientras el jugador explora las opciones.

Personalización del cabello y la barba

La personalización del cabello y la barba usa respectivamente el sistema de componentes de ropa y el de superposiciones de cabeza. Los peinados se configuran con SetPedComponentVariation usando el índice de componente 2 (cabello) y el índice 1 (variación de textura/color). El color del cabello se aplica por separado con SetPedHairColor que recibe un índice de color principal y uno de color de reflejos. Los estilos de barba usan el sistema de superposiciones de cabeza en el índice 1, con un valor de opacidad que controla el grosor del vello facial. Crea la NUI para mostrar miniaturas o nombres de cada estilo disponible y actualizar el ped en tiempo real cuando el jugador recorra las opciones. Aquí tienes la lógica de Lua para aplicar cambios de cabello y barba con previsualización en tiempo real:

function ApplyHairStyle(ped, styleIndex, textureIndex, primaryColor, highlightColor)
    SetPedComponentVariation(ped, 2, styleIndex, textureIndex or 0, 0)
    SetPedHairColor(ped, primaryColor, highlightColor)
end

function ApplyBeardStyle(ped, styleIndex, opacity, color)
    SetPedHeadOverlay(ped, 1, styleIndex, opacity or 1.0)
    SetPedHeadOverlayColor(ped, 1, 1, color, color)
end

function ApplyEyebrows(ped, styleIndex, opacity, color)
    SetPedHeadOverlay(ped, 2, styleIndex, opacity or 1.0)
    SetPedHeadOverlayColor(ped, 2, 1, color, color)
end

function GetAvailableHairStyles(ped)
    local styles = {}
    local numStyles = GetNumberOfPedDrawableVariations(ped, 2)

    for i = 0, numStyles - 1 do
        local numTextures = GetNumberOfPedTextureVariations(ped, 2, i)
        table.insert(styles, {
            index = i,
            textures = numTextures,
            label = 'Style ' .. (i + 1)
        })
    end

    return styles
end

function GetAvailableBeardStyles(ped)
    local styles = {}
    local numStyles = GetNumHeadOverlayValues(1)

    for i = 0, numStyles - 1 do
        table.insert(styles, {
            index = i,
            label = 'Beard ' .. (i + 1)
        })
    end

    return styles
end

La interfaz NUI debería mostrar los colores de cabello como una paleta visual, no como índices numerados, relacionando los índices de color internos de GTA con sus valores RGB reales. Incluye un control deslizante para la opacidad de la barba que permita a los jugadores ajustar el grosor del vello facial, desde una barba incipiente ligera hasta una barba completa y tupida. Guarda la apariencia anterior del jugador antes de abrir el menú de la barbería para poder revertir todos los cambios si pulsa Cancelar, y evitar que los cambios accidentales le cuesten dinero.

Sistema de superposición de tatuajes

Los tatuajes en GTA V usan el sistema de decoraciones, que superpone texturas sobre el modelo del ped mediante pares de hashes de colección y superposición. Cada tatuaje se define mediante un nombre de colección (como "mpairraces_overlays") y un nombre de superposición (como "MP_Airraces_Tattoo_000_M") que, juntos, identifican el recurso específico del tatuaje. Aplica tatuajes con AddPedDecorationFromHashes y elimina todos los tatuajes con ClearPedDecorations. La parte complicada es que no existe una función nativa para eliminar un solo tatuaje, así que cuando un jugador quiera quitarse uno, tendrás que borrar todas las decoraciones y volver a aplicar todas menos la que se eliminó. Esto significa que debes mantener una lista completa de los tatuajes actuales del jugador en tu modelo de datos. Así puedes gestionar la colección y la aplicación de tatuajes:

-- Tattoo database structure
local TattooZones = {
    head = {label = 'Head', camera = 'face'},
    torso = {label = 'Torso', camera = 'torso'},
    left_arm = {label = 'Left Arm', camera = 'torso'},
    right_arm = {label = 'Right Arm', camera = 'torso'},
    left_leg = {label = 'Left Leg', camera = 'legs'},
    right_leg = {label = 'Right Leg', camera = 'legs'},
    back = {label = 'Back', camera = 'torso'}
}

-- Available tattoos per zone (abbreviated, real list has hundreds)
Config.Tattoos = {
    torso = {
        {collection = 'mpairraces_overlays', overlay = 'MP_Airraces_Tattoo_000_M',
         label = 'Wing Design', price = 500, zone = 'torso'},
        {collection = 'mpbiker_overlays', overlay = 'MP_MP_Biker_Tat_000_M',
         label = 'Flame Skull', price = 750, zone = 'torso'},
    },
    left_arm = {
        {collection = 'mpchristmas2_overlays', overlay = 'MP_Xmas2_M_Tat_000',
         label = 'Sleeve Art', price = 600, zone = 'left_arm'},
    }
}

function ApplyAllTattoos(ped, tattooList)
    ClearPedDecorations(ped)

    for _, tattoo in ipairs(tattooList) do
        local collectionHash = GetHashKey(tattoo.collection)
        local overlayHash = GetHashKey(tattoo.overlay)
        AddPedDecorationFromHashes(ped, collectionHash, overlayHash)
    end
end

function PreviewTattoo(ped, tattoo, currentTattoos)
    local previewList = {}
    for _, t in ipairs(currentTattoos) do
        table.insert(previewList, t)
    end
    table.insert(previewList, tattoo)
    ApplyAllTattoos(ped, previewList)
end

function RemoveTattoo(ped, tattooIndex, currentTattoos)
    local newList = {}
    for i, t in ipairs(currentTattoos) do
        if i ~= tattooIndex then
            table.insert(newList, t)
        end
    end
    ApplyAllTattoos(ped, newList)
    return newList
end

El catálogo de tatuajes de GTA V contiene cientos de opciones repartidas entre varias colecciones de DLC. Organízalos por zona del cuerpo en tu configuración para que la NUI pueda filtrarlos por área. Para cada tatuaje, comprueba si debes usar la variante masculina o femenina según el modelo de ped del jugador, ya que la mayoría de los tatuajes tienen recursos distintos cuyos nombres terminan en _M o _F. Muestra una vista previa cuando el jugador pase el cursor sobre un tatuaje del catálogo y cóbrale solo cuando confirme la selección.

Sistema de maquillaje y pintura facial

El maquillaje usa el sistema de superposiciones de la cabeza en varios índices: rubor (5), pintalabios (8) y vello del pecho o superposiciones de tez para efectos adicionales. Cada tipo de maquillaje tiene varios estilos y un control deslizante de opacidad para ajustar la intensidad. Crea una sección de maquillaje en la NUI de la barbería que muestre cada categoría con muestras visuales de los colores disponibles. El sistema de colores del maquillaje usa SetPedHeadOverlayColor con un parámetro de tipo de color: tipo 0 para los colores predeterminados, tipo 1 para los colores de pelo (usados en barbas y cejas) y tipo 2 para los colores de maquillaje. El maquillaje usa específicamente el tipo de color 2, que ofrece una paleta de colores cosméticos que incluye varios tonos de piel, rojos, rosas y colores llamativos. Añade un control deslizante de opacidad para cada componente de maquillaje para que los jugadores puedan crear estilos naturales y discretos o looks llamativos. Guarda todos los ajustes de maquillaje junto con los datos de apariencia del jugador para que se mantengan entre sesiones, y ofrece una interacción con un espejo en la casa del jugador para que pueda retocarse sin tener que ir a la tienda.

Sistema de pagos y precios

El sistema de pagos debe registrar qué cambios hizo el jugador durante su sesión y calcular el coste total según los servicios prestados. Cuando un jugador abra el menú de la tienda, guarda una copia de su apariencia actual. A medida que haga cambios, registra cada modificación en un carrito de sesión que acumule el coste total. Cuando confirme y salga, cóbrale el total de todos los cambios realizados. Si cancela, restaura la apariencia guardada. Implementa precios escalonados: los cortes básicos deben costar menos que los estilos prémium, y el precio de los tatuajes debe variar según su tamaño y complejidad. Así puedes implementar el seguimiento de la sesión y el sistema de pagos:

local sessionChanges = {}
local originalAppearance = nil

function StartCustomizationSession()
    local ped = PlayerPedId()
    originalAppearance = CaptureFullAppearance(ped)
    sessionChanges = {}
end

function TrackChange(category, item, price)
    -- Remove previous change in same category if exists
    for i = #sessionChanges, 1, -1 do
        if sessionChanges[i].category == category then
            table.remove(sessionChanges, i)
        end
    end

    table.insert(sessionChanges, {
        category = category,
        item = item,
        price = price
    })

    UpdateCartUI()
end

function UpdateCartUI()
    local total = 0
    local items = {}

    for _, change in ipairs(sessionChanges) do
        total = total + change.price
        table.insert(items, {
            label = change.category .. ': ' .. change.item,
            price = change.price
        })
    end

    SendNUIMessage({
        action = 'updateCart',
        items = items,
        total = total
    })
end

function ConfirmAndPay()
    local total = 0
    for _, change in ipairs(sessionChanges) do
        total = total + change.price
    end

    TriggerServerEvent('appearance:server:pay', total, sessionChanges)
end

function CancelSession()
    if originalAppearance then
        RestoreFullAppearance(PlayerPedId(), originalAppearance)
    end
    sessionChanges = {}
    DestroyPreviewCamera()
end

En el servidor, valida que el jugador tenga fondos suficientes antes de aplicar los cambios de apariencia. Si el pago falla, notifica al cliente para que revierta los cambios. Considera ofrecer paquetes en los que un corte de pelo y un arreglo de barba juntos cuesten menos que la suma de los servicios por separado, para animar a los jugadores a aprovechar toda la gama de servicios en una sola visita.

Guardado de atuendos e integración con el guardarropa

Amplía el sistema de barbería y tatuajes con una función para guardar atuendos, que permita a los jugadores almacenar configuraciones completas de apariencia como preajustes con nombre. Cada preajuste guarda el estado completo de la apariencia del jugador, incluidos el peinado y su color, el vello facial, el maquillaje y todos los tatuajes activos. Después, los jugadores pueden cambiar de look desde el guardarropa de su casa o volviendo a la tienda. Guarda los atuendos en la base de datos y vincúlalos al citizen ID del jugador mediante un bloque JSON que contenga todos los datos de apariencia. Limita la cantidad de atuendos guardados por jugador u ofrece espacios adicionales como función prémium. La interacción con el guardarropa de la casa debe mostrar un panel NUI sencillo con una lista de atuendos guardados y una función de vista previa que aplique temporalmente cada look para que el jugador pueda verlo antes de confirmarlo. Así integras el sistema de apariencia personal con el sistema de viviendas y haces que ambas funciones sean más valiosas juntas que por separado.

Ubicaciones de tiendas e integración de barberos NPC

Define varias ubicaciones de tiendas por el mapa, cada una con sus propios servicios disponibles y multiplicadores de precios. Una barbería económica en el sur de la ciudad podría ofrecer cortes básicos a bajo precio, mientras que un salón de lujo en Vinewood cobraría tarifas prémium, pero ofrecería estilos y colores exclusivos. Usa el sistema de interacción o marcadores de proximidad para abrir el menú de la tienda cuando un jugador se acerque al sillón de barbero o al puesto de tatuajes. Sienta al jugador en el sillón usando TaskStartScenarioInPlace con un escenario de sentado para las barberías, o haz que se quede de pie en un punto concreto durante las sesiones de tatuajes. Genera barberos o tatuadores NPC en cada ubicación con modelos de ped adecuados y asígnales animaciones de reposo que den la impresión de que están trabajando. Cuando un jugador interactúe con la tienda, el NPC puede reproducir una animación de trabajo cerca del jugador para simular el servicio y darle más inmersión a la experiencia. Al terminar la sesión, el NPC vuelve a su posición de reposo. Registra los ingresos de cada tienda en la base de datos para permitir que los jugadores sean propietarios de tiendas, donde los emprendedores puedan comprar y gestionar sus propias barberías o estudios de tatuajes, fijar precios y contratar personal NPC o incluso a otros jugadores como empleados.

¿Listo para empezar?

Consigue los scripts en nuestra tienda, o únete al Discord para soporte, novedades y un adelanto de lo que viene.