This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
tutorials:advanced_animation_techniques [2011/03/11 19:07] – daniel | tutorials:advanced_animation_techniques [2013/03/05 10:19] (current) – external edit 127.0.0.1 | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ====== Advanced Animation Techniques ====== | ||
+ | |||
+ | Now, you think you know all about Sparrow' | ||
+ | |||
+ | ===== Tween events ===== | ||
+ | |||
+ | First, we'll discuss a fact that we have shamefully ignored so far: tweens dispatch events. Three types of events, to be exact: | ||
+ | |||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | |||
+ | The best way to understand those events should be an example. So, back to Ruth Lezz and her blast door! | ||
+ | |||
+ | <code objc> | ||
+ | // create our door object | ||
+ | BlastDoor *door = [[BlastDoor alloc] init]; | ||
+ | [self addChild: | ||
+ | |||
+ | // now move it down! | ||
+ | SPTween *tween = [SPTween tweenWithTarget: | ||
+ | [tween animateProperty: | ||
+ | [juggler addObject: | ||
+ | |||
+ | // add events | ||
+ | [tween addEventListener: | ||
+ | forType: | ||
+ | [tween addEventListener: | ||
+ | forType: | ||
+ | [tween addEventListener: | ||
+ | forType: | ||
+ | | ||
+ | // here are the corresponding event listeners | ||
+ | - (void)onTweenStarted: | ||
+ | { | ||
+ | NSLog(@" | ||
+ | } | ||
+ | |||
+ | - (void)onTweenUpdated: | ||
+ | { | ||
+ | NSLog(@" | ||
+ | } | ||
+ | |||
+ | - (void)onTweenCompleted: | ||
+ | { | ||
+ | NSLog(@" | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | When you add that code to your project and run it, the output will be similar to this: | ||
+ | |||
+ | < | ||
+ | The door starts to move now. | ||
+ | The door is moving. | ||
+ | The door is moving. | ||
+ | The door is moving. | ||
+ | ... | ||
+ | The door is moving. | ||
+ | The door is moving. | ||
+ | The door is now closed. | ||
+ | </ | ||
+ | |||
+ | As you can see, the START- and END-events are the first and the last events of the tween, respectively. The UPDATE-event, | ||
+ | |||
+ | You can use these events to trigger some action in certain stages of a tween' | ||
+ | |||
+ | For most tasks, however, you can use simpler techniques than events. I'll prove that in the following sections. | ||
+ | |||
+ | ===== Chaining Tweens ===== | ||
+ | |||
+ | A common task is having two or more tweens to be executed one after the other. | ||
+ | |||
+ | Let's say you want to move the door down and then up again. To achieve this, you could first create a tween that moves the door down, then add an event listener that fires when the door arrives at the bottom, and finally create the upward moving tween in this event listener. But, frankly, that's a lot of code to write for such a simple thing, isn't it? Well, the ' | ||
+ | |||
+ | <code objc> | ||
+ | // move down | ||
+ | SPTween *downTween = [SPTween tweenWithTarget: | ||
+ | [downTween animateProperty: | ||
+ | [juggler addObject: | ||
+ | |||
+ | // move up | ||
+ | SPTween *upTween = [SPTween tweenWithTarget: | ||
+ | upTween.delay = downTween.time; | ||
+ | [upTween animateProperty: | ||
+ | [juggler addObject: | ||
+ | </ | ||
+ | |||
+ | It's simple, isn't it? The second tween is started with a delay that is as long as the first tween' | ||
+ | |||
+ | ===== Delayed calls ===== | ||
+ | |||
+ | The juggler contains a very useful method to delay calls. Its syntax takes a little time to get used to, but once you've got the hang of it, you will find many uses for this technique. | ||
+ | |||
+ | Let's start with a simple example. You've got a text field and want to change its text. Easy: | ||
+ | |||
+ | <code objc> | ||
+ | |||
+ | Now you want to do the same thing --- but only 2 seconds from now. The juggler allows you to do that in the following way: | ||
+ | |||
+ | <code objc> | ||
+ | [[mJuggler delayInvocationAtTarget: | ||
+ | </ | ||
+ | |||
+ | I know it looks weird --- so let's see if it makes more sense if we write the same thing in 2 lines: | ||
+ | |||
+ | <code objc> | ||
+ | id futureTextField = [mJuggler delayInvocationAtTarget: | ||
+ | [futureTextField setText: | ||
+ | </ | ||
+ | |||
+ | Think of it like this: '' | ||
+ | |||
+ | //(A short warning though: you cannot use ' | ||
+ | |||
+ | Let's use a real-life example to show the power of this method. We want to display a countdown --- a text field that counts: "3 - 2 - 1 - Go!". With the juggler' | ||
+ | |||
+ | <code objc> | ||
+ | // create a text field with the text " | ||
+ | SPTextField *textField = [SPTextField textFieldWithText: | ||
+ | [self addChild: | ||
+ | |||
+ | // now start the countdown! | ||
+ | [[juggler delayInvocationAtTarget: | ||
+ | [[juggler delayInvocationAtTarget: | ||
+ | [[juggler delayInvocationAtTarget: | ||
+ | |||
+ | // and get rid of it later. | ||
+ | [[juggler delayInvocationAtTarget: | ||
+ | </ | ||
+ | |||
+ | Note that we remove the text field from the screen at the end --- which makes this countdown-method completely self contained (there' | ||
+ | |||
+ | ===== Animate Everything!™ ===== | ||
+ | |||
+ | You already know that you can use a tween to animate every numerical property of an object (integer, float, double). This allows us to do a lot of animations: movement, fading, scaling, rotation, etc. | ||
+ | |||
+ | But this fact can be interpreted in a different way, too: | ||
+ | **As soon as you can describe something with a number, you will be able to animate it.** | ||
+ | |||
+ | Again, here's an example. Many games contain a score display --- a text field that shows the player his current score. When the player achieves something, you raise the score, probably like this: | ||
+ | |||
+ | <code objc> | ||
+ | mScore += 100; // mScore is a member variable (int) | ||
+ | scoreTextField.text = [NSString stringWithFormat: | ||
+ | </ | ||
+ | |||
+ | Now the score changes immediately from, say, 400 to 500. But wouldn' | ||
+ | |||
+ | To do that, we create a subclass of SPTextField, | ||
+ | |||
+ | <code objc NumberField.h> | ||
+ | |||
+ | @interface NumberField : SPTextField | ||
+ | { | ||
+ | int mValue; | ||
+ | } | ||
+ | |||
+ | @property (nonatomic, assign) int value; | ||
+ | |||
+ | @end | ||
+ | </ | ||
+ | |||
+ | <code objc NumberField.m> | ||
+ | #import " | ||
+ | |||
+ | @implementation NumberField | ||
+ | |||
+ | @synthesize value = mValue; | ||
+ | |||
+ | - (void)setValue: | ||
+ | { | ||
+ | mValue = value; | ||
+ | self.text = [NSString stringWithFormat: | ||
+ | } | ||
+ | |||
+ | @end | ||
+ | </ | ||
+ | |||
+ | Voilà, we've just created the " | ||
+ | |||
+ | <code objc> | ||
+ | NumberField *score = [[NumberField alloc] init]; | ||
+ | score.value = 400; | ||
+ | |||
+ | SPTween *raiseScore = [SPTween tweenWithTarget: | ||
+ | [raiseScore animateProperty: | ||
+ | [juggler addObject: | ||
+ | </ | ||
+ | |||
+ | // | ||
+ | |||
+ | ===== Conclusion ===== | ||
+ | |||
+ | If you've followed me through the tutorial this far, you're on your way to becoming a master Sparrow animator! | ||
+ | |||