Old tuto : utilisation des Timer pour animer une balle

Ce tutoriel met en pratique l’utilisation des Timer, notamment pour animer une balle. Mais comme je n’avais pas de balle, j’ai du improviser en la remplaçant par une pomme (clin d’oeil aux Mac User). Voici ce que cela va donner.

1) On pose le décor

A ton niveau, je ne vais pas te faire l’offense d’expliquer pas à pas comment créer une view et son viewcontroller.
Ouvre un nouveau projet que l’on va nommer « tuto10 », puis choisir pour cela un ViewBased-Application.
Dans l’Interface Builder de « tuto10ViewController », ajouter un UIImageView qui recouvre entièrement l’iphone et un UIView qui recouvre les 3/4 environ (320*360).
Dans Xcode, relier la view dans le tuto10ViewController.h et ne pas oublier de mettre une image de fond dans notre UIImage. Ca devrait donner ça à notre niveau, si tu as pensé à relier aussi les éléments dans Interface Builder :

#import <UIKit/UIKit.h>

@interface tuto10ViewController : UIViewController {
    IBOutlet UIView *viewTerrain;

}

@end

Nous allons maintenant ajouter un UIImageView dans l’interface builder : il va nous servir de réceptacle à notre balle. La balle que j’utilise fait 50*50, donc je définit la taille de mon UIImageView à 50*50.
On implémente dans Xcode le tuto10ViewController.h de telle manière à obtenir ceci :

#import <UIKit/UIKit.h>

@interface tuto10ViewController : UIViewController {
    IBOutlet UIView *viewTerrain;
    IBOutlet UIImageView *imageBalle;

}
@property(nonatomic,retain) UIImageView *imageBalle;


@end

tuto10ViewController.m ressemble alors à cela :

#import "tuto10ViewController.h"

@implementation tuto10ViewController
@synthesize imageBalle;

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
    [super viewDidLoad];
}

Vérifier, dans un dernier temps, que le tuto10AppDelegate.h est similaire à celui-ci (généralement il manque les IBOutlet … ).

#import <UIKit/UIKit.h>

@class tuto10ViewController;

@interface tuto10AppDelegate : NSObject <UIApplicationDelegate> {
    IBOutlet UIWindow *window;
    IBOutlet tuto10ViewController *viewController;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet tuto10ViewController *viewController;

@end

Je pense qu’on peut honnêtement compiler et lancer notre programme. Si tout va bien, tu devrais retrouver ta fenêtre graphique.

2) Timer : Animer une pomme

Rentrons désormais dans le vif du sujet. Il est temps d’implémenter tuto10ViewController de manière à animer notre balle.
Le principe est simple, il consiste à créer un timer qui envoie des « tops » d’horloge à intervalle fixe. Ainsi, à chaque « top », on appelle une fonction qui modifie la position de la balle. Le tout se doit d’être fluide. Don’t worry, tout est expliqué un peu plus bas. Voici comment on s’y prend dans le tuto10ViewController.m :

#import "tuto10ViewController.h"

@implementation tuto10ViewController
@synthesize imageBalle;
@synthesize timermouvement;

    float vitesse=0.04;

-(void)topDepartMouvement: (NSTimer*)timer{
    coordonnees = CGPointMake(11,7);
    timer = [NSTimer scheduledTimerWithTimeInterval:vitesse target:self selector:@selector(topHorloge) userInfo:nil repeats:YES];
    timermouvement = timer;
}

- (void)topHorloge{
    imageBalle.center=CGPointMake(imageBalle.center.x+coordonnees.x, imageBalle.center.y+coordonnees.y);
    if(imageBalle.center.x-25 < 0 || imageBalle.center.x +25 > 320)
        coordonnees.x = -coordonnees.x;
    if(imageBalle.center.y-25 < 0 || imageBalle.center.y +25 > 360)
        coordonnees.y = -coordonnees.y;
}

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
    [super viewDidLoad];
    [self topDepartMouvement: timermouvement];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
    // Release anything that's not essential, such as cached data
}


- (void)dealloc {
    [super dealloc];
}

@end

Ne pas oublier d’implémenter tuto10ViewController.m, où il faut effectuer nos déclarations :

#import <UIKit/UIKit.h>

@interface tuto10ViewController : UIViewController {
    IBOutlet UIView *viewTerrain;
    IBOutlet UIImageView *imageBalle;
    CGPoint coordonnees;
    NSTimer *timermouvement;

}

@property(nonatomic,retain) UIImageView *imageBalle;
@property(nonatomic,retain) NSTimer *timermouvement;

-(void)topHorloge;
-(void)topDepartMouvement: (NSTimer*)timer;

@end

Tu peux compiler et lancer pour le fun, tu verras que ta balle se met en mouvement et rebondit sur les bords. Mais tu ne sais pas vraiment comment ça fonctionne. Etudions donc de plus près le code que je t’ai fait écrire.

http://www.ipup.fr/forum/userimages/Image-12-2.jpg

Voici dans l’ordre, comment est compilé le code :

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
    [super viewDidLoad];
    [self topDepartMouvement: timermouvement];
}

Le compilateur charge notre vue principale, initialise la variable vitesse à 0.04, puis fait appel à la méthode topDepartMouvement. On lui passe le paramètre timermouvement, qui est le nom du Timer que l’on a déclaré dans tuto10ViewController.h .

-(void)topDepartMouvement: (NSTimer*)timer{
    coordonnees = CGPointMake(11,7);
    timer = [NSTimer scheduledTimerWithTimeInterval:vitesse target:self selector:@selector(topHorloge) userInfo:nil repeats:YES];
    timermouvement = timer;
}

Que fait cette méthode ? Tout d’abord, elle définit les coordonnées d’un point à (11, 7) qui ne sera autre que la valeur de déplacement du centre de notre balle : on aurait pu choisir un autre couple de valeurs.
Arrive enfin le moment tant attendu de la création de notre Timer. Il fonctionne de la manière suivante : toute les « vitesse »( ici =0,04) secondes, il appelle la méthode « topHorloge ».

- (void)topHorloge{
    imageBalle.center=CGPointMake(imageBalle.center.x+coordonnees.x, imageBalle.center.y+coordonnees.y);
    if(imageBalle.center.x-25 < 0 || imageBalle.center.x +25 > 320)
        coordonnees.x = -coordonnees.x;
    if(imageBalle.center.y-25 < 0 || imageBalle.center.y +25 > 360)
        coordonnees.y = -coordonnees.y;
}

Bienvenue dans le coeur de l’animation de notre balle. Ici, on redéfinit le centre de la balle à partir de ses coordonnées actuelles + un déplacement grâce au CGPointMake coordonnées. Les deux structures conditionnelles qui suivent, servent à fixer les limites de la zone de jeu sans quoi la balle se volatiliserait. A chaque fois que la balle touche un bord, on inverse le signe de notre déplacement.
A noter que nous gérons le rebond sur les côtés de la balle, et non en son centre : cela se concrétise par  imageBalle.center.x-25, où 25 n’est autre que le rayon de notre pomme.

Normalement, à ce niveau, tu commences à mieux comprendre comment fonctionne l’animation de la balle. D’ailleurs, tu peux essayer de modifier les quelques valeurs, pour voir par toi-même comment va réagir la balle : vitesse, coordonnées, …
Concernant les Timers, je vais te donner un autre exemple d’implémentation d’un timer qui peut te permettre notamment de compter les secondes.

-(void)topDepartChrono: (NSTimer*)timer{
    timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(topChrono) userInfo:nil repeats:YES];
    timertemps = timer;
}

- (void)topChrono{
    temps = temps +1; // ou temps ++;
    labelTime.text = [[NSString alloc] initWithFormat:@"time : %d", temps];
    NSLog(@"compteur temps");
}

On appelle le timer comme suit :

[self topDepartChrono: timertemps];

Et voilà, le tour est joué !

3) Arrêter/Redémarrer un Timer

D’abord, on va retourner sous interface builder afin d’ajouter un bouton Stop dans la zone laissée vide. Je te laisse relier le tout sous Xcode et faire les bonnes connections sous IB.

Bien sûr, nous allons mettre en place un IBAction sur notre bouton, on le déclare donc sous Xcode et le relie au bouton dans l’interface builder. Voici comment on va implémenter tuto1ViewController.m :

BOOL appui = NO;
 
 
-(IBAction)appuiStart : (id)sender{
if(appui) {
appui = NO;
[timermouvement invalidate];
timermouvement = nil;
[buttonStartStop setTitle: @"Start" forState:UIControlStateNormal];
[buttonStartStop setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
}
else {
[self topDepartMouvement: timermouvement];
appui = YES;
[buttonStartStop setTitle: @"Stop" forState:UIControlStateNormal];
[buttonStartStop setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
}
}

Penser aussi à déclarer IBAction dans tuto1ViewController.h.

Et si on expliquait un peu ce bout de code :
On initialise une variable « appui » à NO, elle va nous servir à distinguer l’état de la balle : en mouvement ou à l’arrêt. A chaque fois qu’on appuie sur le bouton start/stop ; si « appui » = NO, on désactive le Timer et on switch le titre sur le bouton , mais si « appui » = YES, on recréé un timer et on switch à nouveau le titre du bouton.

Je pense qu’on en a appris assez pour ce tutoriel. Les timers sont très fréquemment utilisés dans les applications, il n’était donc pas inutile d’aborder le sujet.

heyfeel


Exercice : aller plus loin …

Si tu veux te perfectionner un peu plus et mettre à profit toutes les compétences acquises jusque là, je te propose de créer 3 niveaux de difficultés en faisant varier la vitesse de la balle.