Play an Animation on a Player's Character with Verse
Play an Animation Directly on a Player's Character
You want your player to take a bow, throw a punch, or strike a victory pose — on their own character, the moment something happens in your game. Until recently that was a fight: you'd spawn a hidden NPC, swap meshes, or fake it with an Animated Mesh device standing in for the player. The /Fortnite.com/Animation/PlayAnimation module (added in the recent 41.x update) makes it a straight line — get an animation controller from any fort_character and call Play.

The three-hop chain
Every use of this API is the same little chain: an agent gives you a character, the character gives you a controller, and the controller gives you an instance you can steer.
# The whole API in three hops: agent -> character -> controller -> instance.
if:
Character := Presser.GetFortCharacter[] # agent -> fort_character
Controller := Character.GetPlayAnimationController[] # -> play_animation_controller
then:
Instance := Controller.Play(MyAnim) # -> play_animation_instance
The two extension functions are both failable (<decides> in the digest), so you call them with brackets [] inside an if — GetFortCharacter[] (the player might not have a living character right now) and GetPlayAnimationController[]. The real signature, straight from Fortnite.digest.verse:
(InCharacter : fort_character).GetPlayAnimationController()<transacts><decides> : play_animation_controller
What you can animate
GetPlayAnimationController is an extension on fort_character — so it works on any character you can get one from: the player who pressed a button, every player in GetPlayers(), or an NPC you spawned. The clip itself is an animation_sequence asset (from /Verse.org/Assets) that you import in the editor and select on the device. You can't build one in code — its constructor is epic_internal.
A play / stop button device
This device plays an editor-chosen animation on whoever presses Play, and stops it with Stop. It is compile-verified in UEFN 5.8 — the whole project builds clean with it installed.
using { /Fortnite.com/Characters }
using { /Fortnite.com/Animation/PlayAnimation }
using { /Fortnite.com/Devices }
using { /Verse.org/Assets }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
play_emote_on_press_device := class(creative_device):
# You CANNOT construct an animation_sequence in Verse (its constructor is
# epic_internal). It is an editor-imported asset, so the slot is an OPTION:
# pick the asset in the device's Details panel, unwrap it with `EmoteAnim?`.
@editable
EmoteAnim : ?animation_sequence = false
@editable
PlayButton : button_device = button_device{}
@editable
StopButton : button_device = button_device{}
# Keep the latest instance so the Stop button can cancel it.
var CurrentAnim : ?play_animation_instance = false
OnBegin<override>()<suspends> : void =
PlayButton.InteractedWithEvent.Subscribe(OnPlayPressed)
StopButton.InteractedWithEvent.Subscribe(OnStopPressed)
OnPlayPressed(Presser : agent) : void =
# GetFortCharacter and GetPlayAnimationController both <decide>, so they
# share one `if` with the option unwrap -- all three must succeed.
if:
Anim := EmoteAnim?
Character := Presser.GetFortCharacter[]
Controller := Character.GetPlayAnimationController[]
then:
# Play() returns the instance IMMEDIATELY -- it does not suspend.
Instance := Controller.Play(
Anim,
?PlayRate := 1.0, # 1.0 = authored speed, 0.5 = slow-mo
?BlendInTime := 0.2, # ease in over 0.2s
?BlendOutTime := 0.2) # ease out over 0.2s
set CurrentAnim = option{Instance}
Print("Playing animation on the presser's character")
OnStopPressed(Presser : agent) : void =
if (Instance := CurrentAnim?):
Instance.Stop() # blends out and fires InterruptedEvent
Two details worth burning in:
- The editable is
?animation_sequence, notanimation_sequence. Because the constructor isepic_internal,animation_sequence{}does not compile (Invalid access of epic_internal class constructor). Declare the slot as an option defaulting tofalse, assign the asset in the Details panel, and unwrap withAnim?. This is the single most common mistake with this API. Play()does not suspend. It returns aplay_animation_instanceimmediately and the clip plays on its own. That's whyOnPlayPresseddoesn't need<suspends>— and why you keep the returned instance around if you want to stop it later.
The signatures (from the digest)
Play(AnimationSequence : animation_sequence, ?PlayRate : float, ?PlayCount : float,
?StartPositionSeconds : float, ?BlendInTime : float, ?BlendOutTime : float)
: play_animation_instance
PlayAndAwait(AnimationSequence : animation_sequence, ?PlayRate : float, ?PlayCount : float,
?StartPositionSeconds : float, ?BlendInTime : float, ?BlendOutTime : float)<suspends>
: play_animation_result
The optional params are your control panel: ?PlayRate retimes the clip (0.5 slow-mo, 2.0 fast), ?PlayCount repeats it, ?StartPositionSeconds starts partway in, and ?BlendInTime / ?BlendOutTime ease the transition so it doesn't pop.
Driving the instance
Play() hands you a play_animation_instance — a remote control for that one playback.
# The instance returned by Play() is a remote control for that one playback.
Instance := Controller.Play(MyAnim)
Instance.Stop() # stop now (blends out)
State := Instance.GetState() # play_animation_state: Playing, BlendingIn,
# BlendingOut, Completed, Stopped, ...
if (Instance.IsPlaying[]): # <decides>: succeeds while it is running
Print("still going")
# React to lifecycle events without polling:
Instance.CompletedEvent.Subscribe(OnDone)
Instance.InterruptedEvent.Subscribe(OnCut)
GetState() returns a play_animation_state (Playing, BlendingIn, BlendingOut, Completed, Interrupted, Stopped, Error). IsPlaying[] is the failable shortcut for "is it still running?". And the CompletedEvent / InterruptedEvent / BlendedInEvent / BlendingOutEvent listenables let you react to the lifecycle without polling.
Waiting for it to finish
When you want code to run after the animation — a cutscene beat, a follow-up emote — use PlayAndAwait. It suspends until the clip ends and returns a play_animation_result telling you whether it Completed, was Interrupted, or hit an Error. Because it suspends, call it from a <suspends> function (here, one we spawn so the button handler stays non-suspending):
# Play() returns instantly. PlayAndAwait() SUSPENDS until the clip ends and
# returns a play_animation_result enum telling you HOW it ended.
PlayThenReact(Presser : agent)<suspends> : void =
if:
Sequence := MyAnim?
Character := Presser.GetFortCharacter[]
Controller := Character.GetPlayAnimationController[]
then:
Result := Controller.PlayAndAwait(Sequence, ?PlayCount := 1.0)
case (Result):
play_animation_result.Completed => Print("Finished cleanly")
play_animation_result.Interrupted => Print("Something interrupted it")
play_animation_result.Error => Print("Playback errored")
You can get the same "wait" from a plain Play() too: keep the instance and call Instance.Await() — it also <suspends> and returns the same play_animation_result.
How this replaces the old hacks
- No more proxy NPC. You no longer spawn an invisible character just to host an animation —
GetPlayAnimationControllerworks on the real player'sfort_character. - No more Animated Mesh stand-in for player emotes. That device is still great for scenery (a dancing statue, a swinging gate), but for animating the player you now go straight to the character.
- You get a handle. The old workarounds were fire-and-forget.
play_animation_instancegives youStop,GetState,Await, and lifecycle events — real control over a single playback.
Recap
- The chain is agent →
GetFortCharacter[]→GetPlayAnimationController[]→Play(...); both getters are<decides>, so use[]inside anif. Play()returns immediately;PlayAndAwait()<suspends>and returns aplay_animation_result.- Declare the editable clip as
?animation_sequence = false— you can't construct one in Verse. - The returned
play_animation_instancegives youStop,GetState,IsPlaying[],Await, andCompletedEvent/InterruptedEvent. - Tune playback with
?PlayRate,?PlayCount,?StartPositionSeconds,?BlendInTime,?BlendOutTime.
References
- https://dev.epicgames.com/documentation/fortnite/verse-api/fortnitedotcom/animation/playanimation
- https://dev.epicgames.com/documentation/en-us/uefn/verse-api/fortnitedotcom/animation/playanimation/play_animation_controller
- https://dev.epicgames.com/documentation/en-us/uefn/verse-api/fortnitedotcom/animation/playanimation/play_animation_controller/play
- https://dev.epicgames.com/documentation/en-us/uefn/verse-api/fortnitedotcom/animation/playanimation/play_animation_controller/playandawait
- https://dev.epicgames.com/documentation/en-us/uefn/verse-api/fortnitedotcom/animation/playanimation/getplayanimationcontroller
Verse source files
- 01-device.verse · device
- 02-fragment.verse · fragment
Check your understanding
4 quick questions. Pick an answer to see if you've got it.
-
1.
-
2.
-
3.
-
4.
References
© Biloxi Studios Inc. — original Verse Island content.
Original tutorial generated by Verse Island from the indexed Verse/UEFN knowledge base, with references to the Epic Games sources above. Code is validated against the knowledge base.