This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revision | |||
manual:animation [2015/01/26 16:23] – [SPEnterFrameEvent] 218.109.147.77 | manual:animation [2015/03/11 14:15] (current) – old revision restored (2015/01/08 10:49) daniel | ||
---|---|---|---|
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 " | ||
+ | |||
+ | 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: | ||
+ | |||
+ | // the corresponding event listener | ||
+ | - (void)onEnterFrame: | ||
+ | { | ||
+ | NSLog(@" | ||
+ | [enemy moveBy: | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | The method " | ||
+ | |||
+ | ===== Tween ===== | ||
+ | |||
+ | Now to predefined animations. They are very common and have names such as " | ||
+ | |||
+ | Those animations are described in an object called " | ||
+ | |||
+ | Enough theory, let's go for an example: | ||
+ | |||
+ | <code objc> | ||
+ | SPTween *tween = [SPTween tweenWithTarget: | ||
+ | [tween animateProperty: | ||
+ | [tween animateProperty: | ||
+ | [tween animateProperty: | ||
+ | [tween animateProperty: | ||
+ | </ | ||
+ | |||
+ | 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: | ||
+ | [tween scaleTo: | ||
+ | [tween fadeTo: | ||
+ | </ | ||
+ | |||
+ | 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, ' | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | Tweens offer some additional features, like delaying their execution, repeating them, or executing certain blocks during the life-time of a tween. | ||
+ | |||
+ | <code objc> | ||
+ | SPTween *tween = [SPTween tweenWithTarget: | ||
+ | |||
+ | // delay by 2 seconds | ||
+ | tween.delay = 2.0f; | ||
+ | |||
+ | // repeat 4 times in yoyo-style | ||
+ | tween.repeatCount = 4; | ||
+ | tween.reverse = TRUE; | ||
+ | |||
+ | // execute blocks while the tween is running | ||
+ | tween.onStart | ||
+ | tween.onUpdate | ||
+ | tween.onComplete = ^{ NSLog(@" | ||
+ | </ | ||
+ | |||
+ | Now we have created the tween --- but nothing will happen yet. A tween object describes the animation, but it does not execute it. That's what the juggler is for. | ||
+ | |||
+ | <note tip> | ||
+ | Find a comprehensive description of tweens in [[tutorials: | ||
+ | </ | ||
+ | ===== 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 in Sparrow. | ||
+ | |||
+ | The easiest way to execute an animation is by the line below --- just add the animation (tween) to the default juggler, and you are done. When the tween is finished, it will automatically be removed. | ||
+ | |||
+ | <code objc> | ||
+ | [Sparrow.juggler addObject: | ||
+ | </ | ||
+ | |||
+ | 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 default juggler. Thus, it fades in just fine. | ||
+ | |||
+ | When you create a custom juggler, all you have to do is to call its " | ||
+ | |||
+ | <code objc> | ||
+ | // In your main game class, listen to EnterFrameEvents: | ||
+ | - (void)onEnterFrame: | ||
+ | { | ||
+ | if (activeMsgBox) | ||
+ | // message box is visible, wait for user input | ||
+ | else | ||
+ | | ||
+ | } | ||
+ | |||
+ | // the game area class advances its juggler in the | ||
+ | // " | ||
+ | - (void)advanceTime: | ||
+ | { | ||
+ | [gameJuggler advanceTime: | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | 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 only one method: | ||
+ | |||
+ | <code objc> | ||
+ | - (void)advanceTime: | ||
+ | </ | ||
+ | |||
+ | This method will automatically be called once per frame. This is how your animation is executed. | ||
+ | |||
+ | If your animated object should be removed from the juggler, you can do so manually through the " | ||
+ | |||
+ | <note tip> | ||
+ | Find a comprehensive description of [[tutorials: | ||
+ | </ | ||
+ | ===== 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 " | ||
+ | |||
+ | 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 " | ||
+ | |||
+ | ==== The Classic Way ==== | ||
+ | |||
+ | To do that, make a call like the following: | ||
+ | |||
+ | <code objc> | ||
+ | [[juggler delayInvocationAtTarget: | ||
+ | </ | ||
+ | |||
+ | 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 '' | ||
+ | |||
+ | <code objc> | ||
+ | [self gameOver]; | ||
+ | </ | ||
+ | |||
+ | That syntax makes it easy to call arbitrarily complex methods. It's perfectly fine to do something like this: | ||
+ | |||
+ | <code objc> | ||
+ | [[juggler delayInvocationAtTarget: | ||
+ | doXYZ: | ||
+ | </ | ||
+ | |||
+ | ==== Using Blocks ==== | ||
+ | |||
+ | Alternatively, | ||
+ | |||
+ | <code objc> | ||
+ | [juggler delayInvocationByTime: | ||
+ | { | ||
+ | [self gameOver]; | ||
+ | }; | ||
+ | </ | ||
+ | |||
+ | ----- | ||
+ | |||
+ | //Next section: [[Movie Clips]]// |