< PREVIOUS: Mouse Events | NEXT: Armatures >
 

Animation

Playing a Defined Animation

AnimatedSceneNode contains a list of AnimationFrames which describes the state of the node's geometry at any point during an animation. An animation is simply a named interval of such AnimationFrames. While animations are defined automatically when an AnimatedSceneNode is loaded from a file, they can also be defined manually via the AnimatedSceneNode.DefineAnimation method.

Any defined animation can then be played via the AnimatedSceneNode.Play method and stopped via the AnimatedSceneNode.Pause method. Below are parameters for playing an animation:
  • loop: A boolean specifying whether or not to loop the animation
  • restart: A boolean specifying whether or not to reset the animation to its beginning frame before playing
  • fps: An integer specifying the play rate in frames-per-second (a negative value will play the animation in reverse)
The following example shows how to define and play an animation:

// Define the animation "Run" to be frames 50 through 100, inclusive
animNode.DefineAnimation("Run", 50, 100);

// Play the animation from the start and loop for ten seconds at 60 frames-per-second
animNode.Play("Run", true, true, 60.0);
Thread.Sleep(10000);
animNode.Pause();

AnimatedSceneNode also contains an event called AnimationCompleted. This event can be subscribed to and will be fired whenever a non-looping animation completes, supplying the name of the completed animation in its SceneNodeAnimationEventArgs.

Timelines

When it is necessary to play multiple synchronized animations on various AnimatedSceneNodes, either simultaneously or in sequence, the Ascend Timeline class (not to be confused with WPF's own Timeline class) can be used.

A Timeline is made up of multiple TimelineAnimations, each of which define:
  • The AnimatedSceneNode to animate
  • The animation to play, which must already be defined on the AnimatedSceneNode
  • When to start the animation, measured in frames since the beginning of the Timeline
  • When to end the animation, measured in frames since the beginning of the Timeline
    • The animation will loop until the Timeline frame reaches this point
    • If this parameter is left out, it will be computed such that the animation will play once
  • The play rate of the animation, relative to the Timeline
    • Specifying a negative value will play the animation in reverse
The following code shows how to set up and play a Timeline:

// Create Timeline for animNode1 and animNode2, where each will simultaneously:
// - Loop over "Run" for 200 frames, then
// - Loop over "Walk" for 200 frames, then
// - "Jump" once, then
// - "Jump" again at 2.5 times the nominal play rate
Timeline timeline = new Timeline();
timeline.Animations.Add(new TimelineAnimation(animNode1, "Run", 0, 199));
timeline.Animations.Add(new TimelineAnimation(animNode2, "Run", 0, 199));
timeline.Animations.Add(new TimelineAnimation(animNode1, "Walk", 200, 399));
timeline.Animations.Add(new TimelineAnimation(animNode2, "Walk", 200, 399));
timeline.Animations.Add(new TimelineAnimation(animNode1, "Jump", 400));
timeline.Animations.Add(new TimelineAnimation(animNode2, "Jump", 400));

int currentEnd = timeline.EndFrameNumber;
timeline.Animations.Add(new TimelineAnimation(animNode1, "Jump", currentEnd + 1, 2.5));
timeline.Animations.Add(new TimelineAnimation(animNode2, "Jump", currentEnd + 1, 2.5));

// Play the Timeline from the start and loop for ten seconds at 60 frames-per-second
timeline.Play(true, true, 60.0);
Thread.Sleep(10000);
timeline.Pause();

Timeline also contains an event called Completed. This event can be subscribed to and will be fired whenever a non-looping Timeline completes.

Animation Using WPF

Since the transformation properties (position, rotation, scale) on the TransformableObject class are implemented using WPF dependency properties, they are animatable via procedural code. This means that any SceneNode or Bone can be animated programmatically. This is an important capability as it allows the developer to animate a wide variety of Ascend objects dynamically rather than relying on predefined animations described only on an AnimatedSceneNode. The following code shows an example of how to animate the ScaleX property of a SceneNode called mySceneNode:

// Create a DoubleAnimation
DoubleAnimation myDoubleAnimation = new DoubleAnimation();
myDoubleAnimation.From = 1.0;
myDoubleAnimation.To = 0.0;
myDoubleAnimation.Duration = new Duration(TimeSpan.FromSeconds(5));
myDoubleAnimation.AutoReverse = true;
myDoubleAnimation.RepeatBehavior = RepeatBehavior.Forever;
    
// Create a Storyboard and apply the animation the the ScaleX property
Storyboard myStoryboard = new Storyboard();
myStoryboard.Children.Add(myDoubleAnimation);
Storyboard.SetTarget(myDoubleAnimation, mySceneNode);
Storyboard.SetTargetProperty(myDoubleAnimation,
    new PropertyPath(TransformableObject.ScaleXProperty));

// Start the animation
myStoryboard.Begin();

Bones and Procedural Animation

Special consideration must be given when animating Bones via the method outlined above. If, for instance, the ScaleX property were being animated on a Bone, the change would not reflect on the Bone's skin until a call to Armature.UpdateSkin was made (for more information on Armatures, Bones, and skins, see the Armatures section of this manual). Unlike animation via AnimatedSceneNode.Play, Armature.UpdateSkin is not called automatically for WPF procedural animation. The following code shows how to set up a System.Windows.Threading.DispatcherTimer to call Armature.UpdateSkin at regular intervals:

// Pick a Bone to animate
Bone bone = myAnimatedNode.Armature.RootBones.First();

// Create a Storyboard and apply the animation the the ScaleX property
Storyboard myStoryboard = new Storyboard();
myStoryboard.Children.Add(myDoubleAnimation);
Storyboard.SetTarget(myDoubleAnimation, bone);
Storyboard.SetTargetProperty(myDoubleAnimation,
    new PropertyPath(TransformableObject.ScaleXProperty));

// Choose an update frame rate
int fps = 60;

// Create a DispatcherTimer to update skin once per frame
DispatcherTimer timer =
    new DispatcherTimer { Interval = new TimeSpan(0, 0, 0, 0, 1000 / fps) };

timer.Tick += (sender, e) =>
    {
        bone.Armature.UpdateSkin();
    };

// Stop timer when animation is done
myStoryboard.Completed += (sender, e) =>
    {
        timer.Stop();
    };

// Start the animation and timer
myStoryboard.Begin();
timer.Start();

< PREVIOUS: Mouse Events | NEXT: Armatures >
 

Last edited May 1, 2014 at 5:54 PM by menehune23, version 41

Comments

No comments yet.