Skip to content

Commit 62e2134

Browse files
committed
Animations
1 parent 863897c commit 62e2134

4 files changed

Lines changed: 282 additions & 0 deletions

File tree

documentation/blender/animation.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ export class ScrollTimeline extends Behaviour {
244244

245245
## Next Steps
246246

247+
- **[Play Animations](/docs/how-to-guides/animation)** - Cross-editor guide: triggering from UI, controlling from code, and building animations/timelines at runtime
247248
- **[Components Overview](/docs/blender/components)** - Learn about other interactive components
248249
- **[Custom Components](/docs/how-to-guides/scripting/create-components)** - Write code to trigger animations
249250
- **[Deployment](/docs/how-to-guides/deployment/)** - Publish your animated scenes
Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
---
2+
title: Play Animations
3+
description: Play and control animations in Needle Engine — with the Animation component, Animator, or Timeline, authored in Unity/Blender without code or driven from TypeScript.
4+
---
5+
6+
# Play Animations
7+
8+
Needle Engine has several ways to play animations, and **most of them need no code** — you author them in Unity or Blender and they just work on the web. Pick the approach that matches your content, then trigger it from the editor, from UI, or from code.
9+
10+
## Which approach should I use?
11+
12+
| Approach | Best for | No-code in editor? | Trigger from UI? |
13+
| --- | --- | --- | --- |
14+
| **`Animation` component** | A single clip, or a few clips you start one at a time | Yes | Yes (Button → `play`) |
15+
| **`Animator` / `AnimatorController`** | Multiple states (idle → walk → run, or step 1 → 2 → 3) with conditions, blending, and chained playback | Yes | Yes (set parameters) |
16+
| **Timeline (`PlayableDirector`)** | A **sequence** of clips that play one after another, with **synced audio**, camera moves, activation, and other tracks | Yes | Yes (Button → `play`) |
17+
| **Everywhere Actions (`PlayAnimationOnClick`)** | No-code click-to-play that also works in AR / USDZ on iOS | Yes | Click on object |
18+
| **Code (`animation.play()`, builders)** | Fully custom or runtime-generated sequences || Yes (call from code) |
19+
20+
For most "play this, then that" needs — especially with audio — reach for **Timeline** before writing your own sequencing code.
21+
22+
---
23+
24+
## Author animations in the editor (no code)
25+
26+
All three core animation components are exported from Unity and Blender. You set them up in the editor and they run on the web unchanged — no TypeScript required.
27+
28+
### Animation component
29+
30+
For basic playback of one or a few clips.
31+
32+
1. Add an `Animation` component to your animated object.
33+
2. Assign your animation clip(s). Add more to the `clips` array if needed (by default only the first plays automatically).
34+
3. Enable `playAutomatically` to start on load, or leave it off and trigger later.
35+
36+
### Animator + AnimatorController
37+
38+
For multiple states with transitions, conditions, and blending — characters (idle/walk/run) or step-by-step sequences.
39+
40+
1. Create an `AnimatorController` asset and add states for each animation.
41+
2. Add parameters (bool, float, int, trigger) and connect states with transitions and conditions.
42+
3. Add an `Animator` component to your object and assign the controller.
43+
44+
In Unity you author this in the Animator window; in Blender, via the AnimatorController editor. See [Animation Workflows in Blender](/docs/blender/animation#animatorcontroller-state-machine-animations) for a Blender walkthrough.
45+
46+
### Timeline (PlayableDirector)
47+
48+
For coordinated, choreographed sequences — multiple clips one after another, audio tracks, camera moves, and object activation, all on one shared timeline.
49+
50+
- **Unity:** Build a Timeline asset and add a `PlayableDirector` component referencing it. Needle Engine translates [Unity's Timeline](https://unity.com/features/timeline) into a web-ready format, including animation and audio tracks.
51+
- **Blender:** Author NLA tracks, then add a `PlayableDirector` component and list the objects whose NLA tracks should be exported. See [Animation Workflows → PlayableDirector](/docs/blender/animation#playabledirector-timeline-animation).
52+
53+
Leave `playOnAwake` off if you want the timeline to start only when triggered.
54+
55+
---
56+
57+
## Trigger animations from UI without code
58+
59+
You don't need a script to start an animation from a button:
60+
61+
- **`Button` component (`onClick`):** Wire the button's `onClick` event to a target component and method — for example call `play` on a `PlayableDirector`, or set a parameter on an `Animator`. This works for 3D and UI buttons.
62+
- **Everywhere Actions — `PlayAnimationOnClick`:** Select an animation state from an `Animator` to play when the object is clicked, with an optional follow-up state. It works in the browser, in WebXR, **and** in interactive USDZ/QuickLook on iOS. See [Everywhere Actions](/docs/how-to-guides/everywhere-actions/).
63+
64+
---
65+
66+
## Play a sequence of animations with sound on a button press
67+
68+
This is a common request: *play several animations automatically one after another, with sound, starting when a button is pressed, and stop only once the whole sequence has finished.* The cleanest solution is a **Timeline** driven by a `PlayableDirector` — you generally don't need to write your own sequencing controller.
69+
70+
A Timeline lets you arrange animation clips and audio clips on tracks along one shared timeline. Because the order, timing, and audio are baked into the timeline, playing the whole sequence is a single call:
71+
72+
```ts
73+
import { Behaviour, PlayableDirector, serializable } from "@needle-tools/engine";
74+
75+
export class PlaySequenceButton extends Behaviour {
76+
@serializable(PlayableDirector)
77+
timeline?: PlayableDirector;
78+
79+
// Hook this up to a Button component's onClick in Unity/Blender,
80+
// or call it from your own HTML button.
81+
async playSequence() {
82+
if (!this.timeline || this.timeline.isPlaying) return;
83+
await this.timeline.play(); // resolves when the whole timeline finishes
84+
}
85+
}
86+
```
87+
88+
Why Timeline fits this use case:
89+
90+
- **Animations play in order automatically** — place clips on the track in sequence; no manual "play next when finished" chaining.
91+
- **Audio is part of the timeline** — add an audio track and drop your sound clips where they should play. They stay in sync with the animations. Enable `waitForAudio` on the `PlayableDirector` if you want playback to wait for audio to load.
92+
- **Triggered from UI** — wire a `Button` component's `onClick` to the director's `play` (no code), or call `play()` from a custom component as shown above. Leave `playOnAwake` disabled so it only starts on the button press.
93+
- **Runs until complete**`play()` returns a promise that resolves when the timeline reaches its end, so you can disable the button, show a "finished" state, or chain follow-up logic. Use `pause()` / `stop()` to interrupt.
94+
95+
A **state machine** is another good fit when the sequence is really "states that follow each other": use an `AnimatorController` with one state per animation and transitions between them. Each state can automatically advance to the next, and you can drive it from UI by setting parameters.
96+
97+
If you genuinely need custom logic between steps, you can still sequence the `Animation` component from code — `animation.play(clip)` returns a promise that resolves when that clip finishes, so you can `await` each one in turn. But for a fixed sequence with audio, Timeline is less code and easier to author for non-programmers.
98+
99+
---
100+
101+
## Control animations from code
102+
103+
When you do want code, every component exposes a runtime API.
104+
105+
**Animation component**`play()` returns a promise that resolves when the clip finishes (it won't resolve while looping):
106+
107+
```ts
108+
// Play clips one after another (clipA, clipB are AnimationClips)
109+
await animation.play(clipA, { exclusive: true });
110+
await animation.play(clipB, { exclusive: true });
111+
```
112+
113+
**Animator** — set parameters or jump to a state:
114+
115+
```ts
116+
animator.setBool("isWalking", true);
117+
animator.setFloat("speed", 2.5);
118+
animator.setTrigger("jump");
119+
animator.play("Idle"); // play a state directly
120+
```
121+
122+
**PlayableDirector** — control timeline playback, or scrub it manually:
123+
124+
```ts
125+
timeline.play();
126+
timeline.pause();
127+
timeline.stop();
128+
129+
// Manual scrubbing (e.g. scroll-driven storytelling)
130+
timeline.time = 5.0; // seconds
131+
timeline.evaluate(); // apply the new time while paused
132+
```
133+
134+
For scroll-driven playback, the [`ScrollFollow`](/docs/how-to-guides/components/scroll-follow) component binds scroll position to a `PlayableDirector` without custom code.
135+
136+
---
137+
138+
## Create animations, timelines, and controllers from code
139+
140+
Needle Engine ships fluent **builder** utilities so you can construct animation clips, timelines, and `AnimatorController` state machines at runtime — no editor asset required. These are available as a **preview** in **Needle Engine 5.1+**: `TimelineBuilder`, `AnimatorControllerBuilder` (via `AnimatorController.build()`), and `AnimationBuilder`. They're ideal for procedural content, runtime-generated sequences, and tooling.
141+
142+
### Build a timeline (animations + audio) with `TimelineBuilder`
143+
144+
The most direct way to recreate the "several animations one after another, with synced sound, triggered by a button" use case entirely in code. Clips added to a track are placed back-to-back automatically (each `.clip()` advances a cursor), and an audio track plays alongside:
145+
146+
```ts
147+
import { Behaviour, serializable, TimelineBuilder, PlayableDirector } from "@needle-tools/engine";
148+
import { AnimationClip } from "three";
149+
150+
export class SequenceFromCode extends Behaviour {
151+
@serializable(AnimationClip)
152+
clips: AnimationClip[] = [];
153+
154+
private director?: PlayableDirector;
155+
156+
start() {
157+
// A PlayableDirector hosts the timeline we build
158+
this.director = this.gameObject.getComponent(PlayableDirector)
159+
?? this.gameObject.addComponent(PlayableDirector);
160+
161+
TimelineBuilder.create("Sequence")
162+
.animationTrack("Animation", this.gameObject)
163+
.clip(this.clips[0], { easeIn: 0.2 })
164+
.clip(this.clips[1]) // starts right after the previous clip
165+
.clip(this.clips[2])
166+
.audioTrack("Audio", this.gameObject)
167+
.clip("audio/intro.mp3", { start: 0, duration: 3, volume: 0.8 })
168+
.clip("audio/outro.mp3", { start: 3, duration: 2 })
169+
.signalTrack("Events")
170+
.signal(5, () => console.log("sequence reached 5s"))
171+
.install(this.director); // assigns the timeline; use .build() if you have no signal callbacks
172+
}
173+
174+
// Call from a UI Button's onClick:
175+
async playSequence() {
176+
if (this.director && !this.director.isPlaying)
177+
await this.director.play(); // resolves when the whole timeline finishes
178+
}
179+
}
180+
```
181+
182+
`TimelineBuilder` supports `animationTrack`, `audioTrack`, `activationTrack` (show/hide objects), `controlTrack` (nested timelines), and `signalTrack`/`markerTrack` (fire callbacks at a time). Use `.build()` to get the timeline asset and assign it to `director.playableAsset` yourself, or `.install(director)` to assign it **and** wire up any `.signal()` callbacks.
183+
184+
### State machines with `AnimatorController`
185+
186+
If the sequence is better modeled as states that follow each other, build an `AnimatorController` from code instead.
187+
188+
**Chain clips into a sequence with `createFromClips`.** This turns each clip into a state and auto-transitions to the next — the quickest way to play several clips one after another:
189+
190+
```ts
191+
import { AnimatorController, Animator } from "@needle-tools/engine";
192+
193+
// Each clip becomes a state; autoTransition advances to the next when finished
194+
const controller = AnimatorController.createFromClips(myClips, {
195+
autoTransition: true,
196+
looping: false,
197+
});
198+
199+
const animator = this.gameObject.getComponent(Animator) ?? this.gameObject.addComponent(Animator);
200+
animator.runtimeAnimatorController = controller;
201+
```
202+
203+
::: tip
204+
With `autoTransition: true`, the **last** clip transitions back to the first, so the whole sequence loops. For a sequence that stops at the end, use the fluent builder below and leave the final state without an outgoing transition (or use a Timeline).
205+
:::
206+
207+
**Build a full state machine fluently with `AnimatorController.build()`.** Add parameters, states, transitions, and conditions — ideal when the sequence should be driven by UI:
208+
209+
```ts
210+
import { AnimatorController, Animator } from "@needle-tools/engine";
211+
212+
const controller = AnimatorController.build("Sequence")
213+
.triggerParameter("Play")
214+
.state("Idle", { clip: idleClip, loop: true })
215+
.state("Step1", { clip: step1Clip })
216+
.state("Step2", { clip: step2Clip })
217+
// Start the sequence when the "Play" trigger is set (e.g. from a button)
218+
.transition("Idle", "Step1", { duration: 0.1 })
219+
.condition("Play")
220+
// Advance automatically once each step finishes (exitTime = normalized 0-1)
221+
.transition("Step1", "Step2", { exitTime: 1, duration: 0.1 })
222+
.transition("Step2", "Idle", { exitTime: 1, duration: 0.1 })
223+
.build();
224+
225+
const animator = this.gameObject.getComponent(Animator) ?? this.gameObject.addComponent(Animator);
226+
animator.runtimeAnimatorController = controller;
227+
228+
// Trigger the sequence from a UI Button's onClick, or from code:
229+
animator.setTrigger("Play");
230+
```
231+
232+
You can also set parameters with `animator.setBool(name, value)` / `setFloat(name, value)`, or jump straight to a state with `animator.play("Step1")`.
233+
234+
### Build standalone clips with `AnimationBuilder`
235+
236+
States and timeline tracks don't even need pre-existing clips — author keyframes inline with `.track()` on either builder, or build a standalone clip:
237+
238+
```ts
239+
import { AnimationBuilder } from "@needle-tools/engine";
240+
241+
const slideClip = AnimationBuilder.create("Slide")
242+
.track(this.gameObject, "position", { from: [0, 0, 0], to: [2, 0, 0], duration: 1 })
243+
.build();
244+
```
245+
246+
`AnimationBuilder.track()` supports position/scale/quaternion/rotation/visibility on objects, plus material, light, and camera properties — with either a keyframe array or a `{ from, to, duration }` tween shorthand.
247+
248+
---
249+
250+
## Related
251+
252+
- [Animation Workflows in Blender](/docs/blender/animation) — editor walkthroughs for Animation, AnimatorController, and Timeline (NLA)
253+
- [Scripting Examples → Animation](/docs/reference/scripting-examples#animation) — more code snippets
254+
- [Everywhere Actions](/docs/how-to-guides/everywhere-actions/) — no-code, cross-platform interactions including iOS
255+
- [Scroll Follow](/docs/how-to-guides/components/scroll-follow) — scroll-driven timeline playback
256+
- [FAQ](/docs/reference/faq#animation) — common animation questions

documentation/how-to-guides/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ Head to [Getting Started](/docs/getting-started/) to install Needle Engine for [
4040

4141
## Scripting & Interaction
4242

43+
- [Play Animations](/docs/how-to-guides/animation) - Animation component, Animator, and Timeline — authored in the editor (no code) or driven from TypeScript
4344
- [Create Components](/docs/how-to-guides/scripting/create-components) - Write custom TypeScript components
4445
- [Use Lifecycle Hooks](/docs/how-to-guides/scripting/use-lifecycle-hooks) - awake, start, update, and more
4546
- [Use Coroutines](/docs/how-to-guides/scripting/use-coroutines) - Time-based sequences and delays

documentation/reference/faq.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,30 @@ If postprocessing in Blender does not work as expected, try the following:
691691
We're actively working on improving postprocessing for Blender. Future updates will bring more effects and remove the requirement to place them on the same object.
692692
:::
693693

694+
# Animation
695+
696+
## How can I play animations in Needle Engine?
697+
698+
Needle Engine has several ways to play animations, and most of them need **little or no code** — you author them in Unity or Blender and they run on the web. From simplest to most powerful:
699+
700+
| Approach | Best for | No-code in editor? | Trigger from UI? |
701+
| --- | --- | --- | --- |
702+
| **`Animation` component** | A single clip, or a few clips you start one at a time | Yes | Yes (Button → `play`) |
703+
| **`Animator` / `AnimatorController`** | Multiple states (idle → walk → run, or step 1 → 2 → 3) with conditions, blending, and chained playback | Yes | Yes (set parameters) |
704+
| **Timeline (`PlayableDirector`)** | A **sequence** of clips that play one after another, with **synced audio**, camera moves, and other tracks | Yes | Yes (Button → `play`) |
705+
| **Everywhere Actions (`PlayAnimationOnClick`)** | No-code click-to-play that also works in AR / USDZ on iOS | Yes | Click on object |
706+
| **Code (`animation.play()`, builders)** | Fully custom or runtime-generated sequences || Yes (call from code) |
707+
708+
For most "play this, then that" needs — especially with audio — reach for **Timeline** before writing your own sequencing code.
709+
710+
**See the full guide: [Play Animations](/docs/how-to-guides/animation)** — editor (no-code) setup, triggering from UI, controlling from code, and building animations/timelines/controllers at runtime.
711+
712+
## How do I play multiple animations one after another, with sound, triggered by a button?
713+
714+
Use a **Timeline** driven by a **`PlayableDirector`** component. Arrange your animation clips and audio clips on tracks along one shared timeline (Unity Timeline or Blender NLA), leave `playOnAwake` off, and start the whole sequence with a single `director.play()` — wired to a `Button`'s `onClick` (no code) or called from a component. `play()` returns a promise that resolves once the entire sequence finishes. You generally don't need to write your own sequencing controller.
715+
716+
For the full walkthrough — including the button script, building the sequence in code with `TimelineBuilder`, and the `AnimatorController` alternative — see [Play Animations → Play a sequence with sound on a button press](/docs/how-to-guides/animation#play-a-sequence-of-animations-with-sound-on-a-button-press).
717+
694718
# Performance
695719

696720
## My website becomes too large / is loading slow (too many MB)

0 commit comments

Comments
 (0)