Component that handles playing animation clips as well as binding callbacks to user defined events.

/**
* Transition between two Animation Clips `firstClipName` and `secondClipName`
* on tap by alternating secondClip weight between 0 or 1 within some `transitionInSec`,
* ensuring that secondClip is processed after firstClip as to blend the animations correctly.
*/

//@input Component.AnimationPlayer animationPlayer
//@input number transitionInSec = 1
//@input string firstClipName
//@input string secondClipName

// Keep track of current state of transition.
let isTransitioning = false;
let accumulatedTime = 0;
let secondClipTargetWeight = 1;

// Store a reference to the clips so we don't
// have to keep asking engine.
let firstClip, secondClip;

/*********************************************************
Events that triggers and responds to the transitions.
********************************************************/

// Trigger the transition to the new clip.
const startTransition = script.createEvent("TapEvent");
startTransition.bind(triggerTransition);

const onUpdateEvent = script.createEvent("UpdateEvent");
onUpdateEvent.bind(interpolateBetweenAnimations);

/*********************************************************
Define how transitioning works.
********************************************************/

// Play the first clip immediately.
// and make sure clips are in the right order.
function init() {
script.animationPlayer.playClipAt(script.firstClipName, 0);

// Clips are processed in order. Thus, if we want to transition
// to a second clip, we need to make sure the second clip
// is processed after the first clip.
putClipAtEndIfNeeded(script.animationPlayer, script.secondClipName);

// Store references to the clips.
firstClip = script.animationPlayer.getClip(script.firstClipName);
secondClip = script.animationPlayer.getClip(script.secondClipName);
}

// Trigger the transition to the new clip.
function triggerTransition() {
// Only transition, if we're not in the middle of transitioning.
if (!isTransitioning) {
// If the target weight is 0, that means the clip is already playing
// so we don't need to tell it to play again.
if (secondClipTargetWeight === 1) {
script.animationPlayer.playClipAt(script.secondClipName, 0);
}

accumulatedTime = 0;
isTransitioning = true;

// Begin the update loop that will transition the clips.
onUpdateEvent.enabled = isTransitioning;
}
}

// Blend the weight of a second clip to the first clip,
// based on the time elapsed in transition.
function interpolateBetweenAnimations () {
if (isTransitioning) {
// Keep track of how many seconds elapsed since the start of the transition.
accumulatedTime += getDeltaTime();

// Once time has elapsed beyond our total transition time
// we can finalize our weight and callback.
if (accumulatedTime >= script.transitionInSec) {
secondClip.weight = secondClipTargetWeight;

onTransitionEnd();

return;
}

// Set the current weight of the clips based on % between how long we've beeen transitioning for
// and how long we want total transition to take.
let currentWeight = accumulatedTime / script.transitionInSec ;
secondClip.weight = lerp(1-secondClipTargetWeight, secondClipTargetWeight, currentWeight);
}
}


function onTransitionEnd() {
// Set our target weight to be the opposite.
secondClipTargetWeight = 1 - secondClipTargetWeight;

// Enable triggering transition, and disable the update loop.
isTransitioning = false;
onUpdateEvent.enabled = isTransitioning;
}

/*********************************************************
Helpers
********************************************************/

function putClipAtEndIfNeeded(animationPlayer, clipName) {
const clips = animationPlayer.getAnimationClips();
const currentOrderOfClips = clips.map(function(clip) {return clip.name;});
const clipIndex = currentOrderOfClips.indexOf(clipName);

if (clipIndex != clips.length - 1) {
if (clipIndex < 0) {
print("Clip with that name is not found in the provided Animation Player.");
return;
}

const clonedClip = clips[clipIndex].clone(clipName);
animationPlayer.removeClip(clipName);
animationPlayer.addClip(clonedClip);
}
}

function lerp(a, b, t) {
return a * (1.0 - t) + b * t;
}

/*********************************************************
Initialize the script!
********************************************************/

init();
interface AnimationPlayer {
    clips: AnimationClip[];
    enabled: boolean;
    onEvent: event1<AnimationPlayerOnEventArgs, void>;
    sceneObject: SceneObject;
    uniqueIdentifier: string;
    addClip(clip: AnimationClip): void;
    destroy(): void;
    forceUpdate(deltaTime: number): void;
    getActiveClips(): string[];
    getClip(name: string): AnimationClip;
    getClipCurrentTime(name: string): number;
    getClipEnabled(name: string): boolean;
    getClipIsPlaying(name: string): boolean;
    getInactiveClips(): string[];
    getSceneObject(): SceneObject;
    getTransform(): Transform;
    getTypeName(): string;
    isOfType(type: string): boolean;
    isSame(other: ScriptObject): boolean;
    pauseAll(): void;
    pauseClip(name: string): void;
    playAll(): void;
    playClipAt(name: string, time: number): void;
    removeClip(name: string): void;
    resumeAll(): void;
    resumeClip(name: string): void;
    setClipEnabled(name: string, enabled: boolean): void;
    stopAll(): void;
    stopClip(name: string): void;
}

Hierarchy (view full)

Properties

clips: AnimationClip[]

Array of animation clips

enabled: boolean

If disabled, the Component will stop enacting its behavior.

sceneObject: SceneObject
uniqueIdentifier: string

Methods

  • Adds a clip to the player. If one exists, replace existing clip.

    Parameters

    Returns void

  • Destroys the component.

    Returns void

  • Updates the animation player forcing sampling, resulting in the setting of transforms and firing of animation events.

    Parameters

    • deltaTime: number

    Returns void

  • Get currently playing clips.

    Returns string[]

  • Tries to get a clip from the player, returns null if it does not exist.

    Parameters

    • name: string

    Returns AnimationClip

  • Returns the current time for a clip.

    Parameters

    • name: string

    Returns number

  • Returns if a clip is enabled for playback.

    Parameters

    • name: string

    Returns boolean

  • Returns if a clip is playing.

    Parameters

    • name: string

    Returns boolean

  • Get currently inactive clips.

    Returns string[]

  • Returns the name of this object's type.

    Returns string

  • Returns true if the object matches or derives from the passed in type.

    Parameters

    • type: string

    Returns boolean

  • Returns true if this object is the same as other. Useful for checking if two references point to the same thing.

    Parameters

    Returns boolean

  • Pauses all clips.

    Returns void

  • Pause the clip with name.

    Parameters

    • name: string

    Returns void

  • Plays all clips.

    Returns void

  • Plays clip with the given name and starting from the given time.

    Parameters

    • name: string
    • time: number

    Returns void

  • Removes a clip from the player.

    Parameters

    • name: string

    Returns void

  • Resumes all clips.

    Returns void

  • Resumes clip with name.

    Parameters

    • name: string

    Returns void

  • Sets the clip to be enabled.

    Parameters

    • name: string
    • enabled: boolean

    Returns void

  • Stops all clips and resets time to t = 0.

    Returns void

  • Stops the clip and resets time to t = 0.

    Parameters

    • name: string

    Returns void