← CiudadObjeto 3Dfogon/vagon-de-subte
Vagón de subte
Arrastrá para girarlo// «Vagón de subte» · gpt-6-sol (medium)
// A subway car: a long boxy train car along y in light grey with a yellow stripe along the sides, rows of windows, sliding double doors in the middle of the sides, a front cab (-y) with a big windshield and a destination sign reading LINEA A, sitting on bogies with small wheels.
// Buenos Aires-inspired toy subway car, facing -y.
const ivory = "#e9e9e2", light = "#f5f4ec", grey = "#cbd0ce";
const shadow = "#aab2b1", yellow = "#f4c63e", gold = "#dca832";
const glass = "#315c70", glassLight = "#6ca6b3";
const dark = "#29383d", steel = "#69787b";
// Undercarriage and two chunky bogies.
box(16, 6, 5, 32, 41, 8, dark);
for (const by of [9, 31]) {
box(15, by, 4, 33, by + 9, 7, steel);
box(17, by + 1, 3, 31, by + 8, 5, dark);
for (const x of [15, 33]) {
box(x, by + 1, 5, x, by + 8, 6, shadow);
}
}
// Solid car, with a slightly inset lower skirt and a pale roof.
box(13, 4, 7, 35, 43, 9, shadow);
box(12, 4, 9, 36, 43, 22, ivory);
box(13, 4, 23, 35, 43, 24, light);
box(15, 5, 25, 33, 42, 25, grey);
// Gentle variation in the large painted side panels.
for (const x of [12, 36]) {
for (let y = 6; y <= 42; y++) {
for (let z = 10; z <= 22; z++) {
if (rand() < 0.035) set(x, y, z, rand() < 0.5 ? light : grey);
}
}
}
// Continuous yellow route stripe and darker lower sill.
for (const x of [12, 36]) {
box(x, 5, 11, x, 42, 12, yellow);
box(x, 5, 9, x, 42, 9, grey);
box(x, 5, 22, x, 42, 22, grey);
}
box(12, 4, 11, 36, 4, 12, yellow);
box(12, 43, 11, 36, 43, 12, yellow);
// Side windows, including the cab's narrower side pane.
for (const x of [12, 36]) {
for (const [a, b] of [[5, 7], [9, 12], [15, 18], [32, 35], [38, 41]]) {
box(x, a, 14, x, b, 20, shadow);
box(x, a, 15, x, b, 19, glass);
box(x, a, 19, x, b, 19, glassLight);
set(x, a, 18, "#a5ccd0");
box(x, a, 14, x, b, 14, dark);
}
// Center sliding double doors: a distinct pair of silver leaves.
box(x, 21, 9, x, 29, 22, steel);
box(x, 22, 10, x, 24, 21, grey);
box(x, 26, 10, x, 28, 21, grey);
box(x, 25, 10, x, 25, 21, dark);
for (const [a, b] of [[22, 24], [26, 28]]) {
box(x, a, 16, x, b, 19, glass);
box(x, a, 19, x, b, 19, glassLight);
box(x, a, 15, x, b, 15, light);
}
for (const y of [24, 26]) box(x, y, 12, x, y, 14, dark);
box(x, 21, 22, x, 29, 22, yellow);
box(x, 21, 9, x, 29, 9, gold);
}
// Front cab: broad windshield, destination display, lights and bumper.
box(14, 3, 12, 34, 3, 19, steel);
box(15, 3, 13, 33, 3, 18, glass);
box(15, 3, 18, 33, 3, 18, glassLight);
box(17, 2, 17, 22, 2, 17, "#a9d1d4");
box(17, 2, 16, 18, 2, 16, "#a9d1d4");
box(25, 2, 13, 31, 2, 13, "#507b88");
box(14, 2, 12, 34, 2, 12, light);
box(13, 3, 20, 35, 3, 25, dark);
// Small, variable-width pixel letters: LINEA A.
const letters = {
L: ["10", "10", "10", "10", "11"],
I: ["1", "1", "1", "1", "1"],
N: ["101", "111", "111", "111", "101"],
E: ["111", "100", "110", "100", "111"],
A: ["010", "101", "111", "101", "101"],
" ": ["00", "00", "00", "00", "00"]
};
let tx = 13;
for (const ch of "LINEA A") {
const glyph = letters[ch];
for (let row = 0; row < 5; row++)
for (let col = 0; col < glyph[row].length; col++)
if (glyph[row][col] === "1") set(tx + col, 2, 24 - row, "#fff2a2");
tx += glyph[0].length + 1;
}
for (const x of [14, 33]) {
box(x, 2, 10, x + 2, 2, 11, "#fff1bc");
set(x + 1, 1, 11, "#ffffff");
}
box(14, 2, 7, 34, 2, 8, steel);
box(17, 1, 7, 31, 1, 7, dark);
box(22, 1, 8, 26, 1, 8, yellow);
// Back marker lamps.
for (const x of [15, 33]) box(x, 44, 10, x + 1, 44, 11, "#d7564c");
// Roof equipment and long clean roof seams.
for (const y of [12, 32]) {
box(18, y, 26, 30, y + 6, 27, grey);
box(19, y + 1, 28, 29, y + 5, 28, light);
for (let i = 0; i < 4; i++)
box(20, y + 1 + i, 29, 28, y + 1 + i, 29, steel);
}
box(14, 6, 25, 14, 41, 25, light);
box(34, 6, 25, 34, 41, 25, light);
// Each exposed wheel is separately animated around its own axle.
for (const x of [10, 37]) {
for (const y of [11, 16, 33, 38]) {
const name = `wheel_${x}_${y}`;
part(name, [x + 0.5, y, 3]);
for (let dx = 0; dx <= 1; dx++) {
for (let dy = -3; dy <= 3; dy++) {
for (let dz = -3; dz <= 3; dz++) {
const d = dy * dy + dz * dz;
if (d <= 9) {
const faceX = x + dx;
set(faceX, y + dy, 3 + dz,
d >= 6 ? "#222b2d" : d >= 3 ? "#687478" : "#adb6b4");
}
}
}
}
const outer = x === 10 ? 9 : 39;
for (const [dy, dz] of [[0, 2], [2, 0], [0, -2], [-2, 0]])
set(outer, y + dy, 3 + dz, light);
set(outer, y, 3, dark);
animate(name, { type: "spin", axis: "x", speed: 6 });
part("body");
}
}import { draw } from "./assets/voxel";
import { vagonDeSubte } from "./assets/vagon-de-subte";
// En cada cuadro: parado sobre (x, y), el centro de su base.
draw(ctx, vagonDeSubte, x, y, { size: 64, angle: 0, t: performance.now() / 1000 });
// size: lo que mide en px su lado más largo
// angle: giro sobre el piso, en radianes
// t: segundos; si se mueve, se anima solo con eso# En una terminal, en la carpeta del juego
fogon assets add vagon-de-subte
# Con las herramientas de Fogón (conector)
add_assets({ slug: "<tu-juego>", ids: ["vagon-de-subte"] })
# Queda en client/assets/vagon-de-subte.ts
# https://fogon.page/object/vagon-de-subte