Build your own easing curve and use it on keys and Lerp (#247)
game-maker / gate (push) Successful in 3m44s
game-maker / suite (push) Successful in 10m25s

The built-in eases cover the usual shapes, but a game often wants its
own: a coin that bounces twice, a door that overshoots and settles.
A new asset type holds such a curve, and keys and the Lerp node can
pick it instead of a built-in ease.

- New asset type "curve": points like in Vector art, x is t and y is
  the value. A new curve is the straight line from (0, 0) to (1, 1).
- One pure evaluation in src/core/model/curve.ts serves the timeline
  preview, the running game and the Lerp node.
- The curve always has one value per t. The end points stay at t 0
  and 1, points cannot pass each other, and a handle that reaches past
  its neighbour is shortened along its direction. Loading a hand-edited
  file applies the same rules.
- Curve tab: click on the line to insert a point that keeps the shape,
  click on free space to put one there, drag points and handles,
  double-click or Delete removes, Alt-click toggles round and sharp.
  Every step is one undo step.
- Presets: all 30 built-in eases plus Linear, fitted with adaptive
  subdivision. Each stays within 0.4 % of the built-in ease.
- Preview: a dot runs along the curve once a second, a second dot
  shows only the value. The Preview button stops it.
- Animation keys get "Curve" under Easing with a curve picker. Lerp
  gets "Curve" under Ease, and its result may overshoot b.
- A missing or unpicked curve runs linear. The inspector says so, the
  node shows it as a problem, and deleting a used curve warns with the
  number of keys and Lerp nodes.

Tests:
- ./check.sh: 180 files, 2612 tests, CHECK EXIT 0
- GM_STEPS=1-6,25,53: ALL PASSED, 309 checks
- New e2e module 53 with 13 checks, all with the real mouse. Expected
  values come from its own Bezier evaluation.
- Counter-checks bite: without Bezier evaluation 4 unit tests fail,
  without handle clamping 2. In the browser, a key that cannot find its
  curve, a click that sets no point and a handle drag that moves the
  point all turn checks red.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-09-27 15:58:05 +02:00
1 parent 320d3e27e1
commit 393b8ec7f9
41 files changed
+1981 -67

No files matched your search

+27
View File
@@ -52,6 +52,19 @@ const machineWithStates = (states: { id: string; name: string }[]): GraphLookup
machine: (id) => (id === 'm' ? { name: 'M', states, start: '', transitions: [] } : null),
});
/** A lookup holding one curve "c" (#247): straight up to 1.5 at the middle, back down to 1 at the end. */
const overshootingCurve: GraphLookup = {
...NO_LOOKUP,
curve: (id) =>
id === 'c'
? [
{ x: 0, y: 0 },
{ x: 0.5, y: 1.5 },
{ x: 1, y: 1 },
]
: null,
};
/** A lookup holding one machine "m" with the given transitions – what `State transition` reads. */
const machineLookup = (transitions: { from: string; to: string; name?: string }[]): GraphLookup => ({
...NO_LOOKUP,
@@ -579,6 +592,20 @@ const CASES: Record<string, Case[]> = {
params: { ease: 'out' },
outputs: { result: 3 },
},
{
name: 'along a Curve asset, overshooting b',
inputs: { a: 0, b: 8, t: 0.5 },
params: { ease: 'curve', curve: 'c' },
lookup: overshootingCurve,
outputs: { result: 12 },
},
{
name: 'along a Curve asset that is gone: linear',
inputs: { a: 0, b: 8, t: 0.25 },
params: { ease: 'curve', curve: 'gone' },
lookup: overshootingCurve,
outputs: { result: 2 },
},
],
Floor: [
{ name: 'rounds down', inputs: { value: 2.9 }, outputs: { result: 2 } },