Servicio mundo
Referencia del servicio mundo. Lo que hay en la escena: piezas, zonas, apariciones, luces y mapas de baldosas, y los eventos de tocar, entrar y chocar.
Corresponde aFormato 1, API 1 Para quien escribe código Esta página se genera desde las fuentes del SDK.
En esta página
- Funciones y propiedades de mundo
- mundo.pieza
- mundo.existe
- mundo.piezas
- mundo.crear
- mundo.eliminar
- mundo.zona
- mundo.zonas
- mundo.aparicion
- mundo.apariciones
- mundo.alTocar
- mundo.alDejarDeTocar
- mundo.alEntrar
- mundo.alSalir
- mundo.rayo
- mundo.mientrasToca
- mundo.alChocar
- mundo.piezasEn
- mundo.jugadoresEn
- mundo.grupo
- mundo.crearZona
- mundo.mapa
- mundo.mapas
- mundo.luz
- mundo.luces
- mundo.crearLuz
- mundo.ambiente
- mundo.modelo
- mundo.modelos
- mundo.crearModelo
- Pieza
- pieza.tiene
- pieza.etiquetar
- pieza.desetiquetar
- pieza.moverA
- pieza.girar
- pieza.detener
- pieza.impulsar
- pieza.alTocar
- pieza.eliminar
- pieza.reproducir
- pieza.pararAnimacion
- pieza.alTerminarClip
- Grupo
- grupo.piezas
- grupo.mover
- grupo.moverA
- grupo.girarY
- grupo.detener
- grupo.etiquetar
- grupo.desetiquetar
- grupo.eliminar
- Luz
- luz.eliminar
- Zona
- zona.tiene
- zona.jugadores
- zona.actores
- zona.eliminar
- Mapa
- mapa.leer
- mapa.poner
- mapa.celdaEn
- mapa.centro
- mapa.buscar
- mapa.colision
- Terreno
- terreno.bloques
- terreno.biomas
- terreno.info
- terreno.bloque
- terreno.poner
- terreno.romper
- terreno.llenar
- terreno.altura
- terreno.suelo
- terreno.bioma
- terreno.clima
- terreno.buscarBioma
- terreno.buscarBloque
- terreno.rayo
- terreno.ruido
- terreno.alRomper
- terreno.alPoner
- terreno.alGenerar
- terreno.guardar
- terreno.restaurar
- terreno.borrarGuardado
- terreno.regenerar
- Notas de comportamiento
- Cómo se usa el terreno
- Ejemplos completos
- mundo: tocar piezas por etiqueta y por id
- mundo: crear piezas desde el script
- mundo: mover piezas
- mundo: zonas
- mundo: rayo
- mundo: grupos, consultas, choques, permanencia
- mundo: luces, ambiente y zonas creadas por el script
- Sigue con
Lo que hay en la escena: piezas, zonas, apariciones, luces y mapas de baldosas, y los eventos de tocar, entrar y chocar.
Los tipos que aparecen en las firmas (Vec3, Texto, el selector) están en la referencia de la API.
Funciones y propiedades de mundo#
| Propiedad | Tipo | Descripción |
|---|---|---|
mundo.gravedad | number | Aceleración vertical (negativa hacia abajo). |
mundo.terreno | Terreno | El terreno de cubos de la escena (mundo abierto). Existe siempre: en una escena sin terreno, activo es false y sus métodos fallan con un mensaje claro. Agregado en la versión 1.1. |
mundo.pieza#
mundo.pieza(id: string): PiezaLa pieza con ese id. Falla con un mensaje claro si no existe (usa mundo.existe para preguntar).
De docs/EJEMPLOS.md, «interfaz: rótulos, barras y números en el mundo»:
const torre = mundo.pieza('torre');mundo.existe#
mundo.existe(id: string): booleanDice si hay una pieza con ese id.
De games/isla-taller/scripts/principal.ts:
if (!mundo.existe(`pad-${o.id}`)) continue;mundo.piezas#
mundo.piezas(etiqueta?: string): Pieza[]Todas las piezas con esa etiqueta (sin numeral). Sin argumento, todas las piezas.
De docs/EJEMPLOS.md, «azar»:
const losas = azar.mezclar(mundo.piezas('losa'));mundo.crear#
mundo.crear(def: PiezaDef): PiezaCrea una pieza nueva. Mismo formato que en la escena.
De docs/EJEMPLOS.md, «mundo: crear piezas desde el script»:
const moneda = mundo.crear({
forma: 'cilindro',
posicion: [azar.entre(-10, 10), 1, azar.entre(-10, 10)],
rotacion: [90, 0, 0],
tamano: [0.9, 0.16, 0.9],
material: { color: 'ambar', acabado: 'brillante' },
solida: false,
etiquetas: ['moneda']
});mundo.eliminar#
mundo.eliminar(pieza: Pieza | string): voidElimina una pieza. Recibe la pieza o su id.
De games/isla-taller/scripts/principal.ts:
function quitarFugaz(cristal: Pieza): void {
const haz = cristal.datos.haz as string;
if (mundo.existe(haz)) mundo.eliminar(haz);
cristal.eliminar();
}mundo.zona#
mundo.zona(id: string): ZonaLa zona con ese id.
De games/isla-taller/scripts/principal.ts:
mundo.zona(`zona-${id}`).activa = false;mundo.zonas#
mundo.zonas(etiqueta?: string): Zona[]Todas las zonas con esa etiqueta. Sin argumento, todas las zonas.
mundo.aparicion#
mundo.aparicion(id: string): { id; posicion; rotacionY }El punto de aparición con ese id: su posición y hacia dónde mira.
mundo.apariciones#
mundo.apariciones(): Aparicion[]Todos los puntos de aparición de la escena.
mundo.alTocar#
mundo.alTocar(selector: Selector, fn: (pieza: Pieza, jugador: Jugador) => void): voidUn jugador empieza a tocar una pieza (sólida o no).
De docs/EJEMPLOS.md, «jugadores: mover, teletransportar y cambiar habilidades»:
mundo.alTocar('#trampolin', (_p, j) => j.impulsar([0, 20, 0]));mundo.alDejarDeTocar#
mundo.alDejarDeTocar(selector: Selector, fn: (pieza: Pieza, jugador: Jugador) => void): voidUn jugador deja de tocar una pieza.
De docs/EJEMPLOS.md, «mundo: tocar piezas por etiqueta y por id»:
mundo.alDejarDeTocar('#placa', (placa) => (placa.material = 'gris'));mundo.alEntrar#
mundo.alEntrar(selector: Selector, fn: (zona: Zona, jugador: Jugador) => void): voidUn jugador entra a una zona.
De docs/EJEMPLOS.md, «juego: cambiar de escena»:
mundo.alEntrar('tesoro', (_zona, j) => juego.terminar({ resultado: 'victoria', puntaje: 1, de: j }));mundo.alSalir#
mundo.alSalir(selector: Selector, fn: (zona: Zona, jugador: Jugador) => void): voidUn jugador sale de una zona.
De docs/EJEMPLOS.md, «mundo: zonas»:
mundo.alSalir('zona-segura', (_zona, j) => interfaz.mensaje(interfaz.t('cuidado'), { para: j, tipo: 'peligro' }));mundo.rayo#
mundo.rayo(origen: Vec3, direccion: Vec3, distanciaMaxima: number): { pieza: Pieza | null; punto: Vec3; distancia: number } | nullLanza un rayo contra las piezas sólidas. direccion no necesita estar normalizada.
De docs/EJEMPLOS.md, «mundo: rayo»:
for (const j of jugadores.todos()) {
const p = j.posicion;
const hacia: [number, number, number] = [p[0] - ojo[0], p[1] + 1 - ojo[1], p[2] - ojo[2]];
const distancia = Math.hypot(hacia[0], hacia[1], hacia[2]);
const impacto = mundo.rayo(ojo, hacia, distancia);
j.datos.visto = impacto === null || impacto.distancia > distancia - 0.6;
}mundo.mientrasToca#
mundo.mientrasToca(selector: Selector, fn: (pieza: Pieza, jugador: Jugador, dt: number) => void): voidSe llama en cada cuadro mientras un jugador sigue tocando la pieza (daño por segundo, cintas, hielo).
De docs/EJEMPLOS.md, «mundo: grupos, consultas, choques, permanencia»:
// Lava: quita vida mientras se pisa. dt siempre vale 1/60.
mundo.mientrasToca('#lava', (_lava, j, dt) => { j.datos.vida = (j.datos.vida as number) - 10 * dt; });mundo.alChocar#
mundo.alChocar(selector: Selector, fn: (pieza: Pieza, otra: Pieza) => void): voidUna pieza dinámica que calza con el selector empieza a tocar otra pieza (la pelota toca el arco).
De docs/EJEMPLOS.md, «mundo: grupos, consultas, choques, permanencia»:
// Una pelota (pieza dinamica con "rebote": 0.8) toca el arco.
mundo.alChocar('pelota', (_pelota, otra) => { if (otra.tiene('arco')) juego.registro('gol'); });mundo.piezasEn#
mundo.piezasEn(centro: Vec3, radioOTamano: number | Vec3, etiqueta?: string): Pieza[]Piezas (sólidas o no) que tocan una esfera de ese radio, o una caja de ese tamaño, centrada en centro.
De docs/EJEMPLOS.md, «mundo: grupos, consultas, choques, permanencia»:
// Explosion: todo lo que haya a 4 unidades con la etiqueta "caja".
mundo.alTocar('bomba', (bomba) => {
for (const caja of mundo.piezasEn(bomba.posicion, 4, 'caja')) caja.impulsar([0, 12, 0]);
bomba.eliminar();
});mundo.jugadoresEn#
mundo.jugadoresEn(centro: Vec3, radio: number): Jugador[]Jugadores cuyo cuerpo está a menos de radio del punto.
mundo.grupo#
mundo.grupo(nombre: string): GrupoEl grupo de piezas con ese nombre (propiedad grupo de las piezas).
De docs/EJEMPLOS.md, «mundo: grupos, consultas, choques, permanencia»:
// Las piezas con "grupo": "puerta" en la escena se mueven juntas.
const puerta = mundo.grupo('puerta');mundo.crearZona#
mundo.crearZona(def: ZonaDef): ZonaCrea una zona nueva. Mismo formato que en la escena.
De docs/EJEMPLOS.md, «mundo: luces, ambiente y zonas creadas por el script»:
// Una zona de premio que aparece en un lugar nuevo cada vez.
const zona = mundo.crearZona({ id: 'premio', forma: 'esfera', posicion: [5, 1, 5], tamano: [3, 3, 3], datos: { puntos: 5 } });mundo.mapa#
mundo.mapa(id: string): MapaUn mapa de baldosas de la escena actual. Falla si no existe.
De docs/EJEMPLOS.md, «Láminas y mapas de baldosas (2D)»:
// Abre un paso: borra tres baldosas del piso.
const nivel = mundo.mapa('nivel');mundo.mapas#
mundo.mapas(): Mapa[]Los mapas de baldosas de la escena actual.
De docs/CONTRATO.md, «Mapas de baldosas»:
mundo.mapas();mundo.luz#
mundo.luz(id: string): LuzLa luz con ese id (declarada en la escena o creada con mundo.crearLuz).
mundo.luces#
mundo.luces(): Luz[]Todas las luces que el script puede cambiar.
De docs/EJEMPLOS.md, «mundo: luces, ambiente y zonas creadas por el script»:
// Anochece en 20 segundos y se encienden los faroles (luces con "id" en la escena).
reloj.despues(30, () => {
mundo.ambiente({ cielo: 'noche', luzAmbiente: 0.25, sol: { intensidad: 0.1 }, niebla: { desde: 10, hasta: 60 } }, 20);
for (const luz of mundo.luces()) luz.encendida = true;
sonido.ruido({ duracion: 3, filtro: 600, volumen: 0.4, ataque: 1 });
});mundo.crearLuz#
mundo.crearLuz(def: LuzDef): LuzCrea una luz puntual. Hay un máximo de 8 a la vez.
De docs/EJEMPLOS.md, «mundo: luces, ambiente y zonas creadas por el script»:
const faro = mundo.crearLuz({ id: 'faro', posicion: [0, 8, 0], color: 'ambar', intensidad: 3, alcance: 30 });mundo.ambiente#
mundo.ambiente(cambios: { cielo?, luzAmbiente?, sol?, niebla? }, duracion?: number): void // día y noche, tormentaCambia el ambiente mientras se juega: cielo, luz ambiente, sol y niebla. Solo lo que se indique. duracion, en segundos, lo hace gradual.
De docs/EJEMPLOS.md, «mundo: luces, ambiente y zonas creadas por el script»:
// Anochece en 20 segundos y se encienden los faroles (luces con "id" en la escena).
reloj.despues(30, () => {
mundo.ambiente({ cielo: 'noche', luzAmbiente: 0.25, sol: { intensidad: 0.1 }, niebla: { desde: 10, hasta: 60 } }, 20);
for (const luz of mundo.luces()) luz.encendida = true;
sonido.ruido({ duracion: 3, filtro: 600, volumen: 0.4, ataque: 1 });
});mundo.modelo#
mundo.modelo(id: string): ModeloDelJuegoDatos de un modelo del juego (modelos/<id>.glb). Falla con un mensaje claro si no existe.
De docs/CONTRATO.md, «Modelos, texturas y sonidos (versión 1.2)»:
mundo.modelo('robot'); // { id, tamano, clips: [{ nombre, duracion }], triangulos }mundo.modelos#
mundo.modelos(): string[]Ids de los modelos del juego.
De docs/CONTRATO.md, «Modelos, texturas y sonidos (versión 1.2)»:
mundo.modelos(); // ids de los modelos del juegomundo.crearModelo#
mundo.crearModelo(modelo: string, def: ModeloEnMundo): PiezaCrea una pieza con forma 'modelo' a su tamaño natural (por escala). Atajo de crear para no calcular tamano.
De docs/CONTRATO.md, «Modelos, texturas y sonidos (versión 1.2)»:
const robot = mundo.crearModelo('robot', { id: 'r1', posicion: [0, 0.9, 5], escala: 1 }); // pieza a tamaño naturalPieza#
Una pieza del mundo: una de las que declara la escena o una creada con mundo.crear.
| Propiedad | Tipo | Descripción |
|---|---|---|
pieza.id | string | Solo lectura. Id de la pieza. |
pieza.forma | Forma | Solo lectura. Forma de la pieza. |
pieza.fisica | Fisica | Solo lectura. Cómo participa en la física: 'fija', 'dinamica' o 'ninguna'. |
pieza.existe | boolean | Solo lectura. false cuando la pieza fue eliminada. Usar una pieza eliminada no falla, pero no hace nada. |
pieza.posicion | Vec3 | Centro de la pieza. Leerla da una copia; para moverla, asigna un vector nuevo. |
pieza.rotacion | Vec3 | Grados. |
pieza.tamano | Vec3 | Ancho (X), alto (Y) y largo (Z). |
pieza.material | MaterialDef | El nombre de un color de la paleta, o un objeto con color, acabado y patrón. |
pieza.visible | boolean | false: no se dibuja. |
pieza.solida | boolean | false: los jugadores la atraviesan, pero sigue avisando toques. |
pieza.velocidad | Vec3 | Solo piezas dinámicas. |
pieza.etiquetas | readonly string[] | Solo lectura. Las etiquetas de la pieza. Se cambian con etiquetar y desetiquetar. |
pieza.datos | Record<string, unknown> | Datos libres del script. Parte con los datos declarados en la escena. |
pieza.grupo | string | Solo lectura. Nombre del grupo al que pertenece, o ''. |
pieza.rebote | number | Cuánto rebota (0 a 1.5). |
pieza.friccion | number | Roce (0 a 2). |
pieza.opacidad | number | De 0, invisible, a 1, opaca. |
pieza.emision | number | De 0 a 1: brillo propio. |
pieza.velocidadDeGiro | Vec3 | Solo dinámicas: velocidad de giro en grados por segundo. |
pieza.lamina | string | Solo piezas con forma lamina: id de la lámina que muestra ('' en las demás piezas). Se puede cambiar por otra del paquete. |
pieza.animacion | string | number | null | Solo piezas con forma lamina: animación (nombre) o cuadro fijo (número) que muestra. null: la primera animación. Asignarla la reinicia. |
pieza.espejo | boolean | Solo piezas con forma lamina: dibujada espejada en horizontal. |
pieza.modelo | string | Solo lectura. Solo modelos: id del modelo que muestra ('' en las demás piezas). |
pieza.clips | readonly string[] | Solo lectura. Solo modelos: nombres de los clips del modelo (vacío en las demás piezas). |
pieza.tiene#
pieza.tiene(etiqueta: string): booleanDice si la pieza tiene esa etiqueta.
De docs/EJEMPLOS.md, «mundo: grupos, consultas, choques, permanencia»:
// Una pelota (pieza dinamica con "rebote": 0.8) toca el arco.
mundo.alChocar('pelota', (_pelota, otra) => { if (otra.tiene('arco')) juego.registro('gol'); });pieza.etiquetar#
pieza.etiquetar(etiqueta: string): voidLe pone una etiqueta. Los eventos con selector '#etiqueta' la toman en cuenta desde ese momento.
De docs/EJEMPLOS.md, «azar»:
for (const trampa of losas.slice(0, 3)) trampa.etiquetar('peligro'); // tres trampas distintas cada partidapieza.desetiquetar#
pieza.desetiquetar(etiqueta: string): voidLe quita una etiqueta.
De games/circuito-luz-verde/scripts/principal.ts:
const semaforo = mundo.pieza('semaforo-corredor');
// ...
reloj.despues(0.3, () => semaforo.desetiquetar('recien-verde'));pieza.moverA#
pieza.moverA(destino: Vec3, opciones: { duracion: number; suavizado?: 'lineal' | 'suave'; vaiven?: boolean; espera?: number; alLlegar?: () => void }): voidMueve la pieza con suavidad hasta destino. Reemplaza cualquier movimiento anterior.
De docs/EJEMPLOS.md, «mundo: mover piezas»:
// Ida y vuelta para siempre.
mundo.pieza('plataforma').moverA([0, 0, 20], { duracion: 3, vaiven: true, espera: 0.5 });pieza.girar#
pieza.girar(gradosPorSegundo: Vec3): voidGira sin parar a gradosPorSegundo en cada eje. [0, 0, 0] detiene el giro.
De docs/EJEMPLOS.md, «mundo: crear piezas desde el script»:
for (let i = 0; i < 12; i++) {
const moneda = mundo.crear({
forma: 'cilindro',
posicion: [azar.entre(-10, 10), 1, azar.entre(-10, 10)],
rotacion: [90, 0, 0],
tamano: [0.9, 0.16, 0.9],
material: { color: 'ambar', acabado: 'brillante' },
solida: false,
etiquetas: ['moneda']
});
moneda.girar([0, 160, 0]);
}pieza.detener#
pieza.detener(): voidDetiene moverA y girar.
De games/copa-de-bloques/scripts/principal.ts:
for (const p of publico) {
p.pieza.detener();
p.pieza.posicion = p.base;
}pieza.impulsar#
pieza.impulsar(velocidad: Vec3): voidSolo piezas dinámicas: empujón instantáneo (cambio de velocidad).
De docs/EJEMPLOS.md, «mundo: grupos, consultas, choques, permanencia»:
for (const caja of mundo.piezasEn(bomba.posicion, 4, 'caja')) caja.impulsar([0, 12, 0]);pieza.alTocar#
pieza.alTocar(fn: (jugador: Jugador) => void): voidAtajo de mundo.alTocar(pieza.id, ...).
pieza.eliminar#
pieza.eliminar(): voidQuita la pieza del mundo. Los eventos registrados con pieza.alTocar se van con ella.
De docs/EJEMPLOS.md, «jugadores: mover, teletransportar y cambiar habilidades»:
mundo.alTocar('#botas', (botas, j) => { botas.eliminar(); j.saltosExtra = 1; j.rapidez = 11; });pieza.reproducir#
pieza.reproducir(clip: string, opciones?: OpcionesClip): voidSolo modelos: reproduce un clip desde el principio. Falla con un mensaje claro si el modelo no lo tiene. La animación se dibuja: no mueve la pieza ni cambia cómo choca.
De docs/CONTRATO.md, «Modelos, texturas y sonidos (versión 1.2)»:
const robot = mundo.crearModelo('robot', { id: 'r1', posicion: [0, 0.9, 5], escala: 1 }); // pieza a tamaño natural
// ...
robot.reproducir('saludar', { repetir: false, mezcla: 0.2, velocidad: 1 }); // desde el principiopieza.pararAnimacion#
pieza.pararAnimacion(mezcla?: number): voidSolo modelos: deja de animar y vuelve al reposo, mezclando durante mezcla segundos (0,2 por defecto).
De docs/CONTRATO.md, «Modelos, texturas y sonidos (versión 1.2)»:
const robot = mundo.crearModelo('robot', { id: 'r1', posicion: [0, 0.9, 5], escala: 1 }); // pieza a tamaño natural
// ...
robot.pararAnimacion(0.3); // vuelve al reposo mezclando 0,3 spieza.alTerminarClip#
pieza.alTerminarClip(fn: (clip: string) => void): voidSolo modelos: avisa cuando un clip que no se repite llega a su fin. El momento sale de la duración del clip y del reloj de la partida: es el mismo en todos los equipos, dibujen a la velocidad que dibujen.
De docs/CONTRATO.md, «Modelos, texturas y sonidos (versión 1.2)»:
const robot = mundo.crearModelo('robot', { id: 'r1', posicion: [0, 0.9, 5], escala: 1 }); // pieza a tamaño natural
// ...
robot.alTerminarClip((clip) => robot.reproducir('quieto')); // solo clips que no se repitenGrupo#
Varias piezas que se manejan juntas. Se arma con la propiedad grupo de cada pieza. No hay jerarquía: son operaciones en lote.
| Propiedad | Tipo | Descripción |
|---|---|---|
grupo.nombre | string | Solo lectura. Nombre del grupo. |
grupo.centro | Vec3 | Solo lectura. Centro del grupo (promedio de las posiciones). |
grupo.visible | boolean | Se aplica a todas las piezas del grupo. |
grupo.solida | boolean | Se aplica a todas las piezas del grupo. |
grupo.material | MaterialDef | Se aplica a todas las piezas del grupo. |
grupo.piezas#
grupo.piezas(): Pieza[]Las piezas vivas del grupo.
grupo.mover#
grupo.mover(delta: Vec3): voidDesplaza todas las piezas.
grupo.moverA#
grupo.moverA(destino: Vec3, opciones: OpcionesMover): voidMueve el grupo, con suavidad, hasta que su centro quede en destino, conservando la forma.
De docs/EJEMPLOS.md, «mundo: grupos, consultas, choques, permanencia»:
// Las piezas con "grupo": "puerta" en la escena se mueven juntas.
const puerta = mundo.grupo('puerta');
// ...
mundo.alTocar('boton', () => puerta.moverA([0, 6, 10], { duracion: 1.5 }));grupo.girarY#
grupo.girarY(grados: number, pivote?: Vec3): void // gira el conjunto alrededor de un eje verticalGira el grupo grados alrededor del eje Y que pasa por pivote (por defecto, su centro).
grupo.detener#
grupo.detener(): voidDetiene el movimiento del grupo.
grupo.etiquetar#
grupo.etiquetar(etiqueta: string): voidPone la etiqueta a todas las piezas del grupo.
grupo.desetiquetar#
grupo.desetiquetar(etiqueta: string): voidQuita la etiqueta a todas las piezas del grupo.
grupo.eliminar#
grupo.eliminar(): voidElimina todas las piezas del grupo.
Luz#
Una luz puntual que el script puede cambiar.
| Propiedad | Tipo | Descripción |
|---|---|---|
luz.id | string | Solo lectura. Id de la luz. |
luz.posicion | Vec3 | Dónde está la luz. |
luz.color | string | Color de la paleta. |
luz.intensidad | number | Cuánto ilumina, de 0 a 10. |
luz.alcance | number | Unidades hasta donde llega. |
luz.encendida | boolean | Apagada no ilumina (conserva su intensidad para volver a encenderla). |
luz.eliminar#
luz.eliminar(): voidQuita la luz del mundo.
Zona#
Una zona de la escena, o una creada con mundo.crearZona: avisa cuando alguien entra o sale.
| Propiedad | Tipo | Descripción |
|---|---|---|
zona.id | string | Solo lectura. Id de la zona. |
zona.posicion | Vec3 | Solo lectura. Centro de la zona. |
zona.tamano | Vec3 | Solo lectura. Ancho (X), alto (Y) y largo (Z). |
zona.etiquetas | readonly string[] | Solo lectura. Las etiquetas de la zona. |
zona.activa | boolean | Una zona desactivada no avisa entradas ni salidas. |
zona.datos | Record<string, unknown> | Datos libres del script. Parte con los datos declarados en la escena. |
zona.tiene#
zona.tiene(etiqueta: string): booleanDice si la zona tiene esa etiqueta.
zona.jugadores#
zona.jugadores(): Jugador[]Jugadores que están dentro ahora.
De games/circuito-luz-verde/scripts/principal.ts:
jugadores.alAccion('accion', (j) => {
if (ascensorOcupado || !mundo.zona('zona-ascensor').jugadores().includes(j)) return;
subirAscensor();
});zona.actores#
zona.actores(): Actor[]Actores que están dentro ahora.
zona.eliminar#
zona.eliminar(): voidQuita la zona del mundo.
De docs/EJEMPLOS.md, «mundo: luces, ambiente y zonas creadas por el script»:
mundo.alEntrar('premio', (z, j) => { j.puntos += z.datos.puntos as number; z.eliminar(); });Mapa#
Un mapa de baldosas de la escena. Las columnas crecen hacia +X; las filas, hacia abajo ('xy') o hacia +Z ('xz').
| Propiedad | Tipo | Descripción |
|---|---|---|
mapa.id | string | Solo lectura. El id del mapa en la escena. |
mapa.plano | 'xy' | 'xz' | Solo lectura. 'xy': mapa vertical, el de la vista lateral. 'xz': mapa de suelo, el de las vistas desde arriba y los mundos 3D. |
mapa.columnas | number | Solo lectura. Cuántas columnas tiene: el largo de la fila más larga. |
mapa.filas | number | Solo lectura. Cuántas filas tiene. |
mapa.celda | number | Solo lectura. Tamaño de una baldosa, en unidades. |
mapa.leer#
mapa.leer(columna: number, fila: number, capa?: number | string): string | nullCarácter de la baldosa en esa celda, o null si está vacía. capa: número o id; por defecto, la primera que no es fondo.
De docs/CONTRATO.md, «Mapas de baldosas»:
const nivel = mundo.mapa('nivel'); // falla con un mensaje claro si no existe
// ...
nivel.leer(3, 2); // '=' o null. Tercer parámetro: capa (número o id); por defecto, la primera que no es fondomapa.poner#
mapa.poner(columna: number, fila: number, baldosa: string | null, capa?: number | string): voidCambia una baldosa (null la borra). La colisión y el dibujo se actualizan solos en el cuadro siguiente.
De docs/EJEMPLOS.md, «Láminas y mapas de baldosas (2D)»:
mundo.alTocar('#vida', (vida, j) => {
vida.eliminar();
// Abre un paso: borra tres baldosas del piso.
const nivel = mundo.mapa('nivel');
for (let c = 9; c <= 11; c++) nivel.poner(c, 5, null);
sonido.tocar('poder', { para: j });
});mapa.celdaEn#
mapa.celdaEn(punto: Vec3): { columna: number; fila: number }Celda que contiene un punto del mundo (puede caer fuera del mapa).
De docs/CONTRATO.md, «Mapas de baldosas»:
const nivel = mundo.mapa('nivel'); // falla con un mensaje claro si no existe
// ...
nivel.celdaEn(jugador.posicion); // { columna, fila }mapa.centro#
mapa.centro(columna: number, fila: number): Vec3Centro de una celda en el mundo. En mapas de suelo ('xz') queda a ras del piso.
De docs/CONTRATO.md, «Mapas de baldosas»:
const nivel = mundo.mapa('nivel'); // falla con un mensaje claro si no existe
// ...
nivel.centro(3, 2); // Vec3 del centro de la celdamapa.buscar#
mapa.buscar(baldosa: string, capa?: number | string): { columna: number; fila: number }[]Todas las celdas que tienen esa baldosa.
De docs/CONTRATO.md, «Mapas de baldosas»:
const nivel = mundo.mapa('nivel'); // falla con un mensaje claro si no existe
// ...
nivel.buscar('^'); // todas las celdas con esa baldosamapa.colision#
mapa.colision(columna: number, fila: number): ColisionBaldosaCómo choca esa celda, mirando todas las capas que no son fondo. 'ninguna' si está vacía.
De docs/CONTRATO.md, «Mapas de baldosas»:
const nivel = mundo.mapa('nivel'); // falla con un mensaje claro si no existe
// ...
nivel.colision(3, 2); // 'plataforma', 'ninguna', ...Terreno#
Terreno de cubos: un mundo generado a partir de una semilla y de la tabla de biomas de la escena. Las coordenadas de un bloque son enteras: el bloque [x, y, z] ocupa de x a x+1, de y a y+1 y de z a z+1. El mundo mide 64 bloques de alto (y de 0 a 63). Se obtiene con mundo.terreno.
| Propiedad | Tipo | Descripción |
|---|---|---|
terreno.activo | boolean | Solo lectura. La escena tiene terreno. |
terreno.semilla | number | Solo lectura. Semilla del mundo: la de terreno.semilla de la escena o, si no tiene, la de la partida. |
terreno.nivelDelMar | number | Solo lectura. Altura del mar: el generador llena de agua hasta ahí. |
terreno.alto | number | Solo lectura. Alto del mundo en bloques (64). |
terreno.limite | number | Solo lectura. El mundo llega hasta esta distancia del origen en X y en Z. |
terreno.cambios | number | Solo lectura. Cuántos bloques difieren hoy del mundo generado. |
terreno.bloques#
terreno.bloques(): string[]Ids de los bloques declarados, en orden.
terreno.biomas#
terreno.biomas(): string[]Ids de los biomas declarados, en orden.
terreno.info#
terreno.info(bloque: string): InfoBloqueLos datos de un bloque declarado: id, solido, transparente, liquido, dureza, luz, color y etiquetas.
De games/valle-abierto/scripts/comun.ts:
export const terreno = mundo.terreno;
// ...
const info = terreno.info(id);terreno.bloque#
terreno.bloque(x: number, y: number, z: number): stringId del bloque en esa celda. 'aire' si está vacía o fuera del mundo.
terreno.poner#
terreno.poner(x: number, y: number, z: number, bloque: string, por?: Jugador): booleanPone un bloque ('aire' lo quita). Devuelve false si la celda ya tenía ese bloque o queda fuera del mundo. Avisa a alPoner.
terreno.romper#
terreno.romper(x: number, y: number, z: number, por?: Jugador): stringQuita el bloque de la celda y devuelve su id ('aire' si no había nada). Avisa a alRomper.
terreno.llenar#
terreno.llenar(desde: Vec3, hasta: Vec3, bloque: string): numberLlena la caja entre dos celdas (incluidas) con un bloque. Devuelve cuántas cambiaron. No avisa a alPoner ni a alRomper. Hasta 4096 celdas.
terreno.altura#
terreno.altura(x: number, z: number): numberAltura del bloque sólido más alto de la columna, más uno: donde quedan los pies. Cuenta árboles y lo construido.
terreno.suelo#
terreno.suelo(x: number, z: number): numberAltura del suelo natural que da el generador en esa columna, sin árboles ni cambios. Es barata: sirve para lugares lejanos.
terreno.bioma#
terreno.bioma(x: number, z: number): stringId del bioma de esa columna.
terreno.clima#
terreno.clima(x: number, z: number): { temperatura: number; humedad: number }Clima de esa columna, de 0 a 1.
terreno.buscarBioma#
terreno.buscarBioma(bioma: string, desde: Vec3, radio?: number): Vec3 | nullLa columna del bioma pedido más cercana a desde, o null si no hay ninguna a menos de radio (512 por defecto, hasta 2000). Devuelve los pies: [x + 0.5, suelo, z + 0.5].
terreno.buscarBloque#
terreno.buscarBloque(bloque: string, desde: Vec3, radio?: number, alto?: number): Vec3 | nullLa celda más cercana a desde con ese bloque, a menos de radio en horizontal (24 por defecto, hasta 48), o null. Con alto, solo cuentan las celdas a no más de esa distancia vertical de desde (lo que queda al alcance de la mano). Genera los trozos que revisa.
terreno.rayo#
terreno.rayo(origen: Vec3, direccion: Vec3, distancia: number): BloqueApuntado | nullPrimer bloque que toca un rayo (los líquidos y el aire no cuentan), hasta 64 unidades.
terreno.ruido#
terreno.ruido(x: number, z: number, escala?: number, canal?: number): numberRuido reproducible de -1 a 1 en un punto del plano, atado a la semilla del mundo. escala es el tamaño de sus manchas en bloques (32 por defecto); canal da ruidos independientes.
terreno.alRomper#
terreno.alRomper(fn: (cambio: CambioDeBloque) => void): voidSe llama cada vez que romper quita un bloque, con la posición, lo que había, lo que quedó y el jugador que lo hizo, si lo hubo.
terreno.alPoner#
terreno.alPoner(fn: (cambio: CambioDeBloque) => void): voidSe llama cada vez que poner cambia un bloque, con los mismos datos que alRomper.
terreno.alGenerar#
terreno.alGenerar(fn: (trozo: { x: number; z: number }) => void): voidGancho de generación: se llama cuando la sala carga un trozo de 16 x 16 por primera vez (cerca de un jugador). Recibe la esquina del trozo. Lo que el script cambie ahí con poner o llenar queda como parte del mundo.
terreno.guardar#
terreno.guardar(jugador: Jugador): { bloques: number; bytes: number }Guarda los cambios del mundo (solo lo que difiere del generado) en el progreso del jugador. Devuelve cuántos bloques y cuántos bytes ocupó.
terreno.restaurar#
terreno.restaurar(jugador: Jugador): numberVuelve a aplicar los cambios guardados por guardar. Devuelve cuántos bloques restauró (0 si no había nada o la semilla es otra).
terreno.borrarGuardado#
terreno.borrarGuardado(jugador: Jugador): voidBorra los cambios guardados de ese jugador.
terreno.regenerar#
terreno.regenerar(semilla: number): voidReemplaza el mundo por el de otra semilla (un "mundo nuevo"): olvida los cambios y devuelve a los jugadores a su aparición.
Notas de comportamiento#
Un selector es un id ('meta') o una etiqueta con numeral ('#moneda'). Las etiquetas se evalúan en el momento del toque: puedes etiquetar y desetiquetar piezas mientras se juega (pieza.etiquetar('peligro')).
alTocar se dispara cuando un jugador empieza a tocar una pieza, sea sólida o no (pararse encima cuenta). rayo choca solo con piezas sólidas e ignora a los jugadores.
Ojo: pieza.posicion[0] = 5 no mueve nada (modifica una copia). Escribe pieza.posicion = [5, y, z].
Cómo se usa el terreno#
El terreno de cubos de la escena. Existe siempre: en una escena sin terreno, activo es false y sus métodos fallan con un mensaje que dice cómo activarlo. Los bloques se nombran por su id ('pasto'); el vacío es 'aire'.
Romper y poner los decide el script: el motor da el rayo, cambia el bloque y avisa. El esqueleto de toda construcción (está completo en la plantilla mundo-abierto):
juego.cadaCuadro(() => {
for (const j of jugadores.todos()) {
const a = j.apuntaBloque(5);
interfaz.marcarBloque(a ? a.posicion : null, {}, j);
if (a && j.pulsa('primaria')) terreno.romper(a.posicion[0], a.posicion[1], a.posicion[2], j);
}
});
jugadores.alAccion('secundaria', (j) => {
const a = j.apuntaBloque(5);
if (a) terreno.poner(a.vecino[0], a.vecino[1], a.vecino[2], 'tabla', j);
});Lo que conviene saber:
ponerno deja encerrar a nadie: devuelvefalsesi en esa celda hay un jugador o un actor. A quien solo la roza (hasta un quinto de bloque) lo corre ese poquito.llenarno revisa.- El fondo del mundo no se toca (
y = 0), ni lo que queda fuera delimiteo sobrey = 63. - Guardado. El mundo no se guarda: se vuelve a generar con su semilla.
terreno.guardar(jugador)escribe en el progreso de ese jugador solo lo que difiere del mundo generado, por trozo, a 4 caracteres por bloque, junto con la semilla.terreno.restaurar(jugador)vuelve a ese mundo (si la escena no fija semilla, cambia a la guardada) y le pone los cambios encima. Topes: 60 000 bloques cambiados a la vez (al llegar,poneryromperfallan con un mensaje claro: revisaterreno.cambios) y 320 KB de guardado, aparte de los 64 KB deprogreso. La clave que usa (_terreno:<escena>) está reservada. - Costo.
bloque,alturaybuscarBloquegeneran el trozo si no estaba (cerca de 1 ms la primera vez);suelo,bioma,climaybuscarBiomano generan nada. - Intervenir la generación. El autor que quiere más que la tabla tiene tres caminos:
estructurasen los datos, cambios directos al empezar (llenarun campamento, un faro), yalGenerar, que corre cuando la sala carga cada trozo cerca de un jugador (lo que se cambie ahí queda como parte del mundo y viaja a todos). El gancho no corre para los trozos lejanos que solo se dibujan: lo que agrega aparece al acercarse.
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.
mundo: tocar piezas por etiqueta y por id#
import { juego, mundo } from '@greengames/sdk';
juego.alIniciar(() => {
mundo.alTocar('#moneda', (moneda, j) => { // todas las piezas con la etiqueta "moneda"
moneda.eliminar();
j.puntos++;
});
mundo.alTocar('boton-rojo', (boton) => { // la pieza con id "boton-rojo"
boton.material = { color: 'verde', acabado: 'neon' };
mundo.pieza('puerta').solida = false;
mundo.pieza('puerta').visible = false;
});
mundo.alDejarDeTocar('#placa', (placa) => (placa.material = 'gris'));
});mundo: crear piezas desde el script#
import { azar, juego, mundo } from '@greengames/sdk';
juego.alIniciar(() => {
for (let i = 0; i < 12; i++) {
const moneda = mundo.crear({
forma: 'cilindro',
posicion: [azar.entre(-10, 10), 1, azar.entre(-10, 10)],
rotacion: [90, 0, 0],
tamano: [0.9, 0.16, 0.9],
material: { color: 'ambar', acabado: 'brillante' },
solida: false,
etiquetas: ['moneda']
});
moneda.girar([0, 160, 0]);
}
});mundo: mover piezas#
import { juego, mundo, reloj } from '@greengames/sdk';
juego.alIniciar(() => {
// Ida y vuelta para siempre.
mundo.pieza('plataforma').moverA([0, 0, 20], { duracion: 3, vaiven: true, espera: 0.5 });
// Una vez, con aviso al llegar.
mundo.pieza('puente').moverA([0, 0, 8], { duracion: 1.5, alLlegar: () => juego.registro('puente listo') });
});
// A mano, cuadro a cuadro: una pieza en órbita. Se asigna un vector nuevo.
juego.cadaCuadro(() => {
const a = reloj.ahora * 2;
mundo.pieza('satelite').posicion = [Math.cos(a) * 5, 3, Math.sin(a) * 5];
});mundo: zonas#
import { interfaz, juego, mundo } from '@greengames/sdk';
juego.alIniciar(() => {
mundo.alEntrar('#control', (zona, j) => {
j.aparicion = zona.id; // hay una aparición con el mismo id que la zona
interfaz.mensaje(interfaz.t('control'), { para: j, tipo: 'exito' });
});
mundo.alSalir('zona-segura', (_zona, j) => interfaz.mensaje(interfaz.t('cuidado'), { para: j, tipo: 'peligro' }));
mundo.alEntrar('meta', (_zona, j) => juego.terminar({ resultado: 'victoria', puntaje: j.puntos, de: j }));
});mundo: rayo#
import { juego, jugadores, mundo } from '@greengames/sdk';
// ¿La torre ve al jugador? Un rayo desde la torre hasta su pecho: si choca antes con una pieza, está oculto.
juego.cadaCuadro(() => {
const ojo: [number, number, number] = [0, 6, 0];
for (const j of jugadores.todos()) {
const p = j.posicion;
const hacia: [number, number, number] = [p[0] - ojo[0], p[1] + 1 - ojo[1], p[2] - ojo[2]];
const distancia = Math.hypot(hacia[0], hacia[1], hacia[2]);
const impacto = mundo.rayo(ojo, hacia, distancia);
j.datos.visto = impacto === null || impacto.distancia > distancia - 0.6;
}
});mundo: grupos, consultas, choques, permanencia#
import { juego, mundo } from '@greengames/sdk';
juego.alIniciar(() => {
// Las piezas con "grupo": "puerta" en la escena se mueven juntas.
const puerta = mundo.grupo('puerta');
mundo.alTocar('boton', () => puerta.moverA([0, 6, 10], { duracion: 1.5 }));
// Explosion: todo lo que haya a 4 unidades con la etiqueta "caja".
mundo.alTocar('bomba', (bomba) => {
for (const caja of mundo.piezasEn(bomba.posicion, 4, 'caja')) caja.impulsar([0, 12, 0]);
bomba.eliminar();
});
// Una pelota (pieza dinamica con "rebote": 0.8) toca el arco.
mundo.alChocar('pelota', (_pelota, otra) => { if (otra.tiene('arco')) juego.registro('gol'); });
// Lava: quita vida mientras se pisa. dt siempre vale 1/60.
mundo.mientrasToca('#lava', (_lava, j, dt) => { j.datos.vida = (j.datos.vida as number) - 10 * dt; });
});mundo: luces, ambiente y zonas creadas por el script#
import { juego, mundo, reloj, sonido } from '@greengames/sdk';
juego.alIniciar(() => {
// Anochece en 20 segundos y se encienden los faroles (luces con "id" en la escena).
reloj.despues(30, () => {
mundo.ambiente({ cielo: 'noche', luzAmbiente: 0.25, sol: { intensidad: 0.1 }, niebla: { desde: 10, hasta: 60 } }, 20);
for (const luz of mundo.luces()) luz.encendida = true;
sonido.ruido({ duracion: 3, filtro: 600, volumen: 0.4, ataque: 1 });
});
const faro = mundo.crearLuz({ id: 'faro', posicion: [0, 8, 0], color: 'ambar', intensidad: 3, alcance: 30 });
reloj.cada(0.5, () => (faro.encendida = !faro.encendida));
// Una zona de premio que aparece en un lugar nuevo cada vez.
const zona = mundo.crearZona({ id: 'premio', forma: 'esfera', posicion: [5, 1, 5], tamano: [3, 3, 3], datos: { puntos: 5 } });
mundo.alEntrar('premio', (z, j) => { j.puntos += z.datos.puntos as number; z.eliminar(); });
juego.registro('zona creada', zona.id);
});Sigue con#
- Servicio jugadores
- Referencia de la API: los once servicios y los tipos comunes.
- Scripts: las reglas de un script y cómo corre dentro de la sala.