Saltar al contenido
Docs

Servicio interfaz

Referencia del servicio interfaz. Lo que ve cada jugador sobre el juego: HUD, mensajes, cronómetros, marcador, menús y rótulos en el mundo.

Corresponde aFormato 1, API 1 Para quien escribe código Esta página se genera desde las fuentes del SDK.

En esta página

Lo que ve cada jugador sobre el juego: HUD, mensajes, cronómetros, marcador, menús y rótulos en el mundo.

Los tipos que aparecen en las firmas (Vec3, Texto, el selector) están en la referencia de la API.

Funciones y propiedades de interfaz#

interfaz.t#

ts
interfaz.t(clave: string, vars?: Record<string, string | number>): TextoClave

Arma un texto traducible a partir de una clave de textos del manifiesto.

De docs/EJEMPLOS.md, «mundo: zonas»:

ts
mundo.alSalir('zona-segura', (_zona, j) => interfaz.mensaje(interfaz.t('cuidado'), { para: j, tipo: 'peligro' }));

interfaz.hud#

ts
interfaz.hud(id: string, elemento: { texto: Texto; icono?: Icono; lugar?: LugarHud; estilo?: 'normal' | 'destacado' | 'peligro' } | null, para?: Jugador): void

Pone o actualiza un elemento del HUD. Con null lo quita.

De docs/EJEMPLOS.md, «interfaz: mensajes que no estorban, menú con opción deshabilitada, textos anidados»:

ts
// Textos mas cortos en telefonos verticales.
jugadores.alCambiarPantalla((j) => interfaz.hud('ayuda', { texto: interfaz.t(j.pantalla.vertical ? 'ayudaCorta' : 'ayuda'), lugar: 'abajo-centro' }, j));

interfaz.cronometro#

ts
interfaz.cronometro(id: string, opciones?: { lugar?: LugarHud; cuentaRegresiva?: number; alTerminar?: () => void; para?: Jugador }): Cronometro

Cronómetro en el HUD. Lo dibuja el reproductor cuadro a cuadro: no hay que actualizarlo.

De docs/EJEMPLOS.md, «interfaz: mensajes que no estorban, menú con opción deshabilitada, textos anidados»:

ts
interfaz.cronometro('reloj', { cuentaRegresiva: 90, formato: 'segundos' });

interfaz.mensaje#

ts
interfaz.mensaje(texto: Texto, opciones?: { duracion?: number; tipo?: 'info' | 'exito' | 'peligro'; para?: Jugador }): void

Mensaje breve. Por defecto, grande y al centro. lugar lo sube o lo baja; tamano: 'chico' no tapa el juego; cola: true espera a que termine el mensaje anterior en vez de apilarse.

De docs/EJEMPLOS.md, «mundo: zonas»:

ts
mundo.alSalir('zona-segura', (_zona, j) => interfaz.mensaje(interfaz.t('cuidado'), { para: j, tipo: 'peligro' }));

interfaz.marcador#

ts
interfaz.marcador(def: { titulo: Texto; filas: { nombre: Texto; valor: Texto | number; destacado?: boolean }[] } | null, para?: Jugador): void

Tabla a la vista (posiciones, vueltas). Con null la quita. Sin llamar a esto no hay marcador.

De docs/EJEMPLOS.md, «interfaz: marcador»:

ts
reloj.cada(0.5, () => {
  const filas = jugadores.todos()
    .sort((a, b) => b.puntos - a.puntos)
    .map((j) => ({ nombre: j.nombre, valor: j.puntos, destacado: !j.esBot }));
  interfaz.marcador({ titulo: interfaz.t('posiciones'), filas });
});

interfaz.menu#

ts
interfaz.menu(def: { id: string; titulo: Texto; texto?: Texto; opciones: { id: string; texto: Texto }[] }, para?: Jugador): void

Abre un menú de opciones para un jugador (o para todos). La partida no se pausa.

De docs/EJEMPLOS.md, «interfaz: mensajes que no estorban, menú con opción deshabilitada, textos anidados»:

ts
interfaz.menu({ id: 'tienda', titulo: interfaz.t('tienda'), opciones: [
  { id: 'botas', texto: interfaz.t('botas') },
  { id: 'alas', texto: interfaz.t('alas'), deshabilitada: true }
] });

interfaz.cerrarMenu#

ts
interfaz.cerrarMenu(id: string, para?: Jugador): void

Cierra el menú con ese id, para un jugador o para todos.

De games/defensa-del-faro/scripts/principal.ts:

ts
interfaz.cerrarMenu('inicio');

interfaz.alElegir#

ts
interfaz.alElegir(fn: (menu: string, opcion: string, jugador: Jugador) => void): void

Un jugador eligió una opción. El menú ya se cerró.

De docs/EJEMPLOS.md, «interfaz: menú»:

ts
interfaz.alElegir((menu, opcion, j) => {
  if (menu !== 'camino') return;
  j.teletransportar(opcion === 'corto' ? 'inicio-corto' : 'inicio-largo');
  j.congelado = false;
});

interfaz.rotulo#

ts
interfaz.rotulo(id: string, def: { en: Vec3 | Pieza | Actor | Jugador; texto?: Texto; barra?: number; color?: string; altura?: number; estilo?; alcance?: number } | null, para?: Jugador): void

Pone, actualiza o (con null) quita un rótulo en el mundo: texto y barra sobre una pieza, un actor, un jugador o un punto.

De docs/EJEMPLOS.md, «interfaz: rótulos, barras y números en el mundo»:

ts
const pintar = () => interfaz.rotulo('vida-torre', { en: torre, texto: interfaz.t('torre'), barra: (torre.datos.vida as number) / 10, color: 'coral', altura: 1 });

interfaz.flotante#

ts
interfaz.flotante(texto: Texto, en: Vec3 | Pieza | Actor | Jugador, opciones?: { tipo?; duracion?: number; para?: Jugador }): void

Texto que sale de un punto del mundo, sube y se desvanece ("+10", "-3").

De docs/EJEMPLOS.md, «interfaz: rótulos, barras y números en el mundo»:

ts
mundo.alTocar('torre', (_torre, j) => {
  torre.datos.vida = (torre.datos.vida as number) - 1;
  interfaz.flotante('-1', torre, { tipo: 'peligro', para: j });
  if ((torre.datos.vida as number) <= 0) interfaz.rotulo('vida-torre', null);
  else pintar();
});

interfaz.marcadorDePuntos#

ts
interfaz.marcadorDePuntos(def: { titulo: Texto } | null): void

Marcador automático con el contador puntos de cada jugador, ordenado. Con null lo quita.

De docs/EJEMPLOS.md, «interfaz: mensajes que no estorban, menú con opción deshabilitada, textos anidados»:

ts
interfaz.marcadorDePuntos({ titulo: interfaz.t('puntos') });

interfaz.barraRapida#

ts
interfaz.barraRapida(def: { ranuras: { color?: string; icono?: Icono; texto?: Texto; cantidad?: number }[] } | null, para?: Jugador): void

Barra rápida: una fila de hasta 9 ranuras abajo al centro. La elegida es jugador.ranura. En pantallas táctiles cada ranura se toca. Con null se quita.

De games/valle-abierto/scripts/comun.ts:

ts
interfaz.barraRapida({ ranuras }, j);

interfaz.marcarBloque#

ts
interfaz.marcarBloque(posicion: Vec3 | null, opciones?: { avance?: number; color?: string }, para?: Jugador): void

Marca un bloque del terreno con un recuadro (el bloque apuntado). avance de 0 a 1 muestra cuánto falta para romperlo. Con null se quita. Cada jugador tiene su marca.

De games/valle-abierto/scripts/principal.ts:

ts
interfaz.marcarBloque(a.posicion, { avance, color }, j);

Cronometro#

Un cronómetro del HUD: lo que devuelve interfaz.cronometro.

PropiedadTipoDescripción
cronometro.valornumberSolo lectura. Segundos transcurridos (o restantes, si es cuenta regresiva).
cronometro.corriendobooleanSolo lectura. true mientras avanza.

cronometro.iniciar#

ts
cronometro.iniciar(): void

Lo pone a correr.

De docs/EJEMPLOS.md, «interfaz: HUD, mensajes y cronómetro»:

ts
juego.alIniciar(() => {
  const crono = interfaz.cronometro('tiempo', {
    cuentaRegresiva: 60,
    alTerminar: () => juego.terminar({ resultado: 'derrota' })
  });
  crono.iniciar();

  jugadores.alEntrar((j) => {
    interfaz.hud('puntos', { icono: 'estrella', texto: interfaz.t('puntos', { n: 0 }), lugar: 'arriba-izquierda' }, j);
  });
  interfaz.mensaje(interfaz.t('objetivo'), { duracion: 3 });
});

cronometro.detener#

ts
cronometro.detener(): void

Lo detiene.

De games/circuito-luz-verde/scripts/principal.ts:

ts
let crono: Cronometro;
// ...
crono.detener();

cronometro.reiniciar#

ts
cronometro.reiniciar(): void

Vuelve a 0 (o al total de la cuenta regresiva) y lo detiene.

cronometro.sumar#

ts
cronometro.sumar(segundos: number): void

Suma segundos (una penalización) o los resta con un número negativo.

De games/isla-taller/scripts/principal.ts:

ts
let crono: Cronometro;
// ...
crono.sumar(-AJUSTES.bono);

cronometro.quitar#

ts
cronometro.quitar(): void

Lo quita del HUD.

De games/defensa-del-faro/scripts/principal.ts:

ts
let crono: Cronometro | null = null;
// ...
crono?.quitar();

Texto#

Un Texto es interfaz.t('clave', { n: 3 }) (lo recomendado: lo traduce cada reproductor), { es, en } o una cadena tal cual (solo para números y nombres propios). interfaz.t devuelve un objeto, no una cadena: no lo concatenes; pon las variables en la plantilla ("monedas": "{n} de {total}").

ts
type Texto = string | { es: string; en: string } | TextoClave;
interface TextoClave {
  clave: string;
  vars?: Record<string, string | number | TextoClave | { es: string; en: string }>;
}

Notas de comportamiento#

  • Iconos: moneda, reloj, corazon, estrella, bandera, llave, rayo, jugador, escudo, espada, martillo, casa, gota, hoja, fuego, engranaje, diana, diamante, calavera, flecha, caja, pregunta, luz, ola.
  • Lugares: arriba-izquierda, arriba-centro, arriba-derecha, abajo-izquierda, abajo-centro, abajo-derecha.
  • El cronómetro lo dibuja el reproductor cuadro a cuadro: no hay que actualizarlo.
  • Un menú no pausa la partida. Si quieres que nadie se mueva, congela a los jugadores. Una opción con deshabilitada: true se ve pero no se puede elegir. El menú hace scroll si no cabe.
  • interfaz.mensaje admite lugar ('centro', 'arriba', 'abajo'), tamano ('chico', 'normal', 'grande') y cola: true (espera a que termine el mensaje anterior en vez de apilarse). Un mensaje chico arriba no tapa el juego.
  • interfaz.cronometro admite formato: 'centesimas' (1:23.45, por defecto), 'segundos' (1:23) o 'entero' (83). cronometro.sumar(n) suma n al tiempo transcurrido: en una cuenta regresiva, sumar(5) quita 5 segundos de los que quedan y sumar(-5) regala 5.
  • Una variable de interfaz.t puede ser otro texto traducible: interfaz.t('ganaste', { premio: interfaz.t('copa') }).
  • interfaz.rotulo pone texto, una barra (barra de 0 a 1), o las dos cosas, sobre una pieza, un actor, un jugador o un punto del mundo; sigue a lo que esté pegado. Con el mismo id se actualiza; con null se quita.
  • interfaz.flotante muestra un texto que sale de un punto, sube y se desvanece ("+10").
  • jugador.puntos no se dibuja solo. interfaz.marcadorDePuntos({ titulo }) muestra un marcador automático con los puntos de cada jugador; o dibuja los tuyos con interfaz.hud o interfaz.marcador.
  • El HUD hace salto de línea en pantallas angostas, y el marcador va en la columna de arriba a la derecha, bajo los chips, sin montarse.
  • La pantalla de inicio (portada, dificultad, controles), la pausa y el panel de fin los pone el reproductor: el juego no los programa.

Ejemplos completos#

Scripts completos de docs/EJEMPLOS.md. Cada uno supone una escena con lo que nombra, y las claves de texto (interfaz.t('...')) deben existir en textos de juego.json.

interfaz: HUD, mensajes y cronómetro#

scripts/principal.ts
import { interfaz, juego, jugadores } from '@greengames/sdk';

juego.alIniciar(() => {
  const crono = interfaz.cronometro('tiempo', {
    cuentaRegresiva: 60,
    alTerminar: () => juego.terminar({ resultado: 'derrota' })
  });
  crono.iniciar();

  jugadores.alEntrar((j) => {
    interfaz.hud('puntos', { icono: 'estrella', texto: interfaz.t('puntos', { n: 0 }), lugar: 'arriba-izquierda' }, j);
  });
  interfaz.mensaje(interfaz.t('objetivo'), { duracion: 3 });
});

interfaz: menú#

scripts/principal.ts
import { interfaz, juego, jugadores } from '@greengames/sdk';

juego.alIniciar(() => {
  for (const j of jugadores.todos()) j.congelado = true;       // un menú no pausa la partida
  interfaz.menu({
    id: 'camino',
    titulo: interfaz.t('elige'),
    opciones: [{ id: 'corto', texto: interfaz.t('caminoCorto') }, { id: 'largo', texto: interfaz.t('caminoLargo') }]
  });
  interfaz.alElegir((menu, opcion, j) => {
    if (menu !== 'camino') return;
    j.teletransportar(opcion === 'corto' ? 'inicio-corto' : 'inicio-largo');
    j.congelado = false;
  });
});

interfaz: marcador#

scripts/principal.ts
import { interfaz, juego, jugadores, reloj } from '@greengames/sdk';

juego.alIniciar(() => {
  reloj.cada(0.5, () => {
    const filas = jugadores.todos()
      .sort((a, b) => b.puntos - a.puntos)
      .map((j) => ({ nombre: j.nombre, valor: j.puntos, destacado: !j.esBot }));
    interfaz.marcador({ titulo: interfaz.t('posiciones'), filas });
  });
});

interfaz: rótulos, barras y números en el mundo#

scripts/principal.ts
import { interfaz, juego, mundo } from '@greengames/sdk';

juego.alIniciar(() => {
  const torre = mundo.pieza('torre');
  torre.datos.vida = 10;
  const pintar = () => interfaz.rotulo('vida-torre', { en: torre, texto: interfaz.t('torre'), barra: (torre.datos.vida as number) / 10, color: 'coral', altura: 1 });
  pintar();
  mundo.alTocar('torre', (_torre, j) => {
    torre.datos.vida = (torre.datos.vida as number) - 1;
    interfaz.flotante('-1', torre, { tipo: 'peligro', para: j });
    if ((torre.datos.vida as number) <= 0) interfaz.rotulo('vida-torre', null);
    else pintar();
  });
});

interfaz: mensajes que no estorban, menú con opción deshabilitada, textos anidados#

scripts/principal.ts
import { interfaz, juego, jugadores } from '@greengames/sdk';

juego.alIniciar(() => {
  interfaz.mensaje(interfaz.t('pista'), { lugar: 'arriba', tamano: 'chico', cola: true, duracion: 4 });
  interfaz.mensaje(interfaz.t('ganaste', { premio: interfaz.t('copa') }), { tamano: 'grande', tipo: 'exito' });
  interfaz.cronometro('reloj', { cuentaRegresiva: 90, formato: 'segundos' });
  interfaz.marcadorDePuntos({ titulo: interfaz.t('puntos') });
  interfaz.menu({ id: 'tienda', titulo: interfaz.t('tienda'), opciones: [
    { id: 'botas', texto: interfaz.t('botas') },
    { id: 'alas', texto: interfaz.t('alas'), deshabilitada: true }
  ] });
  // Textos mas cortos en telefonos verticales.
  jugadores.alCambiarPantalla((j) => interfaz.hud('ayuda', { texto: interfaz.t(j.pantalla.vertical ? 'ayudaCorta' : 'ayuda'), lugar: 'abajo-centro' }, j));
});

Sigue con#