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 <div>.
  • #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<Data> 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<Data>: serializa/deserializa datos del cliente al servidor

Construyendo la UI

@Override
public void build(@Nonnull Ref<EntityStore> ref, @Nonnull UICommandBuilder uiCommandBuilder,
                  @Nonnull UIEventBuilder uiEventBuilder, @Nonnull Store<EntityStore> 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<EntityStore> ref, @Nonnull Store<EntityStore> 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.