|
24 | 24 | <tbody> |
25 | 25 | <tr> |
26 | 26 | <td>🔮 Simple, drop-in API</td> |
27 | | -<td>✨ Supports PixiJS v6, v7, v8+</td> |
| 27 | +<td>✨ Supports PixiJS 8, 7 and 6+</td> |
28 | 28 | </tr> |
29 | 29 | <tr> |
30 | 30 | <td>✅ Plugin compatible</td> |
|
37 | 37 | </tbody> |
38 | 38 | </table> |
39 | 39 |
|
40 | | -## Sample Usage |
| 40 | +## Usage |
41 | 41 |
|
42 | 42 | ```ts |
43 | | -function update() { |
44 | | - spriteA.x += 10 |
45 | | - spriteB.rotation -= Math.PI * 0.125 |
46 | | -} |
47 | | - |
48 | | -const loop = new InterpolatedTicker({ app, update }) |
49 | | - |
50 | | -// run @ 24Hz: |
51 | | -loop.updateIntervalMs = 1000 / 24 |
52 | | -loop.start() |
| 43 | +const ticker = new InterpolatedTicker({ renderer, stage }); |
| 44 | +const mySprite = new Sprite({ texture: "cat" }); |
| 45 | + |
| 46 | +ticker.start({ |
| 47 | + // triggered at a fixed timestep |
| 48 | + update: (fixedDeltaMs: number) |
| 49 | + { |
| 50 | + mySprite.position.x += 10; |
| 51 | + mySprite.rotation += 0.1; |
| 52 | + } |
| 53 | +}); |
53 | 54 | ``` |
54 | 55 |
|
55 | | -## Getting Started |
56 | | - |
57 | | -### 💿 Install |
| 56 | +## 💿 Install |
58 | 57 |
|
59 | 58 | ```sh |
60 | 59 | npm i pixijs-interpolated-ticker |
61 | 60 | ``` |
62 | 61 |
|
63 | | -### Overview |
64 | | - |
65 | | -- The **update()** function records **keyframes** |
66 | | -- The ticker renders frames to the framebuffer using interpolated values for `x`, `y`, `scale`, `rotation`, and `alpha` |
67 | | - |
68 | | -## Advanced Configuration |
69 | | - |
70 | | -### Ticker Options |
71 | | - |
72 | | -*Configuring your interpolation ticker.* |
| 62 | +## Configuration |
73 | 63 |
|
74 | 64 | ```ts |
75 | | -const mainLoop = new InterpolationTicker({ |
76 | | - app: myApplication, |
77 | | - |
78 | | - // the update loop function, used as keyframes |
79 | | - update: () => {}, |
80 | | - |
81 | | - // enable/disable frame interpolation (default = true) |
82 | | - interpolation: true, |
83 | | - |
84 | | - // how frequently to trigger update loop (default = 1000/60) |
85 | | - updateIntervalMs: 1000/30, |
86 | | - |
87 | | - // set a maximum render FPS, -1 is unlimited (default = -1) |
88 | | - maxRenderFPS: 60, |
89 | | - |
90 | | - // maximum change in alpha to animate (default = 0.5): |
91 | | - autoLimitAlpha: 0.1, |
92 | | - |
93 | | - // maximum change in x or y to animate (default = 100): |
94 | | - autoLimitPosition: 250, |
95 | | - |
96 | | - // maximum change in rotation to animate (default = Math.PI / 4): |
97 | | - autoLimitRotation: Math.PI, |
98 | | - |
99 | | - // maximum change in scale to animate (default = 1.0): |
100 | | - autoLimitScale: 1.5, |
101 | | - |
102 | | - // hook triggered at the start of a render, immediately |
103 | | - // following any update frames that have been processed. |
104 | | - // containers' values are their latest keyframe values. |
105 | | - preRender: ( deltaTimeMs ) => {}, |
106 | | - |
107 | | - // hook triggered during a render, immediately before |
108 | | - // writing to the framebuffer. containers' values are |
109 | | - // their interpolated values. |
110 | | - onRender: ( deltaTimeMs ) => {}, |
111 | | - |
112 | | - // hook triggered at the end of a render. containers' |
113 | | - // values are their latest keyframe values. |
114 | | - postRender: ( deltaTimeMs ) => {}, |
115 | | - |
116 | | - // hook triggered at the start of each evaluation cycle, before |
117 | | - // any update or render frames are processed. |
118 | | - evalStart: ( startTime ) => {}, |
119 | | - |
120 | | - // hook triggered at the end of each evaluation cycle, after all |
121 | | - // update and render frames have been processed. |
122 | | - evalEnd = ( startTime ) => {}, |
123 | | - |
124 | | - // initial number of containers to reserve memory in the internal |
125 | | - // buffer for. note: the internal buffer will resize automatically |
126 | | - // when it is full. (default = 500): |
127 | | - initialCapacity: 2500, |
128 | | -}) |
| 65 | +const ticker = new InterpolatedTicker({ renderer, stage }); |
| 66 | +const mySprite = new Sprite({ texture: "cat" }); |
| 67 | + |
| 68 | +ticker.start({ |
| 69 | + update: (fixedDeltaMS: number) |
| 70 | + { |
| 71 | + // triggered at a fixed interval |
| 72 | + // fixedDeltaMS never changes |
| 73 | + }, |
| 74 | + |
| 75 | + render: (renderDeltaMs: number, progress: number) |
| 76 | + { |
| 77 | + // triggered at display refresh rate |
| 78 | + // e.g. drawing additional particle effects, etc. |
| 79 | + }, |
| 80 | + |
| 81 | + renderPrepare: (renderDeltaMS: number) |
| 82 | + { |
| 83 | + // triggered at display refresh rate |
| 84 | + // prior to container interpolation |
| 85 | + }, |
| 86 | +}); |
| 87 | + |
| 88 | +// increasing the speed affects the rate at which update(fixedDeltaMS) is |
| 89 | +// executed, but does not affect the value of fixedDeltaMS. |
| 90 | +ticker.speed = 2; |
| 91 | + |
| 92 | +// limit render FPS |
| 93 | +ticker.renderFPS = 30; |
| 94 | + |
| 95 | +ticker.on("fps", (fps) => |
| 96 | +{ |
| 97 | + // render FPS updated |
| 98 | +}); |
| 99 | + |
| 100 | +ticker.on("devicefps", (fps) => |
| 101 | +{ |
| 102 | + // device FPS updated (independent of actual renders) |
| 103 | +}); |
129 | 104 | ``` |
130 | 105 |
|
131 | | -and other additional non-constructor values: |
| 106 | +### Ticker Options |
132 | 107 |
|
133 | 108 | ```ts |
134 | | -const mainLoop = new InterpolatedTicker({ app }) |
135 | | - |
136 | | -// run the update loop at 125% speed: |
137 | | -mainLoop.speed = 1.25 |
138 | | - |
139 | | -// restrict render skips - if rendering is interrupted for any |
140 | | -// reason - e.g. the window loses focus - then this will |
141 | | -// limit the maximum number of "catch-up" frames (default = 10): |
142 | | -mainLoop.maxUpdatesPerRender = 10 |
143 | | - |
144 | | -// set custom opt-in or opt-out logic for container interpolation. |
145 | | -// when `container.interpolation` is not set yet, this function is |
146 | | -// evaluated once to hydrate that property. |
147 | | -// |
148 | | -// you could set this to () => false to opt-out by default, and then |
149 | | -// manually set container.interpolation = true in the containers you |
150 | | -// want to interpolate. |
151 | | -// |
152 | | -// (default: () => true) |
153 | | -mainLoop.getDefaultInterpolation = ( container ): boolean => { |
154 | | - return !(container instanceof Mesh) |
155 | | -} |
| 109 | +new InterpolatedTicker({ |
| 110 | + /** |
| 111 | + * PixiJS renderer. |
| 112 | + */ |
| 113 | + renderer: Renderer; |
| 114 | + |
| 115 | + /** |
| 116 | + * Stage root view. |
| 117 | + */ |
| 118 | + stage: Container; |
| 119 | + |
| 120 | + /** |
| 121 | + * Fixed timestep interval in milliseconds. |
| 122 | + * |
| 123 | + * @default 16.666666666666668 |
| 124 | + */ |
| 125 | + fixedDeltaMs?: number; |
| 126 | + |
| 127 | + /** |
| 128 | + * Whether container interpolation is enabled. |
| 129 | + * |
| 130 | + * When enabled, container values (position, scale, rotation, alpha) are |
| 131 | + * rendered at interpolated positions. |
| 132 | + * |
| 133 | + * @default true |
| 134 | + */ |
| 135 | + interpolation?: boolean; |
| 136 | + |
| 137 | + /** |
| 138 | + * Container interpolation options (when enabled). |
| 139 | + * |
| 140 | + * @default undefined |
| 141 | + */ |
| 142 | + interpolationOptions?: { |
| 143 | + /** |
| 144 | + * Maximum interpolatable change in position x/y. |
| 145 | + * |
| 146 | + * @default 100 |
| 147 | + */ |
| 148 | + maxDeltaPosition?: number; |
| 149 | + |
| 150 | + /** |
| 151 | + * Maximum interpolatable change in scale. |
| 152 | + * |
| 153 | + * @default 1 |
| 154 | + */ |
| 155 | + maxDeltaScale?: number; |
| 156 | + |
| 157 | + /** |
| 158 | + * Maximum interpolatable change in rotation. |
| 159 | + * |
| 160 | + * @default Math.PI/2 |
| 161 | + */ |
| 162 | + maxDeltaRotation?: number; |
| 163 | + |
| 164 | + /** |
| 165 | + * Maximum interpolatable change in alpha. |
| 166 | + * |
| 167 | + * @default 0.5 |
| 168 | + */ |
| 169 | + maxDeltaAlpha?: number; |
| 170 | + |
| 171 | + /** |
| 172 | + * Initial number of containers to preallocate interpolation memory for. |
| 173 | + * |
| 174 | + * @default 256 |
| 175 | + */ |
| 176 | + capacity?: number; |
| 177 | + |
| 178 | + /** |
| 179 | + * Maximum number of containers to allocate interpolation memory for. |
| 180 | + * |
| 181 | + * @default 4096 |
| 182 | + */ |
| 183 | + maxCapacity?: number; |
| 184 | + }, |
| 185 | + |
| 186 | + /** |
| 187 | + * The display refresh rate to target (in frames per second). Actual render |
| 188 | + * rate will vary on different displays. |
| 189 | + * |
| 190 | + * A value of `0` means unlimited refresh rate. |
| 191 | + * |
| 192 | + * @default 0 |
| 193 | + */ |
| 194 | + renderFPS?: number; |
| 195 | + |
| 196 | + /** |
| 197 | + * When `renderFPS` set, this is the maximum tolerance in milliseconds for |
| 198 | + * limiting the render frame interval. |
| 199 | + * |
| 200 | + * @default 7.0 |
| 201 | + */ |
| 202 | + renderIntervalToleranceMS?: number; |
| 203 | + |
| 204 | + /** |
| 205 | + * Maximum frame time in milliseconds that fixed updates may accrue for |
| 206 | + * before frame time stops accruing. Scaled by `speed`. |
| 207 | + * |
| 208 | + * @default fixedDeltaMs*3 |
| 209 | + */ |
| 210 | + maxFrameTimeMs?: number; |
| 211 | + |
| 212 | + /** |
| 213 | + * The minimum interval in milliseconds that fluctuations in FPS are reported. |
| 214 | + * |
| 215 | + * Listen for "fps" and "devicefps" events. |
| 216 | + * |
| 217 | + * @default 1000 |
| 218 | + */ |
| 219 | + fpsIntervalMs?: number; |
| 220 | + |
| 221 | + /** |
| 222 | + * The rounding level for FPS detection (e.g. 0.01). |
| 223 | + * |
| 224 | + * @default 1 |
| 225 | + */ |
| 226 | + fpsPrecision?: number; |
| 227 | +}) |
156 | 228 | ``` |
157 | 229 |
|
158 | 230 | ### Container Options |
159 | 231 |
|
160 | 232 | *Configuring individual containers.* |
161 | 233 |
|
162 | | -Containers are granted _optional_ properties to make it easy to configure advanced interpolation. |
163 | | - |
164 | | -Interpolation is opt-out for stage containers, and disabling interpolation for a container will also disable it for all descendants. |
165 | | - |
166 | 234 | | Property | Description | |
167 | 235 | | :----- | :------ | |
168 | | -| `interpolation` | Whether interpolation is explicitly enabled or disabled for this container. The default behavior for all containers is `true`. | |
169 | | -| `interpolatedChildren` | An array of child containers to include in interpolation. When not set, `children` is used. | |
170 | | -| `interpolationWraparound` | If set, position will smoothly wraparound the given ranges. | |
| 236 | +| `isInterpolated` | Set `false` to disable interpolation on this container. Defaults to `this.visible`. | |
| 237 | +| `hasInterpolatedChildren` | Set `false` to disable interpolation on this container's descendants. Defaults to `this.isInterpolated`. | |
171 | 238 |
|
172 | 239 | ```ts |
173 | | -// disable interpolation for a container |
174 | | -// (and all of its descendants): |
175 | | -const sprite = new Sprite() |
176 | | -sprite.interpolation = false |
177 | | - |
178 | | -// allow a container's position to wraparound smoothly: |
179 | | -const background = new Sprite() |
180 | | -background.interpolationWraparound = { |
181 | | - xRange: 1000, |
182 | | - yRange: 2000 |
183 | | -} |
184 | | - |
185 | | -// explicitly set which children may be interpolated |
186 | | -const parent = new Container() |
187 | | -const childA = new Container() |
188 | | -const childB = new Container() |
189 | | -parent.addChild( childA, childB ) |
190 | | -parent.interpolatedChildren = [ childB ] |
| 240 | +const container = new Container(); |
| 241 | + |
| 242 | +// skip interpolation on this container and its children |
| 243 | +container.isInterpolated = false; |
| 244 | + |
| 245 | +// actually ...lets allow children to be interpolated anyway |
| 246 | +container.hasInterpolatedChildren = true; |
191 | 247 | ``` |
192 | 248 |
|
193 | | -## Credits |
| 249 | +## Credit |
194 | 250 |
|
195 | | -PixiJS InterpolatedTicker is a spiritual successor to [kittykatattack/smoothie](https://github.com/kittykatattack/smoothie). |
| 251 | +PixiJS InterpolatedTicker is an implementation of the ideas laid out in |
| 252 | +[Gafferongames' Fix Your Timestep article](https://gafferongames.com/post/fix_your_timestep/), |
| 253 | +and is a spiritual successor to [kittykatattack/smoothie](https://github.com/kittykatattack/smoothie). |
0 commit comments