← City3D assetfogon/tire-stack
Pila de neumáticos
Drag to turn it// «Pila de neumáticos» · propio
// A stack of five black rubber tires with visible tread and round holes in the middle, one more tire leaning against the stack.
// Five tires in a stack and one more leaning against it.
const RUBBER = { side: "#28292d", sideTop: "#3e4046", ring: "#5a5d64", bead: "#33353a", treadA: "#17181a", treadB: "#474a50", rib: "#2c2e32", hole: "#141517" };
const HALF = 3, OUTER = 11.2, INNER = 5.1;
// What a tire has at a point: `axial` along its axle (0 in the middle), `radial` from the axle, `angle` around it.
// `up`: +1 or -1, the side that faces the light.
function tireAt(axial, radial, angle, up) {
if (Math.abs(axial) > HALF) return null;
const shoulder = Math.abs(axial) > 2;
const outer = shoulder ? OUTER - 1 : OUTER, inner = shoulder ? INNER + 0.8 : INNER;
if (radial > outer || radial < inner) return null;
if (radial > outer - 1.3) {
// The tread: dark shoulders (they part one tire from the next), a rib round the middle and lugs on either side.
if (shoulder) return RUBBER.treadA;
if (Math.abs(axial) < 1) return RUBBER.rib;
const seg = Math.floor(((angle + Math.PI) / (2 * Math.PI)) * 24 + (axial > 0 ? 0.5 : 0));
return seg % 2 ? RUBBER.treadA : RUBBER.treadB;
}
if (radial < inner + 1.1) return axial * up > 2 ? RUBBER.bead : RUBBER.hole;
const lit = axial * up > 2;
if (radial > 7.6 && radial < 8.8) return lit ? RUBBER.ring : RUBBER.sideTop;
return lit ? RUBBER.sideTop : RUBBER.side;
}
// The one that leans: its axle points towards the stack and a little down.
const t = (22 * Math.PI) / 180;
const ax = Math.cos(t), az = -Math.sin(t); // the axle
const ux = Math.sin(t), uz = Math.cos(t); // "up" within the tire
const CX = 12.5, CY = 24, CZ = 11.6;
for (let x = 0; x <= 30; x++)
for (let y = 10; y <= 38; y++)
for (let z = 0; z <= 27; z++) {
const dx = x - CX, dy = y - CY, dz = z - CZ;
const axial = dx * ax + dz * az;
const rx = dx - axial * ax, rz = dz - axial * az;
const radial = Math.hypot(rx, dy, rz);
const c = tireAt(axial, radial, Math.atan2(dy, rx * ux + rz * uz), -1);
if (c) set(x, y, z, c);
}
// The stack: each tire a little off the one below.
const offsets = [[0, 0], [1, -1], [-1, 0], [0, 1], [1, 0]];
offsets.forEach(([ox, oy], i) => {
const cx = 31 + ox, cy = 24 + oy;
for (let x = cx - 12; x <= cx + 12; x++)
for (let y = cy - 12; y <= cy + 12; y++)
for (let k = 0; k < 6; k++) {
const c = tireAt(k - 2.5, Math.hypot(x - cx, y - cy), Math.atan2(y - cy, x - cx) + i * 0.4, 1);
if (c) set(x, y, i * 6 + k, c);
}
});import { draw } from "./assets/voxel";
import { tireStack } from "./assets/tire-stack";
// Every frame: standing on (x, y), the centre of its base.
draw(ctx, tireStack, x, y, { size: 64, angle: 0, t: performance.now() / 1000 });
// size: its longest side, in px
// angle: turn on the floor, in radians
// t: seconds; if it moves, that animates it# In a terminal, in the game's folder
fogon assets add tire-stack
# With Fogón's tools (connector)
add_assets({ slug: "<your-game>", ids: ["tire-stack"] })
# It lands in client/assets/tire-stack.ts
# https://fogon.gg/object/tire-stack