Animating the SpriteFigure 19-6 shows the visible methods in Animator. Animator performs three core tasks:
Figure 19-6. Public methods in the Animator classAdding an Animation SequenceThe majority of the public methods in Animator (rotClock( ), rotCounterClock( ), moveLeft( ), moveRight( ), punch( ), moveForward( ), moveBackwards( ), and toggleAc-tive( )) execute in a similar way when called by the KeyBehavior object. They add a predefined animation sequence to a schedule (which is implemented as an ArrayList called animSchedule):
forwards is an array of strings, which represents the animation sequence for moving the sprite forward one step:
"walk1", "walk2", and so forth are the names of the 3DS files holding the sprite's pose This correspondence is used to load the models when AnimTour3D starts. A requirement of an animation sequence is that it ends with "stand". One reason for this is that a sequence should end with the model in a neutral position, so the next sequence can follow on smoothly from the previous one. The other reason is the Animator object uses "stand" to detect the end of a sequence. addAnims( ) adds the sequence to the end of animSchedule and increments seqCount, which stores the number of sequences currently in the schedule:
The maximum number of sequences in the schedule at any time is restricted to MAX_SEQS. This ensures that a key press (or, equivalently, an animation sequence) isn't kept waiting too long in the schedule before being processed. This would happen if the user pressed a key continuously, causing a long schedule to form. By limiting the number of sequences, the Animator briefly ignores key presses when the waiting animation sequence gets too long. However, once the animation sequence gets smaller (after some of it has been processed), key presses will be accepted. addAnims( ) is synchronized so it's impossible for the animSchedule to be read while being extended. Processing an Animation OperationThe Animator constructor creates a WakeupCondition object based on the time delay passed to it from WrapAnimTour3D:
This condition is registered in initialize( ) so processStimulus( ) will be called every td milliseconds:
processStimulus( ) is short since there's no need to examine the wake-up criteria. Since it's been called is enough because a call occurs every td milliseconds. getNextAnim( ) wants to remove an animation operation from animSchedule. However, the ArrayList may be empty, so the method can return null:
getNextAnim( ) is synchronized to enforce mutual exclusion on animSchedule. If the retrieved operation is "stand", then the end of an animation sequence has been reached, and seqCount is decremented. My defense of this wonderful design is that "stand" performs two useful roles: it signals the end of a sequence (as here), and changes the sprite pose to a standing position, which is a neutral stance before the next sequence begins. doAnimation( ) can process an animation operation (represented by a String) in two ways: the operation may trigger a transformation in the user's sprite (called bob), and/or cause a change to the sprite's pose. In addition, it may be necessary to update the user's viewpoint if the sprite has moved:
The first part of doAnimation( ) specifies how an animation operation is translated into a sprite transformation. One trick is shown in the processing of the forward and backwards sequences. These sequences are defined as:
The forwards sequence is carried out in response to the user pressing the down arrow. What happens? The sequence is made up of three poses ("walk1", "walk2", and "stand"), so the sequence will be spread over three activations of processStimulus( ). This means that the total sequence will take 3*<time delay> to be evaluated, which is about 60 ms. Multiple steps forward are achieved by adding multiple copies of the forward sequence to the Animator's scheduler list. The punching animation sequence is defined as:
Since "punch1" and "punch2" appear twice, they will be processed twice by processStimulus( ), which means their effects will last for 2*<time delay>. Consequently, the poses will be on the screen for twice the normal time, suggesting that the sprite is holding its stance. Updating the User's ViewpointAnimator uses the viewpoint manipulation code developed in TouristControls (in Chapter 18). As a sprite moves, the viewpoint sticks with it, staying a constant distance away unless the user zooms the viewpoint in or out. The initial viewpoint is set up in Animator's constructor via a call to setViewer( ), which is the same method as in TouristControls. The new problem with Animator is when to update the viewpoint. It shouldn't be updated until the animation operation (e.g., "walk1") is executed. For that reason, the viewpoint update method, viewerMove( ), is called at the end of doAnimation( ). The final aspects of viewpoint adjustment are the keys i and o, which zoom the viewpoint in and out. The keys are immediately processed in Animator by shiftViewer( ), which changes the viewpoint based on the sprite's current position:
The shift operations aren't scheduled like the other sprite movement commands. As a consequence, the Animator changes the viewpoint immediately, even if a large number of sprite movement key presses precede the i or o keys. This behavior may be disconcerting to a user since the viewpoint seems to change too soon before earlier sprite moves have been carried out. An obvious question is why do I support this strange behavior? Why not schedule the viewpoint zooming along with the sprite animation? The answer is to illustrate that viewpoint and sprite changes can be separated. |
Tuesday, January 19, 2010
Animating the Sprite
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment