← AnimalesObjeto 3Dfogon/tortuga-marina
Tortuga marina
Arrastrá para girarlo// «Tortuga marina» · gpt-6-sol (medium)
// A cartoon sea turtle swimming, horizontal along y, head at the front (-y): a flatter oval shell in green and brown hexagon plates, a head with a beak and kind eyes, two big front flippers like wings and two small back flippers, skin in olive green with lighter scales.
// A chunky sea turtle, facing -y.
const skin = "#729b4c";
const skinLight = "#91b963";
const skinDark = "#52783c";
function oval(cx, cy, cz, rx, ry, rz, base, top) {
ellipsoid(cx, cy, cz, rx, ry, rz, base);
for (let x = Math.ceil(cx - rx); x <= cx + rx; x++)
for (let y = Math.ceil(cy - ry); y <= cy + ry; y++)
for (let z = Math.ceil(cz); z <= cz + rz; z++)
if (get(x, y, z) === base && rand() < 0.35)
set(x, y, z, top);
}
// Belly and broad, low shell.
ellipsoid(24, 27, 8, 11, 12, 8, "#c5b777");
ellipsoid(24, 27, 6, 10, 11, 5, "#a99d64");
ellipsoid(24, 27, 14, 13, 14, 9, "#405b30");
ellipsoid(24, 27, 11, 14, 15, 3, "#54743a");
// Hexagonal scutes on the domed shell. The dark gaps make the plates readable.
const plates = ["#819744", "#a18348", "#718d40", "#ae8a4c",
"#92743e", "#779346", "#9a8246"];
for (let x = 11; x <= 37; x++) {
for (let y = 13; y <= 41; y++) {
const dx = (x - 24) / 13, dy = (y - 27) / 14;
const radius2 = dx * dx + dy * dy;
if (radius2 > 0.87) continue;
const z = Math.round(14 + 9 * Math.sqrt(1 - radius2));
let nearest = Infinity, second = Infinity, qBest = 0, rBest = 0;
for (let q = -3; q <= 3; q++) {
for (let r = -3; r <= 3; r++) {
const px = 24 + 6 * (q + r / 2);
const py = 27 + 5.2 * r;
const d = (x - px) ** 2 + (y - py) ** 2;
if (d < nearest) {
second = nearest;
nearest = d;
qBest = q;
rBest = r;
} else if (d < second) second = d;
}
}
const index = ((qBest * 3 + rBest * 5) % plates.length + plates.length) % plates.length;
let color = second - nearest < 8 ? "#405a30" : plates[index];
if (second - nearest >= 8 && nearest < 5) color = "#b1a45b";
if (second - nearest >= 8 && rand() < 0.055) color = "#b3a263";
set(x, y, z, color);
if (radius2 < 0.72) set(x, y, z - 1, color);
}
}
// Little rear paddles, peeking out from behind the shell.
for (const side of [-1, 1]) {
const cx = 24 + side * 12;
line(24 + side * 8, 36, 8, cx, 41, 7, 3, skinDark);
ellipsoid(cx, 40, 7, 4, 5, 2, skin);
ellipsoid(cx, 40, 9, 3, 3, 1, skinLight);
set(cx - side, 42, 9, "#b1c879");
set(cx + side, 39, 9, "#b1c879");
}
// Neck and oversized, friendly head.
ellipsoid(24, 15, 11, 6, 7, 5, skinDark);
oval(24, 9, 13, 7, 7, 6, skin, skinLight);
ellipsoid(19, 7, 11, 3, 4, 3, "#83aa58");
ellipsoid(29, 7, 11, 3, 4, 3, "#83aa58");
// Short hooked beak and a simple smile.
ellipsoid(24, 3, 10, 4, 3, 2, "#8dad5c");
box(22, 0, 10, 26, 2, 11, "#9cba67");
box(23, 0, 9, 25, 1, 10, "#688745");
line(21, 3, 9, 27, 3, 9, 0, "#4e6338");
set(20, 3, 12, "#456039");
set(28, 3, 12, "#456039");
// Eyes sit high so they also read from above.
for (const x of [20, 28]) {
ellipsoid(x, 5, 17, 2, 2, 2, "#f3e9bd");
ellipsoid(x, 4, 18, 1, 1, 1, "#202b22");
set(x, 3, 19, "#ffffff");
set(x, 6, 19, "#a5c675");
}
for (const x of [18, 30]) {
set(x, 9, 18, "#b3cd7c");
set(x, 11, 17, "#a6c674");
}
// Filled polygon for each broad front paddle.
function inside(px, py, polygon) {
let hit = false;
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
const a = polygon[i], b = polygon[j];
if ((a[1] > py) !== (b[1] > py) &&
px < (b[0] - a[0]) * (py - a[1]) / (b[1] - a[1]) + a[0])
hit = !hit;
}
return hit;
}
const wing = [[0,16], [3,13], [7,10], [10,9], [10,14],
[9,19], [7,23], [3,26], [0,25]];
for (const side of [-1, 1]) {
const name = side < 0 ? "leftFlipper" : "rightFlipper";
const pivotX = side < 0 ? 12 : 36;
part(name, [pivotX, 20, 11]);
for (let u = 0; u <= 10; u++) {
for (let y = 9; y <= 26; y++) {
if (!inside(u + 0.5, y + 0.5, wing)) continue;
const x = pivotX + side * u;
const top = 12 - Math.floor(u * 0.34);
for (let z = top - 3; z <= top; z++)
set(x, y, z, z === top ? skin : skinDark);
if (rand() < 0.22) set(x, y, top, skinLight);
// Bold, sparse scales on the upper paddle.
if (u > 2 && u < 9 && (u + 2 * y) % 7 === 0 &&
y > 12 && y < 23)
set(x, y, top, "#b4ce7b");
}
}
ellipsoid(pivotX, 20, 11, 2, 4, 3, skin);
animate(name, { type: "swing", axis: "y",
speed: side < 0 ? 3 : -3, amplitude: 0.4 });
}
part("body", [24, 24, 12]);import { draw } from "./assets/voxel";
import { tortugaMarina } from "./assets/tortuga-marina";
// En cada cuadro: parado sobre (x, y), el centro de su base.
draw(ctx, tortugaMarina, 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 tortuga-marina
# Con las herramientas de Fogón (conector)
add_assets({ slug: "<tu-juego>", ids: ["tortuga-marina"] })
# Queda en client/assets/tortuga-marina.ts
# https://fogon.page/object/tortuga-marina