Differences

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

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
tutorials:advanced_animation_techniques [2011/03/11 19:07] danieltutorials: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's animation system after what you have read so far? But we have merely scratched the surface! Get ready for the final part of this tutorial to find uses of the juggler you have probably never thought of!
 +
 +===== 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:
 +
 +  * ''SP_EVENT_TYPE_TWEEN_STARTED''
 +  * ''SP_EVENT_TYPE_TWEEN_UPDATED''
 +  * ''SP_EVENT_TYPE_TWEEN_COMPLETED''
 +
 +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:door];
 +
 +// now move it down!
 +SPTween *tween = [SPTween tweenWithTarget:blastDoor time:4.0];
 +[tween animateProperty:@"y" targetValue:100.0f];
 +[juggler addObject:tween];
 +
 +// add events
 +[tween addEventListener:@selector(onTweenStarted:) atObject:self 
 +                forType:SP_EVENT_TYPE_TWEEN_STARTED];
 +[tween addEventListener:@selector(onTweenUpdated:) atObject:self 
 +                forType:SP_EVENT_TYPE_TWEEN_UPDATED];
 +[tween addEventListener:@selector(onTweenCompleted:) atObject:self 
 +                forType:SP_EVENT_TYPE_TWEEN_COMPLETED];
 +  
 +// here are the corresponding event listeners
 +- (void)onTweenStarted:(SPEvent *)event
 +{
 +  NSLog(@"The door starts to move now.")
 +}
 +
 +- (void)onTweenUpdated:(SPEvent *)event
 +{
 +  NSLog(@"The door is moving.");
 +}
 +
 +- (void)onTweenCompleted:(SPEvent *)event
 +{
 +  NSLog(@"The door is now closed.")
 +}
 +</code>
 +
 +When you add that code to your project and run it, the output will be similar to this:
 +
 +<code>
 +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.
 +</code>
 +
 +As you can see, the START- and END-events are the first and the last events of the tween, respectively. The UPDATE-event, however, is dispatched every time the tween is advanced (in other words: once per frame).
 +
 +You can use these events to trigger some action in certain stages of a tween's life cycle. This can come in handy in a lot of situations. 
 +
 +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 'delay'-property comes to the rescue! Look at this:
 +
 +<code objc>
 +// move down
 +SPTween *downTween = [SPTween tweenWithTarget:door time:1.0];
 +[downTween animateProperty:@"y" targetValue:100.0f];
 +[juggler addObject:downTween];
 +
 +// move up
 +SPTween *upTween = [SPTween tweenWithTarget:door time:1.0];
 +upTween.delay = downTween.time; // <- !
 +[upTween animateProperty:@"y" targetValue:0.0f];
 +[juggler addObject:upTween];
 +</code>
 +
 +It's simple, isn't it? The second tween is started with a delay that is as long as the first tween's duration. With this technique, you can chain as many tweens as you want.
 +
 +===== 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>[textField setText:@"Blah"];</code>
 +
 +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:textField byTime:2.0] setText:@"Blah"];
 +</code>
 +
 +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:textField byTime:2.0];
 +[futureTextField setText:@"Blah"];
 +</code>
 +
 +Think of it like this: ''delayInvocationAtTarget:byTime:'' returns a future version of the text field (perhaps it uses a Flux Capacitor™, but who knows?). And everything that you do to this text field will only happen 2 seconds from now.
 +
 +//(A short warning though: you cannot use 'release', 'copy' and other methods of Objective C's NSObject base class -- but that's rarely a problem.)//
 +
 +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's delayed calls, you can do that easily!
 +
 +<code objc>
 +// create a text field with the text "3"
 +SPTextField *textField = [SPTextField textFieldWithText:@"3"];
 +[self addChild:textField];
 +
 +// now start the countdown!
 +[[juggler delayInvocationAtTarget:textField byTime:1.0] setText:@"2"];
 +[[juggler delayInvocationAtTarget:textField byTime:2.0] setText:@"1"];
 +[[juggler delayInvocationAtTarget:textField byTime:3.0] setText:@"Go!"];
 +
 +// and get rid of it later.
 +[[juggler delayInvocationAtTarget:textField byTime:4.0] removeFromParent];
 +</code>
 +
 +Note that we remove the text field from the screen at the end --- which makes this countdown-method completely self contained (there's no need to save that object in a member variable just to remove it from the screen later).
 +
 +===== 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:@"%d", mScore];
 +</code>
 +
 +Now the score changes immediately from, say, 400 to 500. But wouldn't it be nice if it displayed the intermediate values, too? Quickly rising from 400 to 401, 402, 403, etc., until it reaches 500?
 +
 +To do that, we create a subclass of SPTextField, that adds --- a numerical property! Remember, any numerical property can be animated. So we better create one!
 +
 +<code objc NumberField.h>
 +
 +@interface NumberField : SPTextField
 +{
 +  int mValue;
 +}
 +
 +@property (nonatomic, assign) int value;
 +
 +@end
 +</code>
 +
 +<code objc NumberField.m>
 +#import "NumberField.h"
 +
 +@implementation NumberField
 +
 +@synthesize value = mValue;
 +
 +- (void)setValue:(int)value
 +{
 +  mValue = value;
 +  self.text = [NSString stringWithFormat:@"%d", value];
 +}
 +
 +@end
 +</code>
 +
 +Voilà, we've just created the "NumberField" class. It's no more than a text field than has a special property for displaying numbers. With that class in our tool belt, we can animate the score as we wish:
 +
 +<code objc>
 +NumberField *score = [[NumberField alloc] init];
 +score.value = 400;
 +
 +SPTween *raiseScore = [SPTween tweenWithTarget:score time:0.2];
 +[raiseScore animateProperty:@"value" targetValue:500];
 +[juggler addObject:raiseScore];  
 +</code>
 +
 +//(Disclaimer: use the 'alloc/init' approach to create instances of the NumberField class. If you want to use the 'textFieldWith...'-methods, you have to overwrite them in the NumberField class.)//
 +
 +===== Conclusion =====
 +
 +If you've followed me through the tutorial this far, you're on your way to becoming a master Sparrow animator!  :)
 +
  
 
 
Powered by DokuWiki