Workflow and Animations - Sequencing Animations

In the earlier posts in this series I discussed the need for workflow based solutions to complex animation requirements, and showed how continuation functions could be used to achieve this goal.  In this post I am going to concentrate on the sequencing of animations, i.e. the range of ways you can determine that one animation sequence has completed and another is ready to begin.  To illustrate this, I will use the same body of code, focusing now on the sequencing actions.

 

 

STEP

Function

Sequencing

0

SetupBoard

 

Defines an animation for every tile, where all animations except the last terminate, the last triggers the formbox_continuation on completion.

 

1

formbox_continuation

 

Defines an animation for every tile, where all animations except the last terminate, the last triggers the open_continuation on completion.

 

2

open_continuation

 

Defines two animations, one for each of the two tiles removed to open the wall. The first animation will trigger the deal_continuation on completion.

 

3

deal_continuation

 

Defines an animation for every tile, where four animations trigger completions, one representing the completion of each players hand.  On completion, the flowers and seasons (fs) animation is triggered.

4

fs_continuation

 

Defines an animation for each flower or season tile in a players hand.  The last flower or season will trigger the Kong animation.  If there are no flowers or seasons, control directly passes to the Kong animation.

5

kong_continuation

 

Defines an animation for each tile in a Kong in a players hand.  The last tile in the Kong will trigger the redeal animation.  If there is no Kong in the players hand, control directly passes to the redeal animation.

6

redeal_continuation

 

If the player holds less than 13 tiles, an animation for each tile dealt to bring their hand back to 13 tiles will be generated.  The last tile dealt will trigger the flower and season animation.

If the players hand already contains 13 tiles, then the entire sequence will terminate.

 

There are two clear types of sequences here, steps 0 through 3 have a single continuation triggered during each animation cycle, while the remaining steps have four continuations triggered, one for each players hand.  This is a natural reflection of the problem logic, i.e. the first actions are global across the Mah-Jongg board, whereas the latter actions are all focused on individual players hands.

 

There are three types of logical control used in this logic, where 0 through 3 are simple linear flow of control, 4 through six involve conditional flow of control., and conditional logic is used to form a loop from the redeal animation to the flower and season animation. Additionally, the ability to set the durations of animations, and the fact that a set of them run in parallel means that there are multiple ‘virtual’ threads of control active in the animation logic.  By ‘virtual’, I mean that they are independent logical flows, however they are all actually running on the same thread.

 

In summary, the combination of the WPF animation architecture with continuations based on animation completion events allows for the creation of rich control structures, which can be used to weave a complex tapestry of parallel animations.  This means that the animation logic has an excellent opportunity to represent the problem domain faithfully, which in turn means that animations will not have excessive complexity above and beyond their natural requirements.

 

Animation Durations

All of the mechanisms described here assume that animations take a known amount of time to complete, and some mechanisms require that individual animations in any step complete in a specific order.  This is supported directly by WPF animations because every animation can have a duration assigned to it, or a start and finish time.  There is certainly a bit of noise here, i.e. animations may not take exactly the amount of time expected, or there may be slight variations in ordering, however these small errors will not interfere with visual effects.

 

When a series of animations are being started in sequence, and the sequencing logic is triggered by the completion of the previous animation, it is important to offset the start times of each of the individual animations in one atomic block of animations.  For example, in the SetupBoard() logic, each of the individual tiles is animated in series, i.e. each subsequent tile begins motion after the previous one begins moving.  While many tiles may be in motion at once, there is a specific order to when the individual tile animations start and finish.  This allows us to know that when the last tile finishes moving the entire sequence is complete and the next step can be triggered.

 

The following code fragment shows how a progressively incremented timespan is used to offset the start time of each animation, while the lifetime of each animation remains constant.

 

private const int SETUPDELAYMSEC = 50;

….

msec += SETUPDELAYMSEC;

TimeSpan ts = new TimeSpan(0, 0, 0, msec / 1000, msec % 1000);

ApplyTrajectory(t, ts, 5);

 

The ApplyTrajectory method, which is used to activate the animations in a TileTrajectory, takes three arguments, the TileTrajectory instance that is to be activated, the timespan representing the start delay, and the duration of the animation.  In this case, each tile starts 50 milliseconds after the previous animation, and each animation takes 5 seconds to complete.  This allows for simple sequencing of the next animation step, forming the box, by simply attaching the continuation to the last tile animated, which will start (50 * 51) milliseconds after the first animation, where 50 represents the delay between each tile animation, and 51 represents the 51 tiles dealt before the last tile was dealt.

 

Since there are a total of 52 tiles that will be in motion, the following table gives an overview of which tiles have not yet started to move, are in motion, or have completed motion over the entire animation sequence.

 

Single Thread Animations

Serial Animations are those that do not involve conditionals, and where, across the entire set of animations triggered at any step, only one of these animations triggers a continuation.  Consider the formation of the box, where the four walls are pushed together to form a box.  The animation to open the wall should not commence until the box is formed, therefore, it should be triggered when the last wall has moved into place. In terms of design, these kinds of animations are both common and preferable, as they’re easy to understand and implement.  They do have limited functionality, therefore they are used in conjunction with other, more sophisticated control logic to acheive more complex effects.

 

Multi Thread Serial Animations

Multi thread serial animations are those where more than one continuation is triggered across the set of animations triggered at any step.  There are three basic types of multithread sequences, outlined in the following table

 

Fan Out

The number of active animation ‘threads’ grows.  This is illustrated in step 3, the deal animation.  Step 2 will trigger a single continuation, indicating that the box has been formed.  Step 3 will trigger four continuations, one for each hand being dealt.  The subsequent stage (4)  is based on per hand logic, whereas the  previous stage (2) is based on logic global to the board.

 

1:1

The remaining stages (4 through 6) all represent 1:1 continuations of a multithread animation, i.e. each stage is oriented toward processing an individual hand, and therefore needs to be run four times, once for each hand.

 

Fan In

While not illustrated here, this is the converse to a fan out and would occur when some form of negotiation took place between a series of parallel continuations to trigger a single outgoing continuation.  Consider if we wanted to fan in all of the distinct hand continuations into some continuation of board scope.  As each continuation for a hand triggered, we could count that event, and on determining we had counted four events, we would know that all of the hand continuations had fired,.  At this time we could trigger a single continuation, which would then effectively fan in a series of parallel continuations into a single continuation.

 

Conditional Logic in Animations

If you examine the body of redeal_continuation, you can see that the number of iterations in the loop is dependent on the number of tiles that need to be dealt to the player, and that the loop body attaches a continuation handler to the last tile dealt to that player.  Therefore, if the players hand contained no flower or season tiles, and contained no Kongs, no tiles will have been removed from the originally dealt hand, no tiles will need to be redealt, and therefore this hand animation sequence will quietly expire.  Conversely, if new tiles are being dealt to a player, then the continuation sequence will loop back to  fs_continuation and the process will repeat, until the player finally has a hand that contains no new flowers, seasons, or Kongs.

This not only illustrates the use of conditional logic in animation sequences, it also shows how this conditional logic can be used to form more complex structures, in this case a do/while loop.

 

 

Published Tuesday, July 17, 2007 7:12 AM by MarkMMullin
Filed Under: , ,

Comments

# Animation Article Overview @ Tuesday, July 17, 2007 4:25 AM

 
These articles illustrate a mechanism you can use to perform sophisticated animations using WPF...

Mark Mullin's Professional Blog