Skip to content
Docs

The mundo service

Reference for the mundo service. What is in the scene: pieces, zones, spawn points, lights and tile maps, plus the touch, enter and collide events.

DescribesFormato 1, API 1 For those who write code This page is generated from the SDK sources.

On this page

What is in the scene: pieces, zones, spawn points, lights and tile maps, plus the touch, enter and collide events.

The types used in the signatures (Vec3, Texto, the selector) are in the API reference.

Functions and properties of mundo#

PropertyTypeDescription
mundo.gravedadnumberVertical acceleration (negative is down).
mundo.terrenoTerrenoThe cube terrain of the scene (open world). It always exists: in a scene without terreno, activo is false and its methods fail with a clear message. Added in version 1.1.

mundo.pieza#

ts
mundo.pieza(id: string): Pieza

The piece with that id. Fails with a clear message if it does not exist (use mundo.existe to ask first).

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

ts
const torre = mundo.pieza('torre');

mundo.existe#

ts
mundo.existe(id: string): boolean

Tells whether a piece with that id exists.

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

ts
if (!mundo.existe(`pad-${o.id}`)) continue;

mundo.piezas#

ts
mundo.piezas(etiqueta?: string): Pieza[]

Every piece with that tag (without the hash sign). With no argument, every piece.

From docs/EJEMPLOS.md, «azar»:

ts
const losas = azar.mezclar(mundo.piezas('losa'));

mundo.crear#

ts
mundo.crear(def: PiezaDef): Pieza

Creates a new piece. Same format as in the scene file.

From docs/EJEMPLOS.md, «mundo: crear piezas desde el script»:

ts
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#

ts
mundo.eliminar(pieza: Pieza | string): void

Removes a piece. Takes the piece or its id.

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

ts
function quitarFugaz(cristal: Pieza): void {
  const haz = cristal.datos.haz as string;
  if (mundo.existe(haz)) mundo.eliminar(haz);
  cristal.eliminar();
}

mundo.zona#

ts
mundo.zona(id: string): Zona

The zone with that id.

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

ts
mundo.zona(`zona-${id}`).activa = false;

mundo.zonas#

ts
mundo.zonas(etiqueta?: string): Zona[]

Every zone with that tag. With no argument, every zone.

mundo.aparicion#

ts
mundo.aparicion(id: string): { id; posicion; rotacionY }

The spawn point with that id: its position and the direction it faces.

mundo.apariciones#

ts
mundo.apariciones(): Aparicion[]

Every spawn point in the scene.

mundo.alTocar#

ts
mundo.alTocar(selector: Selector, fn: (pieza: Pieza, jugador: Jugador) => void): void

A player starts touching a piece (solid or not).

From docs/EJEMPLOS.md, «jugadores: mover, teletransportar y cambiar habilidades»:

ts
mundo.alTocar('#trampolin', (_p, j) => j.impulsar([0, 20, 0]));

mundo.alDejarDeTocar#

ts
mundo.alDejarDeTocar(selector: Selector, fn: (pieza: Pieza, jugador: Jugador) => void): void

A player stops touching a piece.

From docs/EJEMPLOS.md, «mundo: tocar piezas por etiqueta y por id»:

ts
mundo.alDejarDeTocar('#placa', (placa) => (placa.material = 'gris'));

mundo.alEntrar#

ts
mundo.alEntrar(selector: Selector, fn: (zona: Zona, jugador: Jugador) => void): void

A player enters a zone.

From docs/EJEMPLOS.md, «juego: cambiar de escena»:

ts
mundo.alEntrar('tesoro', (_zona, j) => juego.terminar({ resultado: 'victoria', puntaje: 1, de: j }));

mundo.alSalir#

ts
mundo.alSalir(selector: Selector, fn: (zona: Zona, jugador: Jugador) => void): void

A player leaves a zone.

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

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

mundo.rayo#

ts
mundo.rayo(origen: Vec3, direccion: Vec3, distanciaMaxima: number): { pieza: Pieza | null; punto: Vec3; distancia: number } | null

Casts a ray against solid pieces. direccion does not need to be normalized.

From docs/EJEMPLOS.md, «mundo: rayo»:

ts
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#

ts
mundo.mientrasToca(selector: Selector, fn: (pieza: Pieza, jugador: Jugador, dt: number) => void): void

Called every frame while a player keeps touching the piece (damage per second, conveyor belts, ice).

From docs/EJEMPLOS.md, «mundo: grupos, consultas, choques, permanencia»:

ts
// 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#

ts
mundo.alChocar(selector: Selector, fn: (pieza: Pieza, otra: Pieza) => void): void

A dynamic piece matching the selector starts touching another piece (the ball hits the goal).

From docs/EJEMPLOS.md, «mundo: grupos, consultas, choques, permanencia»:

ts
// 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#

ts
mundo.piezasEn(centro: Vec3, radioOTamano: number | Vec3, etiqueta?: string): Pieza[]

Pieces (solid or not) touching a sphere of that radius, or a box of that size, centered at centro.

From docs/EJEMPLOS.md, «mundo: grupos, consultas, choques, permanencia»:

ts
// 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#

ts
mundo.jugadoresEn(centro: Vec3, radio: number): Jugador[]

Players whose body is closer than radio to the point.

mundo.grupo#

ts
mundo.grupo(nombre: string): Grupo

The group of pieces with that name (the grupo property of the pieces).

From docs/EJEMPLOS.md, «mundo: grupos, consultas, choques, permanencia»:

ts
// Las piezas con "grupo": "puerta" en la escena se mueven juntas.
const puerta = mundo.grupo('puerta');

mundo.crearZona#

ts
mundo.crearZona(def: ZonaDef): Zona

Creates a new zone. Same format as in the scene file.

From docs/EJEMPLOS.md, «mundo: luces, ambiente y zonas creadas por el script»:

ts
// 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#

ts
mundo.mapa(id: string): Mapa

A tile map of the current scene. Fails if it does not exist.

From docs/EJEMPLOS.md, «Láminas y mapas de baldosas (2D)»:

ts
// Abre un paso: borra tres baldosas del piso.
const nivel = mundo.mapa('nivel');

mundo.mapas#

ts
mundo.mapas(): Mapa[]

The tile maps of the current scene.

From docs/CONTRATO.md, «Tile maps»:

ts
mundo.mapas();

mundo.luz#

ts
mundo.luz(id: string): Luz

The light with that id (declared in the scene or created with mundo.crearLuz).

mundo.luces#

ts
mundo.luces(): Luz[]

Every light the script can change.

From docs/EJEMPLOS.md, «mundo: luces, ambiente y zonas creadas por el script»:

ts
// 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#

ts
mundo.crearLuz(def: LuzDef): Luz

Creates a point light. At most 8 can exist at once.

From docs/EJEMPLOS.md, «mundo: luces, ambiente y zonas creadas por el script»:

ts
const faro = mundo.crearLuz({ id: 'faro', posicion: [0, 8, 0], color: 'ambar', intensidad: 3, alcance: 30 });

mundo.ambiente#

ts
mundo.ambiente(cambios: { cielo?, luzAmbiente?, sol?, niebla? }, duracion?: number): void   // day and night, storms

Changes the environment while playing: sky, ambient light, sun and fog. Only what you pass. duracion, in seconds, makes it gradual.

From docs/EJEMPLOS.md, «mundo: luces, ambiente y zonas creadas por el script»:

ts
// 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#

ts
mundo.modelo(id: string): ModeloDelJuego

Data of a model in the game (modelos/<id>.glb). Fails with a clear message if it does not exist.

From docs/CONTRATO.md, «Models, textures and sounds (version 1.2)»:

ts
mundo.modelo('robot');  // { id, tamano, clips: [{ nombre, duracion }], triangulos }

mundo.modelos#

ts
mundo.modelos(): string[]

Ids of the models in the game.

From docs/CONTRATO.md, «Models, textures and sounds (version 1.2)»:

ts
mundo.modelos();  // ids de los modelos del juego

mundo.crearModelo#

ts
mundo.crearModelo(modelo: string, def: ModeloEnMundo): Pieza

Creates a piece with shape 'modelo' at its natural size (times escala). A shortcut for crear so you do not have to work out tamano.

From docs/CONTRATO.md, «Models, textures and sounds (version 1.2)»:

ts
const robot = mundo.crearModelo('robot', { id: 'r1', posicion: [0, 0.9, 5], escala: 1 });  // pieza a tamaño natural

Pieza#

A piece in the world: one declared by the scene or one created with mundo.crear.

PropertyTypeDescription
pieza.idstringRead-only. Id of the piece.
pieza.formaFormaRead-only. Shape of the piece.
pieza.fisicaFisicaRead-only. How it takes part in physics: 'fija' (fixed), 'dinamica' (dynamic) or 'ninguna' (none).
pieza.existebooleanRead-only. false once the piece has been removed. Using a removed piece does not fail, but does nothing.
pieza.posicionVec3Center of the piece. Reading it gives a copy; to move the piece, assign a new vector.
pieza.rotacionVec3Degrees.
pieza.tamanoVec3Width (X), height (Y) and length (Z).
pieza.materialMaterialDefThe name of a palette color, or an object with color, finish and pattern.
pieza.visiblebooleanfalse: it is not drawn.
pieza.solidabooleanfalse: players walk through it, but it still reports touches.
pieza.velocidadVec3Dynamic pieces only.
pieza.etiquetasreadonly string[]Read-only. The tags of the piece. Change them with etiquetar and desetiquetar.
pieza.datosRecord<string, unknown>Free data for the script. Starts with the datos declared in the scene.
pieza.grupostringRead-only. Name of the group it belongs to, or ''.
pieza.rebotenumberHow much it bounces (0 to 1.5).
pieza.friccionnumberFriction (0 to 2).
pieza.opacidadnumberFrom 0, invisible, to 1, opaque.
pieza.emisionnumberFrom 0 to 1: its own glow.
pieza.velocidadDeGiroVec3Dynamic pieces only: spin speed in degrees per second.
pieza.laminastringSprite pieces only: id of the sprite it shows ('' on every other piece). It can be changed to another sprite of the package.
pieza.animacionstring | number | nullSprite pieces only: the animation (a name) or fixed frame (a number) it shows. null: the first animation. Assigning it restarts the animation.
pieza.espejobooleanSprite pieces only: drawn mirrored horizontally.
pieza.modelostringRead-only. Models only: id of the model it shows ('' on other pieces).
pieza.clipsreadonly string[]Read-only. Models only: names of the model’s clips (empty on other pieces).

pieza.tiene#

ts
pieza.tiene(etiqueta: string): boolean

Tells whether the piece has that tag.

From docs/EJEMPLOS.md, «mundo: grupos, consultas, choques, permanencia»:

ts
// 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#

ts
pieza.etiquetar(etiqueta: string): void

Adds a tag. Events with a '#tag' selector take it into account from then on.

From docs/EJEMPLOS.md, «azar»:

ts
for (const trampa of losas.slice(0, 3)) trampa.etiquetar('peligro');     // tres trampas distintas cada partida

pieza.desetiquetar#

ts
pieza.desetiquetar(etiqueta: string): void

Removes a tag.

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

ts
const semaforo = mundo.pieza('semaforo-corredor');
// ...
reloj.despues(0.3, () => semaforo.desetiquetar('recien-verde'));

pieza.moverA#

ts
pieza.moverA(destino: Vec3, opciones: { duracion: number; suavizado?: 'lineal' | 'suave'; vaiven?: boolean; espera?: number; alLlegar?: () => void }): void

Moves the piece smoothly to destino. Replaces any previous movement.

From docs/EJEMPLOS.md, «mundo: mover piezas»:

ts
// Ida y vuelta para siempre.
mundo.pieza('plataforma').moverA([0, 0, 20], { duracion: 3, vaiven: true, espera: 0.5 });

pieza.girar#

ts
pieza.girar(gradosPorSegundo: Vec3): void

Spins forever at gradosPorSegundo on each axis. [0, 0, 0] stops the spin.

From docs/EJEMPLOS.md, «mundo: crear piezas desde el script»:

ts
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#

ts
pieza.detener(): void

Stops moverA and girar.

From games/copa-de-bloques/scripts/principal.ts:

ts
for (const p of publico) {
  p.pieza.detener();
  p.pieza.posicion = p.base;
}

pieza.impulsar#

ts
pieza.impulsar(velocidad: Vec3): void

Dynamic pieces only: an instant push (a change of velocity).

From docs/EJEMPLOS.md, «mundo: grupos, consultas, choques, permanencia»:

ts
for (const caja of mundo.piezasEn(bomba.posicion, 4, 'caja')) caja.impulsar([0, 12, 0]);

pieza.alTocar#

ts
pieza.alTocar(fn: (jugador: Jugador) => void): void

Shortcut for mundo.alTocar(pieza.id, ...).

pieza.eliminar#

ts
pieza.eliminar(): void

Removes the piece from the world. Events registered with pieza.alTocar go with it.

From docs/EJEMPLOS.md, «jugadores: mover, teletransportar y cambiar habilidades»:

ts
mundo.alTocar('#botas', (botas, j) => { botas.eliminar(); j.saltosExtra = 1; j.rapidez = 11; });

pieza.reproducir#

ts
pieza.reproducir(clip: string, opciones?: OpcionesClip): void

Models only: plays a clip from the start. Fails with a clear message if the model does not have it. The animation is only drawn: it does not move the piece or change how it collides.

From docs/CONTRATO.md, «Models, textures and sounds (version 1.2)»:

ts
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 principio

pieza.pararAnimacion#

ts
pieza.pararAnimacion(mezcla?: number): void

Models only: stops animating and returns to the rest pose, blending for mezcla seconds (0.2 by default).

From docs/CONTRATO.md, «Models, textures and sounds (version 1.2)»:

ts
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 s

pieza.alTerminarClip#

ts
pieza.alTerminarClip(fn: (clip: string) => void): void

Models only: tells you when a clip that does not repeat reaches its end. The moment comes from the clip’s length and the match clock: it is the same on every computer, whatever speed it draws at.

From docs/CONTRATO.md, «Models, textures and sounds (version 1.2)»:

ts
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 repiten

Grupo#

Several pieces handled together. It is built from the grupo property of each piece. There is no hierarchy: these are batch operations.

PropertyTypeDescription
grupo.nombrestringRead-only. Name of the group.
grupo.centroVec3Read-only. Center of the group (the average of the positions).
grupo.visiblebooleanApplies to every piece in the group.
grupo.solidabooleanApplies to every piece in the group.
grupo.materialMaterialDefApplies to every piece in the group.

grupo.piezas#

ts
grupo.piezas(): Pieza[]

The live pieces of the group.

grupo.mover#

ts
grupo.mover(delta: Vec3): void

Shifts every piece.

grupo.moverA#

ts
grupo.moverA(destino: Vec3, opciones: OpcionesMover): void

Moves the group smoothly until its center is at destino, keeping its shape.

From docs/EJEMPLOS.md, «mundo: grupos, consultas, choques, permanencia»:

ts
// 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#

ts
grupo.girarY(grados: number, pivote?: Vec3): void   // rotates the whole set around a vertical axis

Rotates the group by grados around the Y axis through pivote (its center by default).

grupo.detener#

ts
grupo.detener(): void

Stops the group's movement.

grupo.etiquetar#

ts
grupo.etiquetar(etiqueta: string): void

Adds the tag to every piece in the group.

grupo.desetiquetar#

ts
grupo.desetiquetar(etiqueta: string): void

Removes the tag from every piece in the group.

grupo.eliminar#

ts
grupo.eliminar(): void

Removes every piece in the group.

Luz#

A point light the script can change.

PropertyTypeDescription
luz.idstringRead-only. Id of the light.
luz.posicionVec3Where the light is.
luz.colorstringA palette color.
luz.intensidadnumberHow bright it is, from 0 to 10.
luz.alcancenumberHow far it reaches, in units.
luz.encendidabooleanWhen off it gives no light (it keeps its intensity for when you turn it back on).

luz.eliminar#

ts
luz.eliminar(): void

Removes the light from the world.

Zona#

A zone from the scene, or one created with mundo.crearZona: it reports when someone enters or leaves.

PropertyTypeDescription
zona.idstringRead-only. Id of the zone.
zona.posicionVec3Read-only. Center of the zone.
zona.tamanoVec3Read-only. Width (X), height (Y) and length (Z).
zona.etiquetasreadonly string[]Read-only. The tags of the zone.
zona.activabooleanA deactivated zone does not report anyone entering or leaving.
zona.datosRecord<string, unknown>Free data for the script. Starts with the datos declared in the scene.

zona.tiene#

ts
zona.tiene(etiqueta: string): boolean

Tells whether the zone has that tag.

zona.jugadores#

ts
zona.jugadores(): Jugador[]

Players inside right now.

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

ts
jugadores.alAccion('accion', (j) => {
  if (ascensorOcupado || !mundo.zona('zona-ascensor').jugadores().includes(j)) return;
  subirAscensor();
});

zona.actores#

ts
zona.actores(): Actor[]

Actors inside right now.

zona.eliminar#

ts
zona.eliminar(): void

Removes the zone from the world.

From docs/EJEMPLOS.md, «mundo: luces, ambiente y zonas creadas por el script»:

ts
mundo.alEntrar('premio', (z, j) => { j.puntos += z.datos.puntos as number; z.eliminar(); });

Mapa#

A tile map of the scene. Columns grow toward +X; rows grow downward ('xy') or toward +Z ('xz').

PropertyTypeDescription
mapa.idstringRead-only. The id of the map in the scene.
mapa.plano'xy' | 'xz'Read-only. 'xy': a vertical map, the one for the lateral view. 'xz': a ground map, the one for the views from above and for 3D worlds.
mapa.columnasnumberRead-only. How many columns it has: the length of its longest row.
mapa.filasnumberRead-only. How many rows it has.
mapa.celdanumberRead-only. Size of one tile, in units.

mapa.leer#

ts
mapa.leer(columna: number, fila: number, capa?: number | string): string | null

Character of the tile in that cell, or null if it is empty. capa: a number or an id; by default, the first layer that is not a background.

From docs/CONTRATO.md, «Tile maps»:

ts
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 fondo

mapa.poner#

ts
mapa.poner(columna: number, fila: number, baldosa: string | null, capa?: number | string): void

Changes a tile (null erases it). Collision and drawing update on their own in the next frame.

From docs/EJEMPLOS.md, «Láminas y mapas de baldosas (2D)»:

ts
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#

ts
mapa.celdaEn(punto: Vec3): { columna: number; fila: number }

The cell that contains a point of the world (it may fall outside the map).

From docs/CONTRATO.md, «Tile maps»:

ts
const nivel = mundo.mapa('nivel');  // falla con un mensaje claro si no existe
// ...
nivel.celdaEn(jugador.posicion);  // { columna, fila }

mapa.centro#

ts
mapa.centro(columna: number, fila: number): Vec3

Center of a cell in the world. In ground maps ('xz') it lies flush with the floor.

From docs/CONTRATO.md, «Tile maps»:

ts
const nivel = mundo.mapa('nivel');  // falla con un mensaje claro si no existe
// ...
nivel.centro(3, 2);  // Vec3 del centro de la celda

mapa.buscar#

ts
mapa.buscar(baldosa: string, capa?: number | string): { columna: number; fila: number }[]

Every cell that has that tile.

From docs/CONTRATO.md, «Tile maps»:

ts
const nivel = mundo.mapa('nivel');  // falla con un mensaje claro si no existe
// ...
nivel.buscar('^');  // todas las celdas con esa baldosa

mapa.colision#

ts
mapa.colision(columna: number, fila: number): ColisionBaldosa

How that cell collides, looking at every layer that is not a background. 'ninguna' if it is empty.

From docs/CONTRATO.md, «Tile maps»:

ts
const nivel = mundo.mapa('nivel');  // falla con un mensaje claro si no existe
// ...
nivel.colision(3, 2);  // 'plataforma', 'ninguna', ...

Terreno#

Cube terrain: a world generated from a seed and from the biome table of the scene. Block coordinates are integers: block [x, y, z] spans x to x+1, y to y+1 and z to z+1. The world is 64 blocks tall (y from 0 to 63). You get it with mundo.terreno.

PropertyTypeDescription
terreno.activobooleanRead-only. The scene has terrain.
terreno.semillanumberRead-only. Seed of the world: the terreno.semilla of the scene or, if it has none, the seed of the match.
terreno.nivelDelMarnumberRead-only. Sea level: the generator fills with water up to there.
terreno.altonumberRead-only. Height of the world in blocks (64).
terreno.limitenumberRead-only. The world reaches this distance from the origin on X and on Z.
terreno.cambiosnumberRead-only. How many blocks differ today from the generated world.

terreno.bloques#

ts
terreno.bloques(): string[]

Ids of the declared blocks, in order.

terreno.biomas#

ts
terreno.biomas(): string[]

Ids of the declared biomes, in order.

terreno.info#

ts
terreno.info(bloque: string): InfoBloque

The data of a declared block: id, solido, transparente, liquido, dureza, luz, color and etiquetas.

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

ts
export const terreno = mundo.terreno;
// ...
const info = terreno.info(id);

terreno.bloque#

ts
terreno.bloque(x: number, y: number, z: number): string

Id of the block in that cell. 'aire' if it is empty or outside the world.

terreno.poner#

ts
terreno.poner(x: number, y: number, z: number, bloque: string, por?: Jugador): boolean

Places a block ('aire' removes it). Returns false if the cell already had that block or is outside the world. Notifies alPoner.

terreno.romper#

ts
terreno.romper(x: number, y: number, z: number, por?: Jugador): string

Removes the block from the cell and returns its id ('aire' if there was nothing). Notifies alRomper.

terreno.llenar#

ts
terreno.llenar(desde: Vec3, hasta: Vec3, bloque: string): number

Fills the box between two cells (inclusive) with a block. Returns how many changed. Does not notify alPoner or alRomper. Up to 4096 cells.

terreno.altura#

ts
terreno.altura(x: number, z: number): number

Height of the highest solid block in the column, plus one: where the feet end up. Trees and anything built count.

terreno.suelo#

ts
terreno.suelo(x: number, z: number): number

Height of the natural ground the generator gives for that column, without trees or changes. It is cheap: useful for far-away places.

terreno.bioma#

ts
terreno.bioma(x: number, z: number): string

Id of the biome of that column.

terreno.clima#

ts
terreno.clima(x: number, z: number): { temperatura: number; humedad: number }

Climate of that column, from 0 to 1.

terreno.buscarBioma#

ts
terreno.buscarBioma(bioma: string, desde: Vec3, radio?: number): Vec3 | null

The column of the requested biome closest to desde, or null if there is none within radio (512 by default, up to 2000). Returns the feet: [x + 0.5, ground, z + 0.5].

terreno.buscarBloque#

ts
terreno.buscarBloque(bloque: string, desde: Vec3, radio?: number, alto?: number): Vec3 | null

The cell closest to desde with that block, within radio horizontally (24 by default, up to 48), or null. With alto, only cells no farther than that vertical distance from desde count (what is within arm's reach). It generates the chunks it checks.

terreno.rayo#

ts
terreno.rayo(origen: Vec3, direccion: Vec3, distancia: number): BloqueApuntado | null

First block a ray hits (liquids and air do not count), up to 64 units.

terreno.ruido#

ts
terreno.ruido(x: number, z: number, escala?: number, canal?: number): number

Reproducible noise from -1 to 1 at a point of the plane, tied to the seed of the world. escala is the size of its patches in blocks (32 by default); canal gives independent noises.

terreno.alRomper#

ts
terreno.alRomper(fn: (cambio: CambioDeBloque) => void): void

Called every time romper removes a block, with the position, what was there, what is left and the player who did it, if any.

terreno.alPoner#

ts
terreno.alPoner(fn: (cambio: CambioDeBloque) => void): void

Called every time poner changes a block, with the same data as alRomper.

terreno.alGenerar#

ts
terreno.alGenerar(fn: (trozo: { x: number; z: number }) => void): void

Generation hook: called when the room loads a 16 x 16 chunk for the first time (near a player). It receives the corner of the chunk. Whatever the script changes there with poner or llenar stays as part of the world.

terreno.guardar#

ts
terreno.guardar(jugador: Jugador): { bloques: number; bytes: number }

Saves the changes of the world (only what differs from the generated one) in the progress of the player. Returns how many blocks and how many bytes it took.

terreno.restaurar#

ts
terreno.restaurar(jugador: Jugador): number

Applies again the changes saved by guardar. Returns how many blocks it restored (0 if there was nothing or the seed is another one).

terreno.borrarGuardado#

ts
terreno.borrarGuardado(jugador: Jugador): void

Deletes the saved changes of that player.

terreno.regenerar#

ts
terreno.regenerar(semilla: number): void

Replaces the world with that of another seed (a "new world"): it forgets the changes and sends the players back to their spawn point.

Behavior notes#

A selector is an id ('meta') or a tag with a hash sign ('#moneda'). Tags are evaluated at the moment of the touch: you can tag and untag pieces while the game runs (pieza.etiquetar('peligro')).

alTocar fires when a player starts touching a piece, solid or not (standing on it counts). rayo only hits solid pieces and ignores players.

Careful: pieza.posicion[0] = 5 moves nothing (it changes a copy). Write pieza.posicion = [5, y, z].

How to use the terrain#

The cube terrain of the scene. It always exists: in a scene without terreno, activo is false and its methods fail with a message that says how to turn it on. Blocks are named by their id ('pasto'); empty space is 'aire'.

Breaking and placing are decided by the script: the engine gives the ray, changes the block and notifies. The skeleton of all building (it is complete in the mundo-abierto template):

ts
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);
});

Worth knowing:

  • poner does not let you wall anyone in: it returns false if there is a player or an actor in that cell. Someone who only brushes it (up to a fifth of a block) is nudged that little bit. llenar does not check.
  • The bottom of the world cannot be touched (y = 0), nor what lies outside limite or above y = 63.
  • Saving. The world is not saved: it is generated again from its seed. terreno.guardar(jugador) writes to the progress of that player only what differs from the generated world, per chunk, at 4 characters per block, together with the seed. terreno.restaurar(jugador) goes back to that world (if the scene does not fix a seed, it switches to the saved one) and puts the changes on top. Caps: 60,000 changed blocks at a time (on reaching it, poner and romper fail with a clear message: check terreno.cambios) and 320 KB of save data, apart from the 64 KB of progreso. The key it uses (_terreno:<escena>) is reserved.
  • Cost. bloque, altura and buscarBloque generate the chunk if it was not there (about 1 ms the first time); suelo, bioma, clima and buscarBioma generate nothing.
  • Stepping into generation. An author who wants more than the table has three ways: estructuras in the data, direct changes at the start (llenar a camp, a lighthouse), and alGenerar, which runs when the room loads each chunk near a player (what is changed there stays as part of the world and travels to everyone). The hook does not run for far-away chunks that are only drawn: what it adds appears when you get close.

Complete examples#

Complete scripts from docs/EJEMPLOS.md. Each one assumes a scene containing what it names, and the text keys (interfaz.t('...')) must exist in textos of juego.json.

mundo: touching pieces by tag and by id#

scripts/principal.ts
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: creating pieces from the script#

scripts/principal.ts
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: moving pieces#

scripts/principal.ts
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: zones#

scripts/principal.ts
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: ray#

scripts/principal.ts
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: groups, queries, collisions, staying in touch#

scripts/principal.ts
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: lights, environment and zones created by the script#

scripts/principal.ts
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);
});

Next#