Cómo Crear una Nueva Interfaz de Usuario en Hytale

Hytale: Guías, Mods, Opiniones | Videojuegos en CIBERED

En esta guía aprenderás cómo mostrar UI personalizada a los jugadores en Hytale.

Explorando el Código de HytaleServer.jar

Información Importante

  • Todos los .ui files deben estar en tu carpeta de recursos del plugin:
    resources/Common/UI/Custom
  • Asegúrate de que tu manifest.json contenga "IncludesAssetPack": true
  • Hytale Client tiene un Diagnostic Mode bajo General, que muestra errores más detallados.

Recursos Útiles

Video Tutorials:

Ejemplos de GitHub:


.ui Files

Hytale actualmente usa .ui files para renderizar UI.

⚠️ Están deprecated, se planea mover a NoesisGUI, pero aún son el método vigente.

Elementos UI

La UI se define usando una estructura tipo árbol, similar a HTML y CSS.

Group {
  TextField #MyInput {
    Style: $Common.@DefaultInputFieldStyle;
    Background: $Common.@InputBoxBackground;
    Anchor: (Top: 10, Width: 200, Height: 50);
  }
}
  • TextField: campo de texto editable por el jugador.
  • Group: contenedor de elementos, similar a un
    .
  • #MyInput: ID obligatorio para acceder al elemento desde Java.

  • Variables

    @MyTex = PatchStyle(TexturePath: "MyBackground.png");
    

    Texturas

    PatchStyle(TexturePath: "MyBackground.png");
    
    • Path relativo al .ui file.
    • Las texturas se ajustan automáticamente al tamaño del elemento.

    Incluir otros .ui files

    $Common = "Common.ui";
    Style: $Common.@DefaultInputFieldStyle;
    

    HUDs

    Un HUD permanece en pantalla siempre, como barra de vida o hotbar.
    No permite interacción con el juego.

    CustomUIHud

    Crea una clase que extienda CustomUIHud y sobrescribe build:

    uiCommandBuilder.append("MyUI.ui"); 
    // El archivo debe estar en resources/Common/UI/Custom/MyUI.ui
    

    Mostrar y ocultar HUD:

    player.getHudManager().setCustomHud(hud);    // Mostrar
    player.getHudManager().hideHudComponent(hud); // Ocultar
    

    UI Pages

    Las UI Pages bloquean la interacción con el juego y permiten usar el cursor del mouse.
    Ejemplos: Crafting, pausa.

    CustomUIPage

    Extiende esta clase si no necesitas recibir input de usuario. Similar a CustomUIHud.

    InteractiveCustomUIPage

    Usa esta clase si necesitas recibir eventos e input del jugador.

    $Common = "Common.ui";
    
    @MyTex = PatchStyle(TexturePath: "MyBackground.png");
    
    Group {
      LayoutMode: Center;
    
      Group #MyPanel {
        Background: @MyTex;
        Anchor: (Width: 800, Height: 1000);
        LayoutMode: Top;
    
        Label #MyLabel {
          Style: (FontSize: 32, Alignment: Center);
          Anchor: (Top: 50);
          Text: "MyText";
          Padding: (Full: 10);
        }
    
        TextField #MyInput {
          Style: $Common.@DefaultInputFieldStyle;
          Background: $Common.@InputBoxBackground;
          Anchor: (Top: 10, Width: 200, Height: 50);
          Padding: (Full: 10);
        }
      }
    }
    

    InteractiveCustomUIPage – Clase de Datos

    public static class Data {
      public static final BuilderCodec CODEC = BuilderCodec.builder(Data.class, Data::new)
        .append(new KeyedCodec<>("@MyInput", Codec.STRING), (data, value) -> data.value = value, data -> data.value)
        .add()
        .build();
    
      private String value; // Valor del TextField
    }
    
    • PlayerRef: jugador que verá la UI
    • CustomPageLifetime: controla si puede cerrar la UI
    • BuilderCodec: serializa/deserializa datos del cliente al servidor

    Construyendo la UI

    @Override
    public void build(@Nonnull Ref ref, @Nonnull UICommandBuilder uiCommandBuilder,
                      @Nonnull UIEventBuilder uiEventBuilder, @Nonnull Store store) {
    
        uiCommandBuilder.append("MyUI.ui");
        uiEventBuilder.addEventBinding(
            CustomUIEventBindingType.ValueChanged,
            "#MyInput",
            EventData.of("@MyInput", "#MyInput.Value"),
            false
        );
    }
    
    • Escucha cambios en el valor de #MyInput y lo mapea a @MyInput.

    Manejar Eventos de Datos

    @Override
    public void handleDataEvent(@Nonnull Ref ref, @Nonnull Store store, Data data) {
      super.handleDataEvent(ref, store, data);
      System.out.println("EVENT: " + data.value);
      sendUpdate(); // Actualiza la UI para el cliente
    }
    

    ⚠️ Siempre llamar sendUpdate() después de un cambio, de lo contrario la UI se queda cargando.


    Abrir UI Pages

    player.getPageManager().openCustomPage(ref, store, new MyUI(playerRef));
    

    Actualizar UI Dinámicamente

    Ejemplo: cambiar el texto de un Label en tiempo real:

    public void updateText(String newText) {
      UICommandBuilder uiCommandBuilder = new UICommandBuilder();
      uiCommandBuilder.set("#MyLabel.TextSpans", Message.raw(newText));
      update(false, uiCommandBuilder); // false = mantener UI existente
    }
    

    Problemas Comunes

    • Failed to apply custom ui hud commands
      → Tu .ui file tiene errores de sintaxis o path incorrecto.

    • Could not find document XXXXX for Custom UI Append command
      → La ruta del .ui file no coincide con la especificada en tu código.