Saltearse al contenido

Convenciones de UI / NUI

Cada recurso tiene su propia página NUI (ui_page 'html/index.html'). bs_ui es el HUD, las notificaciones y las partes compartidas.

Un panel a la vez: bs:ui:opened / bs:ui:closed

Sección titulada «Un panel a la vez: bs:ui:opened / bs:ui:closed»

Cuando se abre tu panel, lo anuncias. Cualquier otro panel lo oye y se cierra. Cuando se cierra el tuyo, también lo anuncias. Código real de bs_config:

local function close()
if not isOpen then return end
isOpen = false
SetNuiFocus(false, false)
SendNUIMessage({ action = 'close' })
TriggerEvent('bs:ui:closed', 'config')
end
local function open()
if isOpen then return close() end
isOpen = true
SetNuiFocus(true, true)
SendNUIMessage({ action = 'open', data = data })
TriggerEvent('bs:ui:opened', 'config')
end
-- another UI took over: step aside
AddEventHandler('bs:ui:opened', function(what)
if what ~= 'config' and isOpen then close() end
end)
AddEventHandler('onResourceStop', function(res)
if res == GetCurrentResourceName() and isOpen then
SetNuiFocus(false, false)
TriggerEvent('bs:ui:closed', 'config')
end
end)

Reglas:

  • Pasa el nombre de tu panel en ambos eventos. Los handlers lo comprueban; un handler que reaccionaba a cualquier cierre rompió el mapa de campo durante meses.
  • Emparéjalos en cada salida: botón de cerrar, Esc, tecla de alternar, otra UI que se abre, parada del recurso. Si fallas una, el jugador se queda atascado con un cursor de ratón y sin juego.
  • Anuncia opened cuando ya estés abierto, y ciérrate desde tu propio handler, no asumas que otros lo harán.

/refreshui (bs_ui) es el botón de pánico: cierra todo, suelta el foco y dispara bs:ui:closed con nil.

-- client
TriggerEvent('bs:ui:notify', { type = 'info', title = 'Radio', message = 'Signal found.', duration = 3000 })
-- server
TriggerClientEvent('bs:ui:notify', src, { type = 'error', title = 'Denied', message = 'Nope.' })

Tipos usados en el código: info, success, error.

Las páginas están maquetadas para 1920x1080. Incluye el escalador compartido para que se ajusten a cualquier resolución:

<script src="https://cfx-nui-bs_ui/html/shared/scale.js"></script>

Escala <body> con un transform: --ui-scale = clamp(min(w/1920, h/1080), 0.6, 1.25). Los jugadores pueden cambiarlo (0.6 a 1.4) en los ajustes.

Cosas que debes saber al usarlo:

  • En el CSS de la página escribe vh/vw como calc(Nvh / var(--ui-scale, 1)), para que sigan siendo fracciones reales del viewport.
  • El JS que define style.left/top a partir de clientX/clientY o de un bounding rect debe dividir por BSScale.get(). BSScale.w() / BSScale.h() dan el viewport local.
  • Desactívalo con <html data-bs-noscale>.
  • html, body { background: transparent !important; } en cada página. Si no, aparece una pantalla negra sobre el juego.
  • Sin mensajes NUI cada frame en reposo. Envía al cambiar, o limita (el HUD del dron envía cada 150 ms mientras vuela, eso es más o menos el máximo).
  • La paleta es ámbar / óxido / hueso / polvo sobre casi negro. Reutiliza los tokens CSS de bs_ui y bs_menu/html/css/faction-hub.css. Sin fuentes externas ni CDNs: puede que los jugadores no tengan acceso a internet para ellas, y además es cuestión de privacidad.
  • Iconos: iconos de línea en bs_ui/html/js/icons.js.
  • Sonidos: bs_ui/html/js/sound.js gestiona los sonidos posicionales (maxDistance, panning).