Differences

This shows you the differences between two versions of the page.

Link to this comparison view

manual_v1:animation [2013/03/05 10:19] (current)
Line 1: Line 1:
 +====== Animation ======
  
 +Animations are a fundamental part of any game. Sparrow helps you by making animations as simple as possible.
 +
 +If you think about it, there are two kinds of animations. There are the animations where you know from the beginning exactly what will happen -- e.g. when you fade in a message box, or when you move some interface element out of the screen. Then there are very dynamic animations, like enemies moving toward the player. The movement of the enemies can change in any frame, as it depends on the actions of the player.
 +
 +Let's first look at the latter type, dynamic animations.
 +
 +===== SPEnterFrameEvent =====
 +
 +In some game engines, you have what is called a "​run-loop"​. That's an endless loop which constantly updates all elements of the scene. In Sparrow, due to the display tree architecture,​ such a run loop would not make much sense. You separated your game in numerous different custom display elements, and each should know for itself what to do when some time has passed.
 +
 +That's what the EnterFrameEvent is for. It's available in any display object, and is dispatched once in every frame. Here is how you use it:
 +
 +<code objc>
 +// e.g. in the init-method
 +[self addEventListener:​@selector(onEnterFrame:​) atObject:​self forType:​SP_EVENT_TYPE_ENTER_FRAME];​
 +
 +// the corresponding event listener
 +- (void)onEnterFrame:​(SPEnterFrameEvent *)event
 +{
 +    NSLog(@"​Time passed since last frame: %f", event.passedTime);​
 +    [enemy moveBy:​event.passedTime * enemy.velocity];​
 +}
 +</​code>​
 +
 +The method "​onEnterFrame:"​ is called once per frame, and you receive the time that has passed since the last frame. With that information,​ you can move your enemies, let snowflakes fall down just a little further, etc.
 +
 +===== Tween =====
 +
 +Now to predefined animations. They are very common and have names such as "​Movement",​ "​Fading",​ "​Rotation",​ etc. Sparrow has a very simple yet powerful approach for those kinds of animations. Basically, you can animate any property of any object, as long as it is numeric (int, float, double).
 +
 +Those animations are described in an object called "​SPTween"​. The term "​Tween"​ comes from hand drawn animations, where a lead illustrator would draw important key frames, while the rest of the team drew the frames in be"​tween"​ those frames.
 +
 +Enough theory, let's go for an example:
 +
 +<code objc>
 +SPTween *tween = [SPTween tweenWithTarget:​msgBox time:0.5f];
 +[tween animateProperty:​@"​y" ​     targetValue:​20.0f];​
 +[tween animateProperty:​@"​scaleX"​ targetValue:​2.0f];​
 +[tween animateProperty:​@"​scaleY"​ targetValue:​2.0f];​
 +[tween animateProperty:​@"​alpha" ​ targetValue:​0.0f];​
 +</​code>​
 +
 +This tween describes an animation that scales the msgBox to twice its size, and simultaneously reduces its opacity till it is invisible. The animation takes half a second. If you are missing the start values: the animation will start using the current values of the specified properties.
 +
 +As you can see, you can animate arbitrary properties of an object, and you can do multiple animations in one tween object.
 +
 +Apropos: since scaling, fading and movement are done so frequently, the SPTween class provides specific methods for that, too. So you can write the following instead:
 +
 +<code objc>
 +[tween moveToX:​0.0f y:20.0f]; // animating "​x"​ and "​y"​
 +[tween scaleTo:​2.0f]; ​        // animating "​scaleX"​ and "​scaleY"​
 +[tween fadeTo:​0.0f]; ​         // animating "​alpha"​
 +</​code>​
 +
 +There is more to tweens: you can change the way the animation is executed -- e.g. letting it start slowly and get quicker and quicker. That's done by specifying a transition type. 
 +
 +These are the available transition types: ​
 +(the default transition type, '​linear',​ was omitted)
 +
 +{{ :​manual:​transitions.png |Tween transitions}}
 +
 +You can also delay the execution of the tween, and you can listen to events that are dispatched when a tween starts or is completed.
 +
 +<code objc>
 +SPTween *tween = [SPTween tweenWithTarget:​msgBox time:0.5f transition:​SP_TRANSITION_EASE_IN];​
 +tween.delay = 2.0f;
 +[tween addEventListener:​@selector(onTweenCompleted:​) atObject:​self
 +                forType:​SP_EVENT_TYPE_TWEEN_COMPLETED];​
 +</​code>​
 +
 +But for now, nothing will happen. A tween object describes the animation, but it does not execute it. That's what the juggler is for.
 +
 +**Hint:** Find a comprehensive description of tweens in [[tutorials:​More about Tweens|this tutorial]].
 +
 +===== The Juggler =====
 +
 +The juggler takes anything that can be animated (that is, anything implementing the SPAnimatable protocol), and executes the animation. There is always a default juggler available at the stage. The easiest way to execute an animation is by the line below --- just add the animation (tween) to the stage juggler, and you are done.
 +
 +<code objc>
 +[[SPStage mainStage].juggler addObject:​tween];​
 +</​code>​
 +
 +In many cases, that simple approach will be fine.
 +
 +Sometimes, though, you want to group your animations into different contexts. Let's say your stage contains a game area, where the game action takes place. When the user clicks on the exit button, you want to pause the game and show an animated message box (this thing won't stop popping up in any chapter!), asking the player if he is sure that he wants to exit the game.
 +
 +In such a case, it makes sense to give the game area its own juggler. As soon as the exit button is pressed, this juggler should just stop animating anything. The game will freeze in its current state. The message box, however, uses the stage juggler. Thus, it fades in just fine.
 +
 +When you create a custom juggler, all you have to do is to call its "​advanceTime:"​ method in every frame. I recommend using jugglers the following way:
 +
 +<code objc>
 +// In your main game class, listen to EnterFrameEvents:​
 +- (void)onEnterFrame:​(SPEnterFrameEvent *)event
 +{
 +    if (activeMsgBox)
 +       // message box is visible, wait for user input
 +    else
 +       ​[gameArea advanceTime:​event.passedTime];​
 +}
 +
 +// the game area class advances its juggler in the
 +// "​advanceTime:"​ method
 +- (void)advanceTime:​(double)seconds
 +{
 +    [gameJuggler advanceTime:​seconds];​
 +}
 +</​code>​
 +
 +That way, you have neatly separated the animations of the game and the message box.
 +
 +By the way: the juggler is not constricted to Tweens. As soon as your class implements the SPAnimatable protocol, you can add it to the juggler. The protocol has one method and one property:
 +
 +<code objc>
 +- (void)advanceTime:​(double)seconds;​
 +@property (nonatomic, readonly) BOOL isComplete;
 +</​code>​
 +
 +You already know what "​advanceTime:"​ is for. The "​isComplete"​ property should return "​NO"​ as long as the animation is not complete (an SPTween returns "​YES"​ as soon as the tween has finished). When the juggler finds a completed animation, it removes it from its list of animations. That's all this method is for.
 +
 +That way, you could e.g. create a simple MovieClip-class. In its "​advanceTime:"​ method, it could constantly change the texture that is displayed. To start the movie clip, you just add it to a juggler. This [[http://​www.sparrow-framework.org/​2010/​05/​a-simple-movie-class/​|blog entry]] has the details. (Sparrow now contains a class for movies called SPMovieClip,​ so you will probably use this class instead -- but look at the blog post anyway, as you learn a lot about how to use the juggler.)
 +
 +**Hint:** Find a comprehensive description of [[tutorials:​More about Jugglers/​|jugglers]] and [[tutorials:​advanced animation techniques|how experts use them]] in the tutorials section.
 +
 +===== Delayed calls =====
 +
 +There is another type of animation that is not that obvious. Sometimes, you want to do something in the future. Let's say the player just lost all his energy. Now, you don't want to stop the game immediately --- that would end the game too abruptly. So, you want to call your "​gameOver"​ method with a delay of, say, 3 seconds.
 +
 +The juggler can do this just as well. The advantage of letting the juggler do this becomes obvious when the game is paused, like in the example above. In that case, you not only want to stop all animations; you want this delayed "​gameOver"​ call to be delayed even more.
 +
 +To do that, make a call like the following:
 +
 +<code objc>
 +[[juggler delayInvocationAtTarget:​self byTime:​3.0f] gameOver];
 +</​code>​
 +
 +Confused? This syntax takes a little time to get used to --- but it's very powerful. To understand it, just imagine that first part of the call (the ''​[juggler delay...]''​ part) returns the "​target"​ argument (in our case: self), but only after 3 seconds (!). Thus, three seconds from now, the following code will be executed:
 +
 +<code objc>
 +[self gameOver];
 +</​code>​
 +
 +That syntax makes it easy to call arbitrarily complex methods. It's perfectly fine to do something like this:
 +
 +<code objc>
 +[[juggler delayInvocationAtTarget:​self byTime:​1.0f]
 +    doXYZ:​@"​text"​ withA:​someObject andB:1234];
 +</​code>​
 +
 +-----
 +
 +//Next section: [[Movie Clips]]//
  manual_v1/animation.txt · Last modified: 2013/03/05 10:19 (external edit)
 
Powered by DokuWiki