Harvest Festival · chicken_coop.js
鶏小屋
赤い屋根とスロープを備えた木造の鶏小屋に、藁の巣、散らばった穀物、そして白い雌鶏が配置された 48×48 の小物です。16 コマの idle(待機)では雌鶏が地面を 2 回つつき、16 コマの roost では戸口に座る雌鶏と窓から顔を出す別の鶏が描かれます。8 コマ一回再生の egg では雌鶏が羽を膨らませて立ち上がり、巣の中に卵が現れます。
6×
動くスプライト素材: Harvest Festival(itch.io)Vol.6・収穫祭の小物 12 点・37 アニメ
冒頭のコメント。これが指示書です。サイズ、視点、光の向き、すべての変種が書かれています。
chicken_coop.js · 1–11
// Chicken coop — 48x48 floor prop, top-down 3/4, key light top-left.// A little wooden hen house on four short legs: vertical planks with a lit left side, a barn-red shingled roof with// a wooden ridge cap and barge boards, an arched door with straw spilling over the sill, a square window, and a// cleated plank ramp running down to the ground. In front: a straw nest, scattered grain, and a white hen with a// brown barred wing, dark brown tail, red comb and wattle, yellow beak and legs.// Variants:// idle 16f loop the hen pecks the ground twice, her head bobs between pecks, her tail flicks// roost 16f loop the hen sits in the doorway and breathes, blinks once; a second hen's head peeks out of the// window and ducks back// egg 8f once the hen fluffs up and rises, an egg is revealed in the straw nest under her, she settles back// onto it; the last frame equals idle frame 0最後の数行。submit でキャンバスの大きさと、描画エンジンが書き出す変種の一覧を指定します。
chicken_coop.js · 344–354
submit(Coop, { size: [W, H], origin: "top-left", kind: "icon", scale: 1, animate: { fps: 10, duration: N_IDLE / 10, loop: true }, anchor: { x: CX, y: GROUND }, args: ["idle"], variants: [ { name: "idle", args: ["idle"] }, { name: "roost", args: ["roost"], animate: { fps: 10, duration: N_ROOST / 10, loop: true } }, { name: "egg", args: ["egg"], animate: { fps: 10, duration: N_EGG / 10, loop: false } }, ],});プログラム全体を表示 (354 行)
chicken_coop.js · 354 行
// Chicken coop — 48x48 floor prop, top-down 3/4, key light top-left.// A little wooden hen house on four short legs: vertical planks with a lit left side, a barn-red shingled roof with// a wooden ridge cap and barge boards, an arched door with straw spilling over the sill, a square window, and a// cleated plank ramp running down to the ground. In front: a straw nest, scattered grain, and a white hen with a// brown barred wing, dark brown tail, red comb and wattle, yellow beak and legs.// Variants:// idle 16f loop the hen pecks the ground twice, her head bobs between pecks, her tail flicks// roost 16f loop the hen sits in the doorway and breathes, blinks once; a second hen's head peeks out of the// window and ducks back// egg 8f once the hen fluffs up and rises, an egg is revealed in the straw nest under her, she settles back// onto it; the last frame equals idle frame 0const W = 48, H = 48, CX = 24, GROUND = 45;const N_IDLE = 16, N_ROOST = 16, N_EGG = 8;const HB = ["n2", "n3", "n4", "n5"]; // white plumage rampconst BX = 32, BY = 45; // ground hen: feet column / ground row// ---- a tiny layered raster: each layer is emitted as named fields; a layer may carry its own ink ring ----------function Layer(name, clip) { const px = new Map(); return { name, px, clip, set(x, y, k, part) { if (!k || x < 0 || y < 0 || x >= W || y >= H) return; if (clip && !clip(x, y)) return; px.set(y * W + x, [k, part || name]); }, has(x, y) { return px.has(y * W + x); }, };}function emitParts(g, entries) { const parts = new Map(); for (const [i, [k, p]] of entries) { if (!parts.has(p)) parts.set(p, { m: new Map(), x0: W, x1: -1, y0: H, y1: -1 }); const P = parts.get(p), x = i % W, y = (i - x) / W; P.m.set(i, k); P.x0 = Math.min(P.x0, x); P.x1 = Math.max(P.x1, x); P.y0 = Math.min(P.y0, y); P.y1 = Math.max(P.y1, y); } for (const [p, P] of parts) g.part(p, () => g.field({ x: [P.x0, P.x1], y: [P.y0, P.y1] }, (x, y) => P.m.get(y * W + x) ?? null));}// ring: ink on every 4-neighbour of the layer that the layer itself does not cover (separates it from what's behind)function emitLayer(g, L, ring) { emitParts(g, L.px); if (!ring) return; const edge = new Map(); for (const i of L.px.keys()) { const x = i % W, y = (i - x) / W; for (const [dx, dy] of [[1, 0], [-1, 0], [0, 1], [0, -1]]) { const nx = x + dx, ny = y + dy; if (nx < 0 || ny < 0 || nx >= W || ny >= H || L.has(nx, ny)) continue; if (L.clip && !L.clip(nx, ny)) continue; edge.set(ny * W + nx, ["ink", L.name + "Edge"]); } } emitParts(g, edge);}function fillEll(L, cx, cy, rx, ry, fn) { for (let y = Math.floor(cy - ry - 1); y <= Math.ceil(cy + ry + 1); y++) for (let x = Math.floor(cx - rx - 1); x <= Math.ceil(cx + rx + 1); x++) { const nx = (x + 0.5 - cx) / rx, ny = (y + 0.5 - cy) / ry; if (nx * nx + ny * ny <= 1) L.set(x, y, fn(x, y, nx, ny)); }}function inPoly(pts, x, y) { let c = false; for (let i = 0, j = pts.length - 1; i < pts.length; j = i++) { const [xi, yi] = pts[i], [xj, yj] = pts[j]; if ((yi > y) !== (yj > y) && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi) c = !c; } return c;}function fillPoly(L, pts, fn) { const xs = pts.map((p) => p[0]), ys = pts.map((p) => p[1]); for (let y = Math.floor(Math.min(...ys)); y <= Math.ceil(Math.max(...ys)); y++) for (let x = Math.floor(Math.min(...xs)); x <= Math.ceil(Math.max(...xs)); x++) if (inPoly(pts, x + 0.5, y + 0.5)) L.set(x, y, fn(x, y));}function disc(L, cx, cy, r, fn) { fillEll(L, cx, cy, r, r, fn); }// ---- the coop ------------------------------------------------------------------------------------------------const roofL = (y) => Math.round(11 - ((y - 8) * 3) / 10);const roofR = (y) => 48 - roofL(y);const DOOR = { x0: 16, x1: 21, y0: 23, y1: 32 }; // door interiorconst WIN = { x0: 28, x1: 32, y0: 24, y1: 27 }; // window interiorconst inDoor = (x, y) => x >= DOOR.x0 && x <= DOOR.x1 && y >= DOOR.y0 && y <= DOOR.y1 && !(y === DOOR.y0 && (x === DOOR.x0 || x === DOOR.x1));const inWin = (x, y) => x >= WIN.x0 && x <= WIN.x1 && y >= WIN.y0 && y <= WIN.y1;function coop(L) { // weathervane: a little iron rooster on a pole at the ridge for (let y = 3; y <= 6; y++) L.set(24, y, "n2", "vane"); for (const [x, y, k] of [[22, 1, "n3"], [23, 1, "n2"], [22, 2, "n2"], [23, 2, "n2"], [24, 2, "n1"], [25, 2, "n2"], [26, 1, "n1"], [23, 3, "n1"], [24, 3, "n1"], [25, 3, "n1"], [21, 2, "g1"], [22, 0, "r1"]]) L.set(x, y, k, "vane"); // legs: back pair (deeper in shadow, shorter), front pair for (let y = 35; y <= 39; y++) { L.set(17, y, "w0", "legs"); L.set(31, y, "w0", "legs"); } for (let y = 35; y <= 42; y++) { L.set(13, y, "w3", "legs"); L.set(14, y, "w2", "legs"); L.set(33, y, "w2", "legs"); L.set(34, y, "w1", "legs"); } L.set(13, 35, "w1", "legs"); L.set(14, 35, "w0", "legs"); L.set(33, 35, "w0", "legs"); L.set(34, 35, "w0", "legs"); // floor beam under the wall: lit top edge, shaded face, dark underside for (let x = 11; x <= 37; x++) { L.set(x, 33, x < 26 ? "w3" : "w2", "sill"); L.set(x, 34, x < 26 ? "w2" : "w1", "sill"); } L.set(11, 33, "w4", "sill"); L.set(12, 33, "w4", "sill"); for (let x = 15; x <= 31; x++) L.set(x, 35, "w0", "sill"); // front wall: vertical planks for (let y = 20; y <= 32; y++) for (let x = 12; x <= 36; x++) { const c = (x - 12) % 3, pl = Math.floor((x - 12) / 3); let k = pl < 3 ? "w3" : pl < 6 ? "w3" : "w2"; if (pl >= 6 && c === 0) k = "w3"; // far planks keep a thin lit edge if (pl < 2 && c === 0) k = "w4"; if (c === 2) k = "w1"; // seam const gy = 25 + ((pl * 5) % 6); // one grain streak per plank if (c === 1 && (y === gy || y === gy + 1)) k = pl < 6 ? "w2" : "w1"; if (y === 20) k = "w0"; // eave shadow else if (y === 21) k = c === 2 ? "w0" : "w1"; if ((y === 22 || y === 31) && c === 1) k = "n3"; // nail heads if (x === 36) k = "w1"; L.set(x, y, k, "wall"); } // door: trim, dark interior, straw on the floor spilling over the sill for (let y = DOOR.y0 - 1; y <= DOOR.y1; y++) for (let x = DOOR.x0 - 1; x <= DOOR.x1 + 1; x++) { if (inDoor(x, y)) continue; const near = inDoor(x + 1, y) || inDoor(x - 1, y) || inDoor(x, y + 1) || inDoor(x + 1, y + 1) || inDoor(x - 1, y + 1); if (!near) continue; const left = x <= DOOR.x0 || (y < DOOR.y0 + 1 && x < 19); L.set(x, y, left ? "w4" : x > DOOR.x1 ? "w1" : "w3", "door"); } for (let y = DOOR.y0; y <= DOOR.y1; y++) for (let x = DOOR.x0; x <= DOOR.x1; x++) { if (!inDoor(x, y)) continue; let k = y < DOOR.y0 + 2 ? "ink" : "n0"; if (x === DOOR.x1 && y > DOOR.y0 + 1) k = "w0"; // the far jamb's inner face catches a little light if (y === 30) k = x % 3 === 0 ? "g1" : "g0"; // straw bedding: dark at the back, lit strands in front if (y === 31) k = x % 2 === 0 ? "g1" : "g0"; if (y === 32) k = x === 17 || x === 20 ? "g2" : "g1"; if (y === 29 && x === 18) k = "g0"; L.set(x, y, k, "door"); } for (const [x, y, k] of [[16, 33, "g2"], [18, 33, "g3"], [19, 33, "g2"], [21, 33, "g2"], [17, 34, "g1"], [20, 34, "g2"]]) L.set(x, y, k, "straw"); // window: lit top/left frame, dark inside, sill board, straw poking over the sill for (let y = WIN.y0 - 1; y <= WIN.y1 + 1; y++) for (let x = WIN.x0 - 1; x <= WIN.x1 + 1; x++) { if (inWin(x, y)) L.set(x, y, y === WIN.y0 ? "ink" : "n0", "window"); else L.set(x, y, x === WIN.x0 - 1 || y === WIN.y0 - 1 ? (x < WIN.x1 + 1 ? "w4" : "w3") : "w1", "window"); } for (let x = WIN.x0 - 2; x <= WIN.x1 + 2; x++) { L.set(x, WIN.y1 + 2, x < WIN.x1 ? "w3" : "w2", "window"); L.set(x, WIN.y1 + 3, "w0", "window"); } for (const [x, y, k] of [[29, 27, "g0"], [30, 27, "g1"], [32, 27, "g0"]]) L.set(x, y, k, "straw"); // roof: fascia board, front slope of staggered shingles, ridge cap, barge boards for (let x = roofL(19) + 1; x <= roofR(19) - 1; x++) L.set(x, 19, x < 22 ? "w4" : x < 33 ? "w3" : "w2", "roof"); for (let y = 8; y <= 18; y++) { const r = Math.floor((y - 8) / 3), line = (y - 8) % 3; for (let x = roofL(y); x <= roofR(y); x++) { const u = (x - roofL(y)) / (roofR(y) - roofL(y)); let k = "r1"; if (line === 0) k = "r0"; // shadow under the row above else if ((x + r * 2) % 4 === 0) k = line === 1 ? "r0" : "r1"; // gaps between tabs else if (line === 1 && u < 0.6 && (x + r * 2) % 4 <= 2) k = "r2"; // lit top-left corner of each tab else if (line === 2 && u > 0.75) k = "r0"; if (y === 18) k = u < 0.5 ? "r2" : u < 0.85 ? "r1" : "r0"; // the eave edge if (x === roofL(y)) k = "w3"; // barge boards if (x === roofR(y)) k = "w1"; L.set(x, y, k, "roof"); } } // a weathered tab and a patch of moss for (const [x, y, k] of [[30, 12, "r0"], [31, 12, "r0"], [15, 15, "e1"], [16, 15, "e2"], [17, 16, "e1"], [35, 9, "e1"]]) L.set(x, y, k, "roof"); for (let x = 11; x <= 37; x++) { L.set(x, 7, x < 20 ? "w3" : x < 32 ? "w2" : "w1", "ridge"); if (x > 11 && x < 37) L.set(x, 6, x < 20 ? "w4" : x < 32 ? "w3" : "w2", "ridge"); }}function leaves(L) { // two fallen leaves resting on the roof (static detail that ties the pack's autumn theme together) for (const [x, y, k] of [[20, 11, "r3"], [21, 11, "r2"], [28, 15, "g2"], [29, 15, "g1"]]) L.set(x, y, k, "leaves");}function ramp(L) { const P = [[16, 34], [22, 34], [12, 44], [6, 44]]; fillPoly(L, P, (x, y) => { const t = (y - 34) / 10, l = 16 - t * 10, r = 22 - t * 10; const u = (x + 0.5 - l) / (r - l); let k = u < 0.2 ? "w4" : u < 0.7 ? "w3" : "w2"; if (y === 44) k = u < 0.5 ? "w1" : "w0"; return k; }); // cleats: a raised slat with a lit top and a shadowed foot for (const cy of [36, 39, 42]) { const t = (cy - 34) / 10, l = Math.round(16 - t * 10), r = Math.round(21 - t * 10); for (let x = l; x <= r; x++) { L.set(x, cy, x < l + 2 ? "w4" : "w3", "cleats"); L.set(x, cy + 1, "w1", "cleats"); } }}function nest(L) { // a low heap of straw behind the hen fillEll(L, 34, 41.5, 5.6, 2.2, (x, y, nx, ny) => { const a = -0.6 * nx - 0.8 * ny; let k = a > 0.45 ? "g2" : a > -0.35 ? "g1" : "g0"; if ((x * 3 + y * 2) % 7 === 0 && a > -0.2) k = "g2"; // a few strands return k; }); for (const [x, y, k] of [[28, 40, "g2"], [40, 40, "g1"]]) L.set(x, y, k, "nest");}function nestLip(L) { // straw strands in front of the egg's lower half for (const [x, y, k] of [[33, 41, "g1"], [34, 41, "g2"], [35, 41, "g1"], [36, 41, "g3"], [37, 41, "g1"]]) L.set(x, y, k, "nestLip");}function egg(L) { L.set(35, 37, "n5", "egg"); for (let y = 38; y <= 40; y++) for (let x = 34; x <= 36; x++) L.set(x, y, x === 34 ? (y === 40 ? "n3" : "n5") : x === 36 ? "n3" : "n4", "egg"); L.set(35, 38, "n5", "egg"); L.set(36, 40, "n2", "egg");}// ---- the hen ---------------------------------------------------------------------------------------------------// pose: { bx, by, peck 0..1, hd (head x nudge), tail 0/1, fluff 0..1, lift px, blink, sit, br (breath 0..1), face -1 }function hen(L, p) { const cx = p.bx + 1, cy = p.by - (p.sit ? 3.4 : 6) - p.lift - (p.br || 0) * 0.4; const rx = 5.6 + p.fluff * 1.2, ry = 3.6 + p.fluff * 0.7 + (p.br || 0) * 0.4; // legs (behind the body) if (!p.sit) for (const [lx, lit] of [[p.bx - 2, true], [p.bx, false]]) { for (let y = Math.floor(cy + 1); y < p.by; y++) L.set(lx, y, lit ? "g2" : "g1", "legs"); L.set(lx - 1, p.by - 1, "g1", "legs"); L.set(lx, p.by - 1, lit ? "g2" : "g1", "legs"); L.set(lx + 1, p.by - 1, "g0", "legs"); } // tail: a dark brown fan raised behind const tf = p.tail; fillPoly(L, [[cx + 2, cy - 1], [cx + 3 + tf, cy - 7], [cx + 5 + tf, cy - 8], [cx + 7 + tf, cy - 5], [cx + 7, cy + 1]], (x, y) => { const along = (x - cx) + (cy - y); if (along > 11 + tf) return "w0"; if ((x - cx) <= 3 + tf && y < cy - 2) return "w2"; // lit leading edge of the sickle feathers return (x + y) % 3 === 0 ? "w0" : "w1"; }); // body const fl = p.fluff; fillEll(L, cx, cy, rx, ry, (x, y, nx, ny) => { const i = litIndex(x, y, cx, cy, rx, ry, 4, 0.22); return HB[Math.max(0, i)]; }); if (fl > 0.5) for (const [dx, dy] of [[-2, -1], [0, -1], [2, -1], [4, 0], [-4, 0]]) { // ruffled feather tips break the outline const x = Math.round(cx + dx * rx / 4), y = Math.round(cy - ry + dy + (Math.abs(dx) === 4 ? ry * 0.6 : 0)); L.set(x, y, dx < 0 ? "n5" : "n4", "body"); } // wing: brown with darker bars, folded over the flank fillEll(L, cx + 1.2, cy - 0.1, 3.4 + fl * 0.6, 2.1 + fl * 0.3, (x, y, nx, ny) => { if (ny < -0.45) return nx < 0.2 ? "w4" : "w3"; if ((x + y * 2) % 3 === 0 && ny < 0.6) return "w1"; return ny > 0.55 ? "w1" : "w2"; }); // neck + head const up = [cx - 4 + (p.hd || 0), cy - 5 + (p.sit ? 0.5 : 0)], down = [cx - 7, p.by - 4]; const k = p.peck; const hx = Math.round(up[0] + (down[0] - up[0]) * k), hy = Math.round(up[1] + (down[1] - up[1]) * k); for (let s = 0; s <= 1.0001; s += 0.2) { const nx0 = cx - 3 + (hx - (cx - 3)) * s, ny0 = cy - 1.5 + (hy - (cy - 1.5)) * s; disc(L, nx0, ny0, 1.6, (x) => (x < nx0 ? "n5" : "n4")); } disc(L, hx + 0.5, hy + 0.5, 2.3, (x, y, nx, ny) => (nx + ny < -0.2 ? "n5" : nx + ny < 0.7 ? "n4" : "n3")); // comb and wattle const dn = k > 0.5; if (!dn) { for (const [x, y, c] of [[hx - 1, hy - 3, "r2"], [hx, hy - 3, "r1"], [hx + 1, hy - 3, "r1"], [hx - 1, hy - 4, "r2"], [hx + 1, hy - 4, "r1"], [hx + 2, hy - 2, "r0"], [hx - 3, hy + 1, "r1"], [hx - 3, hy + 2, "r0"]]) L.set(x, y, c, "comb"); L.set(hx - 3, hy, "g2", "beak"); L.set(hx - 4, hy, "g1", "beak"); L.set(hx - 1, hy - 1, p.blink ? "n3" : "ink", "eye"); if (p.blink) L.set(hx - 2, hy - 1, "n3", "eye"); } else { // head tipped down to the ground: comb faces forward, beak points down for (const [x, y, c] of [[hx - 2, hy - 2, "r2"], [hx - 3, hy - 1, "r2"], [hx - 1, hy - 3, "r1"], [hx - 3, hy - 2, "r1"], [hx, hy - 3, "r0"]]) L.set(x, y, c, "comb"); L.set(hx - 2, hy + 2, "g2", "beak"); L.set(hx - 2, hy + 3, "g1", "beak"); L.set(hx, hy, "ink", "eye"); }}function henAt(L, extra) { hen(L, Object.assign({ bx: BX, by: BY, peck: 0, hd: 0, tail: 0, fluff: 0, lift: 0, blink: 0, sit: false }, extra));}// second hen peeking out of the window (head only, facing right, clipped by the window frame)function windowHen(L, rise) { const hx = 31, hy = 30 - rise; disc(L, hx + 0.5, hy + 0.5, 2.3, (x, y, nx, ny) => (nx + ny < -0.2 ? "n5" : nx + ny < 0.7 ? "n4" : "n3")); for (const [x, y, c] of [[hx, hy - 3, "r2"], [hx + 1, hy - 3, "r1"], [hx - 1, hy - 3, "r1"], [hx - 1, hy - 4, "r2"], [hx + 1, hy - 4, "r1"]]) L.set(x, y, c, "comb"); L.set(hx + 3, hy, "g2", "beak"); L.set(hx + 1, hy - 1, "ink", "eye"); L.set(hx + 2, hy + 2, "r1", "comb");}// ---- poses per variant --------------------------------------------------------------------------------------------const PECK = [0, 0, 0.5, 1, 1, 0.5, 0, 0, 0, 0.5, 1, 1, 0.5, 0, 0, 0];const HD = [0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0];const TAIL = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0];const Coop = sprite(pal, (g, t, mode) => { t = ((t % 1) + 1) % 1; const base = Layer("coop"); coop(base); contactShadow(g, 24, 42, 15, 2); contactShadow(g, 9, 44, 4, 1); if (mode !== "roost") contactShadow(g, 33, 45, 7, 1); g.outlined("ink", () => emitLayer(g, base, false)); const lv = Layer("leaves"); leaves(lv); emitLayer(g, lv, false); const rp = Layer("ramp"); ramp(rp); emitLayer(g, rp, true); g.detached("grain", () => { for (const [x, y, k] of [[20, 44, "g2"], [23, 46, "g3"], [25, 44, "g2"], [22, 42, "g1"], [18, 46, "g2"], [27, 46, "g2"], [15, 45, "g3"], [26, 42, "g3"], [12, 46, "g2"], [29, 46, "g1"]]) g.pixel(x, y, k); }); if (mode !== "roost") { const ns = Layer("nest"); nest(ns); emitLayer(g, ns, true); } if (mode === "roost") { const f = Math.round(t * N_ROOST) % N_ROOST; const wl = Layer("henWindow", inWin); const rise = [0, 0, 0, 1, 3, 4, 4, 4, 4, 4, 4, 3, 1, 0, 0, 0][f]; if (rise > 0) { windowHen(wl, rise); emitLayer(g, wl, false); } const hl = Layer("hen"); hen(hl, { bx: 18, by: 33, peck: 0, hd: 0, tail: 0, fluff: 0, lift: 0, sit: true, br: 0.5 - 0.5 * Math.cos(TAU * t * 2), blink: f === 9 || f === 10 ? 1 : 0 }); emitLayer(g, hl, true); return; } const hl = Layer("hen"); if (mode === "egg") { const f = Math.min(N_EGG - 1, Math.round(t * N_EGG)); const P = [[0, 0, 0], [0.6, 0, 0], [1, 2, -1], [0.8, 5, -1], [0.5, 5, 0], [0.2, 4, 0], [0, 2, 0], [0, 0, 0]][f]; if (f >= 3 && f <= 6) { const el = Layer("egg"); egg(el); emitLayer(g, el, true); const lp = Layer("nestLip"); nestLip(lp); emitLayer(g, lp, false); } henAt(hl, { fluff: P[0], lift: P[1], hd: P[2], tail: f >= 2 && f <= 4 ? 1 : 0 }); } else { const f = Math.round(t * N_IDLE) % N_IDLE; henAt(hl, { peck: PECK[f], hd: HD[f], tail: TAIL[f] }); } emitLayer(g, hl, true);});submit(Coop, { size: [W, H], origin: "top-left", kind: "icon", scale: 1, animate: { fps: 10, duration: N_IDLE / 10, loop: true }, anchor: { x: CX, y: GROUND }, args: ["idle"], variants: [ { name: "idle", args: ["idle"] }, { name: "roost", args: ["roost"], animate: { fps: 10, duration: N_ROOST / 10, loop: true } }, { name: "egg", args: ["egg"], animate: { fps: 10, duration: N_EGG / 10, loop: false } }, ],});掲載されたコードでは、ビルドスクリプトが自動付与する共通部分を省略しています。具体的には 1 行のランタイムヘッダー、共有パレット、および汎用ヘルパー関数(contactShadow、litIndex、flame、hash、chk、TAU)です。表示されている行番号は実際のファイルと一致しています。これらのプログラムは AIエージェントが生成し、描画されたコマの仕上がりを確認しながら修正を加えたものです。
エージェントに、こうしたプログラムを書かせる。
Tessarune は描画 API のリファレンス、25 冊の制作マニュアル、結果を確かめるための描画エンジンをエージェントに渡します。