Add a Lerp node that blends from a to b with an ease (#246)
game-maker / gate (push) Successful in 3m23s
game-maker / suite (push) Failing after 9m24s

Every animation in a behavior had to build its interpolation from
Subtract, Multiply and Add, once per axis. The gold pickup in
"Garden of Sacrifices" needed about 30 nodes for that alone. A single
Lerp node with an ease makes those chains one node each.

- Lerp has the inputs a (default 0), b (1) and t (0) and the output
  result. It sits in the data category next to Clamp.
- The Ease setting offers Linear, In, Out and In-out, all quadratic.
  The node row shows it as "Ease: Out".
- t is held in 0..1. A timer that overshoots in the last frame still
  lands exactly on b.

Tests:
- ./check.sh: 178 files, 2583 tests, CHECK EXIT 0
- 7 new cases in the node catalog test. Without the clamp on t,
  2 of them fail.
- e2e 6z-iii (2 checks): typing "lerp" in the palette adds a linear
  Lerp, and picking "Out" in the inspector reaches the node and its
  row. GM_STEPS=1-6: ALL PASSED, 267 checks.
- README check sum is 1459, computed, not counted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-09-27 14:49:22 +02:00
1 parent 3a3401f8ac
commit 320d3e27e1
5 files changed
+122 -2

No files matched your search

+53 -1
View File
@@ -1098,8 +1098,60 @@ export async function graph(t) {
await mutate(`
const p = __gm.state.project;
p.assets = p.assets.filter((a) => a.id !== 'spawny');
__gm.state.select({ kind: 'asset', assetId: 'mover' });
`);
await lerpNode();
await mutate(`__gm.state.select({ kind: 'asset', assetId: 'mover' });`);
await showTab('scene');
// 6z-iii. Lerp (#246): typed into the palette it arrives linear with a 0…1 range, and the ease
// picked in the inspector lands on the node and on its setting row.
async function lerpNode() {
await mutate(`
const p = __gm.state.project;
p.assets.push({
id: 'lerpy',
type: 'behavior',
name: 'Lerpy',
props: { enabled: true, runInEditor: false },
variables: [],
graph: { nodes: [], edges: [] },
});
__gm.state.select({ kind: 'asset', assetId: 'lerpy' });
`);
await showTab('graph');
const spot = await json('__gm.graph.emptySpot()');
await click(spot.x, spot.y, 'right');
await typeText('lerp');
await press('Enter');
const lerp = (await asset('lerpy')).graph.nodes[0];
const inputs = lerp ? await json(`__gm.graph.nodeInputs(${JSON.stringify(lerp.id)})`) : null;
const settings = lerp ? await json(`__gm.graph.nodeSettings(${JSON.stringify(lerp.id)})`) : null;
check(
'typing "lerp" into the palette adds a linear Lerp from 0 to 1',
lerp?.type === 'Lerp' &&
JSON.stringify(inputs) === JSON.stringify(['a 0', 'b 1', 't 0']) &&
JSON.stringify(settings) === JSON.stringify(['Ease: Linear']),
JSON.stringify({ lerp, inputs, settings }),
);
const easeField = await json(`__gm.inspectorField('Ease')`);
if (easeField) {
await click(easeField.x, easeField.y);
await clickText('Out');
}
const picked = lerp ? (await asset('lerpy')).graph.nodes.find((n) => n.id === lerp.id) : null;
const pickedRows = lerp ? await json(`__gm.graph.nodeSettings(${JSON.stringify(lerp.id)})`) : null;
check(
'picking "Out" in the inspector sets the node and its setting row',
picked?.params.ease === 'out' && JSON.stringify(pickedRows) === JSON.stringify(['Ease: Out']),
JSON.stringify({ easeField, params: picked?.params, pickedRows }),
);
await shot('22-lerp');
await mutate(`
const p = __gm.state.project;
p.assets = p.assets.filter((a) => a.id !== 'lerpy');
`);
}
}