CommunityProgramación y desarrollogithub.com

MericKarabulut/unity-ui-toolkit

Crea y depura UI de juegos en Unity con UI Toolkit (UXML + USS + controladores C#) para cualquier género y plataforma: HUDs, barras de salud/municiones/resistencia, indicadores de interacción, viñetas de daño, menús, pantallas de pausa, contadores de puntuación, pantallas de escenas. Cubre el flujo de trabajo más reciente con PanelRenderer (sin UIDocument) y el clásico UIDocument, redacción de una guía de estilo de UI por juego, diseño sin adivinar píxeles y corrección de problemas de UI Builder / USS / UXML. Úsalo siempre que el usuario mencione UI de Unity, UI Toolkit, UXML, USS, PanelRenderer, UIDocument, UI Builder, un HUD, barra de salud, contador de municiones o menú, o quiera reestilizar, arreglar o agregar cualquier interfaz en el juego en Unity, incluso si nunca dice 'UI Toolkit'.

¿Qué es unity-ui-toolkit?

Esta habilidad te permite construir, depurar y mantener sistemas de interfaz de usuario (UI) para juegos en Unity utilizando UI Toolkit, estructurando la UI con UXML, el estilo con USS y el comportamiento con pequeños controladores C# que reaccionan a eventos del juego. Está diseñada para cualquier género (desde HUDs de acción hasta menús y pantallas de corte) y plataforma (PC, móvil, consola, VR). La habilidad guía en la elección entre el flujo de trabajo moderno con PanelRenderer y el clásico con UDocument, enfatizando la creación de una guía de estilo antes de construir múltiples partes para garantizar consistencia visual. Incluye patrones de arquitectura sólidos: un controlador por parte, actualización por eventos en lugar de sondeo, y gestión de estado mediante clases CSS en lugar de código. También proporciona fragmentos reutilizables para elementos comunes como segmentos, pulsos y viñetas, y una completa tabla de solución de problemas que cubre errores frecuentes como referencias nulas, elementos sin estilo o UXML inválido. Es útil siempre que el usuario mencione UI de Unity, UI Toolkit, UXML, USS, PanelRenderer, UIDocument, UI Builder, un HUD, barra de salud, contador de municiones o menú, o quiera reestilizar, arreglar o agregar cualquier interfaz en el juego en Unity, incluso si nunca dice 'UI Toolkit'.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/MericKarabulut/claude-skills/tree/HEAD/skills/unity-ui-toolkit

Installed? Explore more Programación y desarrollo skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

UI de juego con Unity UI Toolkit

Estructura en UXML, apariencia en USS, comportamiento en pequeños controladores C# que reaccionan a eventos del juego. Destilado de un proyecto real (HUD de un juego de terror-disparos); la lista de solución de problemas es la parte que probablemente ahorrará más tiempo.

1. Antes de escribir código

Pregunta solo por lo que falte, en un lote breve:

  • API de UI. [RequireComponent(typeof(PanelRenderer))] + RegisterUIReloadCallback = flujo de trabajo con PanelRenderer (sin UIDocument, sin rootVisualElement). UIDocument + rootVisualElement = clásico. Infierelo del código pegado; si no está claro, pide un controlador funcional y su UXML. Nunca mezcles los dos (provoca NullReferenceExceptions). Un archivo funcional del usuario es mejor que tu memoria de la API.
  • Género/tema (con capturas de pantalla o paleta), plataforma y entrada, resolución/aspecto de referencia.
  • Los scripts del juego a los que la UI escucha. Pídelos, lee los eventos/propiedades públicos reales, vincula a esos. Si falta un evento, propón la adición más pequeña al script del juego.
  • Responde en el idioma del usuario; escribe comentarios de código en el idioma que usen los scripts existentes.

2. Primero la guía de estilo

La consistencia es lo que los usuarios notan primero. Antes de construir muchas partes: propón una guía breve, construye UNA parte pequeña, deja que el usuario reaccione y convierte sus comentarios en reglas ("sin fondo de caja", "más compacto"). Guárdala en el proyecto (ej. Assets/UI/UI_STYLE_GUIDE.md) y dile al usuario que la pegue en futuras solicitudes de UI. Actualízala cuando cambien partes o reglas. Contenido: lenguaje de diseño (tono, contenedores, densidad, movimiento), tabla de paleta (texto, atenuado, advertencia, crítico, sombra), tipografía (tamaños por rol, regla de sombra), diseño (zonas, márgenes, configuración del panel), convenciones de nomenclatura de clases USS, reglas de arquitectura de código, errores comunes de UXML, lista de partes existentes, lista de verificación para una nueva parte.

3. Arquitectura

  • Una parte = un controlador + un UXML + un USS, con una fuente de datos. Sin mega-controlador.
  • Las partes que deben estar juntas comparten un UXML/contenedor flex (dos controladores pueden compartir un GameObject/PanelRenderer; cada uno encuentra sus propios elementos por nombre).
  • Los controladores solo encuentran elementos, establecen texto/valores y alternan clases USS. Los colores, tamaños y animaciones viven en USS.
  • PanelRenderer: la raíz llega a través de RegisterUIReloadCallback(OnUIReload) (registrar en OnEnable, anular en OnDisable). Se dispara cuando la UI está lista y en cada recarga, así que todas las búsquedas de Q<T>("nombre"), almacenamiento en caché, enlace de eventos y restablecimiento de estado van allí. Protege Update() con nulo hasta que los elementos existan.
  • Los eventos del juego pueden dispararse antes de que exista la UI: almacena en caché los últimos valores en campos, re-aplica al final de OnUIReload.
  • Los objetos del jugador/juego pueden aparecer tarde: referencia serializada, si no FindFirstObjectByType con limitación (~0.5 s), luego enlaza; cancela la suscripción en OnDisable.
  • Empuja, no sondees. Suscríbete a eventos y escribe solo al cambiar. Sondea solo valores sin evento (resistencia continua, un bool de recarga), alternando clases al cambiar. Sin construcción de cadenas por fotograma.
  • Construye elementos repetidos (pipetas, segmentos) en código una vez; reconstruye solo cuando su fuente cambie; limpia las listas en caché al recargar.
  • Temporizadores/pulsos: element.schedule.Execute(...).Every(ms) se crea en pausa, se reanuda solo mientras sea necesario. Destellos de un solo disparo: .StartingIn(ms).
  • Elementos HUD: picking-mode="Ignore" en todas partes, sin controles enfocables. Los menús son la excepción.
  • Nombre del archivo = nombre de la clase MonoBehaviour.

4. Diseño (fuente de la mayoría de los errores visuales)

  • Nunca coloques partes relacionadas con compensaciones de píxeles adivinadas por separado: dos paneles absolutos superpuestos porque bottom se estimó a ciegas. Ponlos en un contenedor flex y posiciona solo ese contenedor.
  • Un contenedor posicionado absolutamente por zona de pantalla; los hijos fluyen con flex y márgenes.
  • Oculta con opacity + transición cuando el diseño no deba saltar; display:none solo cuando el espacio deba colapsarse.
  • width fija en etiquetas numéricas para que 9 a 10 a 100 no desplace a los vecinos.
  • Las cajas son border-box: ancho de fila de segmentos = count x (ancho + margen) - margen. Alinea widgets relacionados por el mismo borde izquierdo y ancho calculado.
  • Configuración del panel: Scale With Screen Size (ej. 1920x1080, match 0.5); para HUDs desactiva Clear Color y Clear Depth Stencil. Usa el Sort Order del PanelRenderer para superponer partes (viñeta de pantalla completa debajo del texto).

5. USS y estado

  • Estado = clase alternada desde C# (EnableInClassList); animación = transición en USS. Nunca establezcas colores o animaciones en código.
  • Nombra los estados por significado: --bajo, --critico, --oculto, --golpe, --pulso, --no-disponible. Prefiere BEM para partes nuevas.
  • Las reglas de anulación (segmentos gastados, destello) van DESPUÉS de las reglas que anulan; misma especificidad, gana la última.
  • Una transición usa la duración/retardo del estado destino: .x--oculto { opacity:0; transition-duration:.5s; transition-delay:.9s; } se desvanece tarde y entra al instante; .x--destello { transition-duration:.05s; } se activa rápido, se desvanece a velocidad base.
  • Texto flotante sin fondo necesita text-shadow. Sin text-transform (mayúsculas en C#); sin degradados radiales (ver viñeta).
  • Para UI reeskinizable coloca colores/tamaños en variables USS en una clase raíz (--c-texto, usado como var(--c-texto)); verifica que la versión de Unity del usuario lo soporte.

6. Esqueleto del controlador (PanelRenderer)

[RequireComponent(typeof(PanelRenderer))]
public class ControladorParte : MonoBehaviour
{
    [SerializeField] private ComponenteFuente fuente;      // vacío = buscar en tiempo de ejecución
    private PanelRenderer _pr; private VisualElement _bloque; private Label _valor;
    private bool _enlazado; private float _v = -1f; private int _mostrado = -1; private float _siguienteBusqueda;

    private void Awake() => _pr = GetComponent<PanelRenderer>();
    private void OnEnable()  { _pr.RegisterUIReloadCallback(OnUIRecarga); IntentarEnlazar(); }
    private void OnDisable() { _pr.UnregisterUIReloadCallback(OnUIRecarga); Desenlazar(); _bloque = null; _valor = null; }

    private void Update()
    {
        if (_enlazado || Time.unscaledTime < _siguienteBusqueda) return;
        _siguienteBusqueda = Time.unscaledTime + 0.5f; IntentarEnlazar();
    }
    private void IntentarEnlazar()
    {
        if (_enlazado) return;
        if (fuente == null) fuente = FindFirstObjectByType<ComponenteFuente>();
        if (fuente == null) return;
        fuente.AlCambiar += AlCambiar; _enlazado = true;
        _v = fuente.Actual; Aplicar();                      // obtener el estado actual una vez
    }
    private void Desenlazar() { if (_enlazado && fuente != null) fuente.AlCambiar -= AlCambiar; _enlazado = false; }

    private void OnUIRecarga(PanelRenderer r, VisualElement raiz)
    {
        _bloque = raiz.Q<VisualElement>("parte"); _valor = raiz.Q<Label>("parte-valor");
        if (_bloque == null || _valor == null) { Debug.LogError("Parte: elementos no encontrados en UXML (revisar Source Asset).", this); _bloque = null; return; }
        _mostrado = -1; Aplicar();                              // nuevo árbol: forzar reescritura desde caché
    }
    private void AlCambiar(float v) { _v = v; Aplicar(); }
    private void Aplicar()
    {
        if (_bloque == null || _v < 0f) return;
        int n = Mathf.CeilToInt(_v);
        if (n != _mostrado) { _mostrado = n; _valor.text = n.ToString(); }   // escribir solo al cambiar
        _bloque.EnableInClassList("parte--bajo", _v <= 0.3f);
    }
}

7. Fragmentos reutilizables

  • Segmentos/pipetas: en OnUIRecarga, fila.Clear(); lista.Clear(); luego agrega N VisualElement{pickingMode=Ignore} con clase seg; al cambiar lista[i].EnableInClassList("seg--gastado", i >= lleno). Muestra al menos 1 lleno mientras valor > 0: lleno = v<=0 ? 0 : Clamp(CeilToInt(v*N-0.001f),1,N).
  • Pulso: _pulso = _bloque.schedule.Execute(() => _bloque.ToggleInClassList("parte--pulso")).Every(400); _pulso.Pause(); Reanudar al entrar en el estado, Pausar + eliminar clase al salir; rastrea un bool _pulsando y restablécelo al recargar.
  • Destello en evento: var b=_bloque; b.AddToClassList("parte--golpe"); b.schedule.Execute(()=>b.RemoveFromClassList("parte--golpe")).StartingIn(150);
  • Viñeta: genera una vez en Awake un Texture2D de 128x128 (RGBA32, Clamp, Bilinear, HideFlags.HideAndDontSave) donde alfa = SmoothStep(0,1,InverseLerp(interior,exterior,dist/1.4142)) desde el centro, RGB blanco; establece _el.style.backgroundImage = new StyleBackground(tex) en OnUIRecarga; Destruir en OnDestroy. USS: elemento absoluto de pantalla completa, opacity:0, -unity-background-scale-mode: stretch-to-fill, -unity-background-image-tint-color para el color, clases --bajo/--critico/--pulso/--destello estableciendo opacidad (destello último, duración rápida). Sort Order más bajo que las partes de texto.

8. Géneros y plataformas

La arquitectura es idéntica en todos los géneros; la guía de estilo y el modelo de entrada cambian. Arcade: números grandes y gruesos, cuenta ascendente, pops de escala, brillo de combo, transiciones contundentes de 0.08-0.25 s, los contenedores son bienvenidos. Retro/píxel: fuente de píxeles, escalado entero, texturas con filtro Point, bordes de 9 cortes de sprite. Móvil: objetivos táctiles de aproximadamente 44-48 dp, área segura del dispositivo como relleno de la raíz (verifica la API actual para la versión de Unity del usuario), sin estados hover, fuente mínima más grande, probar en un dispositivo real. Consola/gamepad: elementos enfocables con un estilo :focus fuerte, foco inicial explícito, márgenes seguros para TV (3-5 por ciento), texto más grande, glifos que cambian según el dispositivo activo. VR/espacio mundial: depende de la versión de Unity, búscalo primero. Estos puntos de plataforma son una buena práctica general pero no probados en un proyecto real: díselo y pide al usuario que verifique en el dispositivo. Mantén las etiquetas de entrada (nombre de tecla o glifo) en datos que el controlador lea, no codificados en UXML.

9. Entrega de una parte

En orden: UXML completo, USS completo, controlador completo, pasos de configuración (GameObject, PanelRenderer + Panel Settings compartido + Source Asset, Sort Order, referencias a rellenar, qué ver en Play), perillas de ajuste (qué valor de USS cambia tamaño/posición/color/umbrales) y una entrada breve en la guía de estilo. Al cambiar archivos existentes, proporciona archivos completos si la mayor parte del archivo cambia (los pegado parciales causaron errores reales).

10. Solución de problemas

SíntomaCausa / solución
NullReference en rootVisualElement / GetComponent<UIDocument>()El código usa UIDocument pero el objeto tiene PanelRenderer. Usa RegisterUIReloadCallback, enlaza en OnUIReload
El controlador registra "elemento no encontrado"Source Asset incorrecto, nombre no coincide con Q<>(), o UXML inválido
UI Builder "Failed to open asset"UXML inválido (duplicado </ui:UXML>, <Style> incorrecto, GUID de marcador de posición, caracteres ocultos). Arregla, luego haz clic derecho en el archivo > Reimport. Lee el mensaje de la consola encima del error genérico
Elementos sin estilo<Style> faltante o ruta incorrecta. En UI Builder: StyleSheets > + > Add Existing USS (escribe project://database/...guid=... completo)
Todo desapareció después de una ediciónUXML se rompió, o la referencia a Source Asset quedó obsoleta después de un renombrado. Arregla, Reimport, reasigna. Prueba con background-color: rgba(255,0,0,.3) en la raíz
Parte mal colocada o superponiéndose a otraGameObject obsoleto que sigue dibujando UXML antiguo, o desplazamientos absolutos separados. Elimina el objeto antiguo, fusiona vecinos en un contenedor
Texto de marcador de posición en la vista GameLa vista Edit dibuja el UXML; presiona Play
No se puede agregar el scriptNombre de archivo != nombre de clase. Renombra dentro de la ventana Project de Unity para que .meta lo siga

Correcciones no válidas: la etiqueta de hoja de estilo es <Style src="..."/> SIN el prefijo ui: (<ui:Style> se ignora silenciosamente); nunca inventes o dejes un GUID de marcador de posición; usa el encabezado estándar <ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">.

11. Trabajar sin ver el editor

Pide una captura de pantalla en quejas de diseño y léela detenidamente; resolvió errores que la adivinanza no podía. Reconoce tus errores brevemente y arréglalos con archivos completos. Para errores de compilación, pide el texto exacto y la versión de Unity en lugar de adivinar de nuevo. No prometas comportamiento o rendimiento en el dispositivo; construye la estructura y pide al usuario que pruebe.

Skills relacionados