fogón

← BibliotecaKitmormon/starter-city-builder

Kit de inicio: constructor de ciudad

m@mormonKit, MIT
v1hace 2 días

Obra original de Kenney ↗

Clonar
FOGONfogon pieces add starter-city-builder
GITgit clone https://git.fogon.gg/mormon/starter-city-builder.git
REMIXfogon new starter-city-builder --from starter-city-builder
ZIPstarter-city-builder.zipBajar
Controles

Kit de inicio: constructor de ciudad

El Starter Kit City Builder de Kenney, que está hecho para Godot, portado a three.js y TypeScript para Fogón: la misma mecánica, los mismos números y la misma escena, con sus modelos y sonidos originales. Una grilla de casillas de un metro, un selector que sigue al puntero y quince estructuras para colocar, girar y demoler; cada una cuesta plata, la cámara se desplaza, gira y hace zoom, y el mapa se guarda y se carga.

Una ciudad chica armada con el kit

En la demo: clic construye lo que está elegido, clic derecho (o R) lo gira, Supr (o X) demuele lo que hay bajo el selector y Q / E cambian de estructura. La cámara se mueve con W A S D, vuelve al centro con F, gira con el botón del medio apretado (o con ← →) y se acerca con la rueda. F1 guarda el mapa, F2 lo carga y F3 trae el mapa de muestra del kit; los mismos botones están arriba, junto con «Exportar / importar» (el mapa como texto) y «Vaciar». En el teléfono: un toque construye, arrastrar mueve la cámara, dos dedos hacen zoom, y los botones de la derecha giran la estructura y pasan de construir a demoler.

Es solo la página. No hay sala ni red: todo pasa en el navegador de quien juega. Es una base para empezar un juego propio, no un juego terminado.

Qué trae

client/city-builder es lo que se lleva.

ArchivoQué hace
builder.tsBuilder: la grilla y el selector. aim / aimAt apuntan, build, demolish, rotate, toggle y choose son las acciones, save y load pasan el mapa a datos y de vuelta. Es el builder.gd del kit.
map.tsCityMap: la ciudad como datos (la plata y qué hay en cada casilla), sin motor. toJSON() y CityMap.from(datos).
structures.tsSTRUCTURES: las quince estructuras, cada una con su modelo, su nombre y su precio.
sample.tsSAMPLE_MAP: el mapa de muestra del kit (122 casillas), decodificado de su map.res.
view.tsView: la cámara en un brazo que mira un punto del suelo. Se mueve, gira y hace zoom suavizado (el view.gd del kit).
environment.tsEnvironment: el fondo, la luz de ambiente, el sol con sombras y la misma curva de color que usa Godot («Filmic»).
audio.tsSounds: los efectos (uno al azar entre varios, con el tono apenas distinto cada vez, como el kit) y el ambiente en bucle.
assets.tsDe dónde salen los archivos: loadModel, loadTexture, url y setBase.

Lo demás es la demo y sirve de ejemplo: client/main.ts tiene el mouse, el teclado, los toques, los botones, guardar en el navegador y lo que se escribe en pantalla.

Cómo se usa en un juego

fogon pieces add starter-city-builder

Eso trae client/city-builder/ (solo código) y agrega three.js al package.json. Los modelos, las imágenes y los sonidos no viajan con el juego: se cargan desde la demo publicada de esta pieza (https://play.fogon.page/starter-city-builder/city/).

import * as THREE from "three";
import { Builder } from "./city-builder/builder";
import { Environment } from "./city-builder/environment";
import { Sounds } from "./city-builder/audio";
import { View } from "./city-builder/view";

const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
const scene = new THREE.Scene();
const environment = new Environment(scene, renderer); // fondo, luces, sombras y curva de color
const view = new View();
const sounds = new Sounds();
const builder = await Builder.create({
  sounds,
  onCash: (cash) => (cashLabel.textContent = "$" + cash),
});
scene.add(view.object, builder.object);

// El puntero: el selector va a la casilla que tiene debajo, y un clic construye.
const raycaster = new THREE.Raycaster();
canvas.addEventListener("pointermove", (e) => {
  raycaster.setFromCamera(new THREE.Vector2((e.clientX / innerWidth) * 2 - 1, -(e.clientY / innerHeight) * 2 + 1), view.camera);
  builder.aim(raycaster.ray);
});
canvas.addEventListener("pointerdown", () => {
  sounds.unlock(); // el navegador solo deja sonar después de un toque o una tecla
  builder.build();
});

renderer.setAnimationLoop(() => {
  const dt = 1 / 60;
  view.update(dt, 0, 0); // (derecha, atrás): hacia dónde mover la cámara, de −1 a 1
  builder.update(dt);
  environment.follow(view.object.position, view.zoom * 0.6 + 6); // las sombras, donde mira la cámara
  renderer.render(scene, view.camera);
});

// Guardar y cargar: el mapa es un objeto chico, sirve para localStorage o para mandarlo a una sala.
const data = builder.save(); // { cash: 9905, structures: [[x, z, estructura, giros], …] }
builder.load(data);

Agregar estructuras

Una estructura es un modelo que ocupa una casilla (de −0,5 a 0,5, apoyado en y = 0) y un precio. La lista se le pasa al constructor; un mapa nombra cada estructura por su lugar en la lista, así que conviene agregar al final.

import { STRUCTURES } from "./city-builder/structures";

const builder = await Builder.create({
  structures: [...STRUCTURES, { model: "building-garage", label: "Otro garaje", price: 120 }],
});

model es el nombre de un .glb de la carpeta models/ de donde se cargan los archivos. Para usar modelos propios, servilos vos (el punto que sigue) y sumalos a esa carpeta.

Usar los archivos sin depender de la demo

Para tenerlos dentro de tu juego (sin conexión, o servidos por vos), bajalos a public/city/ y decíselo al cargador una vez, antes de cargar nada:

mkdir -p public/city/models/Textures public/city/sprites public/city/sounds && cd public/city
B=https://play.fogon.page/starter-city-builder/city
for m in road-straight road-straight-lightposts road-corner road-split road-intersection pavement pavement-fountain \
  building-small-a building-small-b building-small-c building-small-d building-garage grass grass-trees grass-trees-tall; do
  curl -fo models/$m.glb $B/models/$m.glb
done
curl -fo models/Textures/colormap.png $B/models/Textures/colormap.png
for s in selector coin; do curl -fo sprites/$s.png $B/sprites/$s.png; done
for s in ambience placement-a placement-b placement-c placement-d removal-a removal-b removal-c removal-d rotate toggle; do
  curl -fo sounds/$s.ogg $B/sounds/$s.ogg
done
import { setBase } from "./city-builder/assets";
setBase("./city/");

Son 1,7 MB: quince modelos (models/*.glb y su paleta, models/Textures/colormap.png), dos imágenes (sprites/selector.png, sprites/coin.png) y once sonidos (sounds/*.ogg; el ambiente pesa 1,2 MB él solo).

Para hacerlo entre varios

El mapa ya es datos (CityMap): en server/room.ts guardá uno por sala, que el cliente mande «construí esto acá» y que la sala lo valide (¿alcanza la plata?), lo aplique y lo reparta. Builder dibuja lo que diga el mapa.

Qué quedó distinto del original

  • El mapa se guarda como JSON (en el kit es un recurso binario de Godot). El giro de cada casilla va de 0 a 3 cuartos de vuelta; el kit guarda el índice de orientación de Godot (0, 16, 10, 22).
  • Demoler también sale con X o con retroceso, girar con R y la cámara gira con las flechas: no todos los teclados tienen Supr ni todos los mouse botón del medio. Los controles táctiles, los botones, «Exportar / importar» y «Vaciar» son agregados.
  • La cámara se mueve a 15 m por segundo en cualquier pantalla (el kit avanza un cuarto de metro por cuadro: lo mismo a 60 cuadros por segundo).
  • El kit prende oclusión ambiental (SSAO); acá no está.
  • Como en el kit: la plata puede quedar en negativo, demoler no devuelve nada y volver a poner lo mismo en una casilla (para girarlo) es gratis.

Créditos y licencia

El kit original, sus modelos, imágenes y sonidos son de Kenney (kenney.nl): el código bajo licencia MIT (va copiada en LICENSE-kenney.md) y los modelos, imágenes y sonidos en dominio público (CC0). El port a three.js es nuestro, también MIT. No hace falta dar crédito a Kenney, pero se agradece.