Définition de l'arrière-plan de l'application
Vous allez maintenant commencer à écrire le code de l'application. La première étape va consister à définir l'image d'arrière-plan. Cliquez sur
ViewController.m dans le volet de navigation et insérez l'instruction suivante au début de la méthode
viewDidLoad :
Code : Objective-C | self.view.backgroundColor = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:@"background.png"]];
|
Cette instruction a déjà été rencontrée à plusieurs reprises. Elle affecte une image d'arrière-plan (méthode
backgroundColor) à la vue courante (
self.view) en utilisant une image dans les ressources (
[[UIColor alloc] initWithPatternImage:[UIImage imageNamed:). L'image utilisée a pour nom « background.png ». Libre à vous d'utiliser une quelconque autre image, pourvu que sa taille soit égale à 320x480 pixels.
Vous pouvez lancer l'application. Vous l'avez bien mérité. La figure suivante représente ce que vous devriez obtenir.
N'essayez pas de jouer : nous n'avons pas encore écrit le code pour ça.
Déplacement de la balle
Avant de pouvoir jouer, plusieurs étapes sont nécessaires. Dans un premier temps, vous allez donner vie à la balle. Pour cela, vous allez devoir mettre en place un timer. Mais avant, retournez dans le fichier d'en-têtes et définissez la variable d'instance
vitesseBalle comme suit :
Code : Objective-C | @interface ViewController : UIViewController
{
CGPoint vitesseBalle;
}
|
La variable
vitesseBalle est une structure
CGPoint. Elle consiste en deux flottants qui définissent des coordonnées. Dans notre cas, nous utiliserons cette variable pour calculer l'endroit où la balle doit être affichée. La figure suivante vous montre ce que dit la documentation Apple sur la structure
CGPoint.
Retournez dans le fichier
ViewController.m et définissez la méthode
boucleJeu comme suit :
Code : Objective-C | -(void) boucleJeu
{
balle.center = CGPointMake(balle.center.x + vitesseBalle.x , balle.center.y + vitesseBalle.y);
if (balle.center.x > self.view.bounds.size.width || balle.center.x < 0)
vitesseBalle.x = -vitesseBalle.x;
if (balle.center.y > self.view.bounds.size.height || balle.center.y < 0)
vitesseBalle.y = -vitesseBalle.y;
}
|
Examinons les instructions utilisées dans cette méthode.
La ligne 3 calcule les prochaines coordonnées de la balle (
balle.center). Ces coordonnées sont obtenues en ajoutant aux coordonnées actuelles (
balle.center.x et
balle.center.y) le déplacement désiré (
vitesseBalle.x et
vitesseBalle.y). Tout ce petit monde est passé à une instruction
CGPointMake, qui retourne les nouvelles coordonnées de la balle (
CHPointMake(…)). Pour que la balle se déplace, il suffit d'affecter le résultat de l'instruction
CGPointMake à la propriété
center de l'objet
balle, c'est-à-dire au centre de la balle :
Code : Objective-C | balle.center = CGPointMake(balle.center.x + vitesseBalle.x , balle.center.y + vitesseBalle.y);
|
L'instruction suivante s'intéresse à l'abscisse (la position horizontale) de la balle qui, rappelons-le, vient d'être calculée. Si cette abscisse est supérieure à la largeur de la vue (
balle.center.x > self.view.bounds.size.width) ou si elle est négative (
|| balle.center.x < 0), il suffit d'inverser la direction en X de la balle pour qu'elle ne sorte pas de la vue (
vitesseBalle.x = -vitesseBalle.x;). Ce qui donne :
Code : Objective-C | if (balle.center.x > self.view.bounds.size.width || balle.center.x < 0)
vitesseBalle.x = -vitesseBalle.x;
|
Examinez l'instruction suivante. Je suis sûr que vous y voyez quelques similitudes :
Code : Objective-C | if (balle.center.y > self.view.bounds.size.height || balle.center.y < 0)
vitesseBalle.y = -vitesseBalle.y;
|
Ici, c'est l'ordonnée (la position verticale) qui est examinée. Si elle dépasse la hauteur de la vue (
balle.center.y > self.view.bounds.size.height) ou si elle est négative (
|| balle.center.y < 0), la direction en Y est inversée pour que la balle ne sorte pas de la vue (
vitesseBalle.y = -vitesseBalle.y).
Tout cela est bien joli, mais dans quel sens va partir la balle ? Je crois bien que la direction de la balle n'a pas été définie au lancement de l'application.
Effectivement. Nous allons immédiatement réparer cette lacune en ajoutant l'instruction suivante dans la méthode
viewDidLoad :
Code : Objective-C | vitesseBalle = CGPointMake(10,15);
|
Cette instruction définit l'objet
CGPoint vitesseBalle et affecte les valeurs
10 et
15 à ses composantes
X et
Y. Par la suite, nous utiliserons cet objet pour modifier les coordonnées de la balle… et donc la déplacer.
Je vous sens impatients de lancer l'application pour voir la balle se déplacer dans la vue. Je ne vais pas vous priver de ce plaisir. Cliquez sur
Run et admirez… admirez… cette belle balle immobile au milieu de la vue !
Que se passe-t-il ? J'ai pourtant respecté à la lettre tout ce qui a été dit jusqu'ici !
Réfléchissez un peu. Vous avez défini une méthode pour animer la balle, mais croyez-vous que cette méthode va s'exécuter toute seule ? Bien sûr que non, il faut le lui demander en mettant en place un timer. Ajoutez l'instruction suivante dans la méthode
viewDidLoad :
Code : Objective-C | timer1 = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(boucleJeu) userInfo:nil repeats:YES];
|
Cette instruction définit l'objet
timer1 (
timer1 =) en utilisant un message Objective-C. Le message dit en substance ceci :
- le code du timer doit être exécuté toutes les 0,05 seconde, soit 20 fois par seconde : [NSTimer scheduledTimerWithTimeInterval:0.05 ;
- la méthode à exécuter se trouve dans la classe courante, c'est-à-dire celle du contrôleur de vue : target:self ;
- la méthode à exécuter a pour nom boucleJeu : selector:@selector(boucleJeu) ;
- aucune information complémentaire n'est passée à cette méthode : userInfo:nil ;
- la méthode doit se répéter indéfiniment (ou du moins jusqu'à ce qu'il soit désactivé) : repeats:YES].
Pensez également à déclarer l'objet
timer dans le fichier
ViewController.h :
Code : Objective-C | @interface ViewController : UIViewController
{
CGPoint vitesseBalle;
NSTimer *timer1;
}
|
Maintenant, vous pouvez lancer l'application et voir (oh merveille !) la balle se déplacer et rebondir sur les coins de l'écran (et votre doigt sur le device).
Déplacement de la raquette
Vous allez maintenant donner vie à la raquette et lui demander de suivre la souris dans le simulateur.
Définissez la méthode suivante dans le fichier
ViewController.m (par exemple, juste après la méthode
boucleJeu) :
Code : Objective-C | -(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
CGPoint location = [touch locationInView:touch.view];
raquette.center = CGPointMake(location.x,raquette.center.y);
}
|
Examinons les instructions contenues dans cette méthode.
À la ligne 3, l'objet
touch de classe
UITouch est défini (
UITouch *touch). Il est initialisé avec toutes les actions toucher de l'utilisateur (
= [[event allTouches] anyObject];) :
Code : Objective-C | UITouch *touch = [[event allTouches] anyObject];
|
La ligne 4 définit l'objet location de classe
CGPoint (
CGPoint location) et l'initialise avec la position du toucher (
= [touch locationInView:touch.view];) :
Code : Objective-C | CGPoint location = [touch locationInView:touch.view];
|
La variable
location contient la position du doigt de l'utilisateur dans la vue. L'instruction de la ligne 5 affecte l'abscisse de cette position à celle de la raquette (
raquette.center = CGPointMake(location.x, …). L'ordonnée de la raquette, quant à elle, ne doit pas suivre le doigt de l'utilisateur, mais rester sur une ligne horizontale (
raquette.center.y) :
Code : Objective-C | raquette.center = CGPointMake(location.x,raquette.center.y);
|
Lancez l'application en cliquant sur
Run. La raquette doit maintenant suivre les mouvements de la souris, bouton gauche enfoncé. Quelle réussite !
Lisez vite la suite, vous allez découvrir comment détecter des collisions entre deux objets. Cela vous permettra de faire rebondir la balle sur la raquette et d'effacer les briques sur lesquelles rebondit la balle.
Détection des collisions
Comme nous l'avons vu précédemment, il est possible de connaître les coordonnées d'un objet en utilisant les propriétés
center.x et
center.y de cet objet. Par exemple, les coordonnées de la balle sont obtenues avec les expressions
balle.center.x et
balle.center.y.
Est-ce que vous savez comment détecter la collision entre deux objets ? En testant leurs coordonnées
center.x et
center.y bien entendu ! Encore plus fort : vous pouvez utiliser la méthode
CGRectIntersectsRect pour tester la collision entre deux objets.
Voyons comment tester le contact de la balle sur la première brique. Ajoutez le test suivant dans la méthode
boucleJeu :
Code : Objective-C | if ((CGRectIntersectsRect(balle.frame,brique1.frame)) && (brique1.hidden == NO))
{
brique1.hidden=YES;
vitesseBalle.y = -vitesseBalle.y;
}
|
La première ligne teste la collision (
CGRectIntersectsRect) entre la balle (
balle.frame) et la première brique (
brique1.frame), dans le cas où cette brique est toujours affichée (
brique1.hidden == NO) :
Code : Objective-C | if ((CGRectIntersectsRect(balle.frame,brique1.frame)) && (brique1.hidden == NO))
|
La troisième ligne cache la première brique :
Code : Objective-C
Quant à la quatrième ligne, elle inverse le sens de progression vertical de la balle :
Code : Objective-C | vitesseBalle.y = -vitesseBalle.y;
|
La brique 1 n'est pas la seule présente dans la vue. Vous devez donc écrire un code équivalent pour les briques 2 à 7 :
Code : Objective-C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 | if ((CGRectIntersectsRect(balle.frame,brique2.frame)) && (brique2.hidden == NO))
{
brique2.hidden=YES;
vitesseBalle.y = -vitesseBalle.y;
}
if ((CGRectIntersectsRect(balle.frame,brique3.frame)) && (brique3.hidden == NO))
{
brique3.hidden=YES;
vitesseBalle.y = -vitesseBalle.y;
}
if ((CGRectIntersectsRect(balle.frame,brique4.frame)) && (brique4.hidden == NO))
{
brique4.hidden=YES;
vitesseBalle.y = -vitesseBalle.y;
}
if ((CGRectIntersectsRect(balle.frame,brique5.frame)) && (brique5.hidden == NO))
{
brique5.hidden=YES;
vitesseBalle.y = -vitesseBalle.y;
}
if ((CGRectIntersectsRect(balle.frame,brique6.frame)) && (brique6.hidden == NO))
{
brique6.hidden=YES;
vitesseBalle.y = -vitesseBalle.y;
}
if ((CGRectIntersectsRect(balle.frame,brique7.frame)) && (brique7.hidden == NO))
{
brique7.hidden=YES;
vitesseBalle.y = -vitesseBalle.y;
}
|
Si vous voulez écrire un code plus condensé, vous pouvez remplacer ces sept blocs if par une boucle. Je n'ai pas choisi cette voie pour ne pas vous embrouiller : il y a déjà tant de nouveautés dans ce chapitre !
Exécutez l'application. Sous vos yeux ébahis, les briques disparaissent les unes après les autres !
Il nous reste encore à détecter la collision entre la balle et la raquette et à réagir en conséquence.
Ajoutez le test suivant dans la méthode
boucleJeu :
Code : Objective-C | If (CGRectIntersectsRect(balle.frame,raquette.frame))
{
if (balle.center.y < raquette.center.y)
vitesseBalle.y = -vitesseBalle.y;
}
|
La ligne 1 teste la collision entre la balle (
balle.frame) et la raquette (
raquette.frame) :
Code : Objective-C | if(CGRectIntersectsRect(balle.frame,raquette.frame))
|
La ligne 3 teste si la balle se trouve plus bas que la raquette :
Code : Objective-C | if (balle.center.y < raquette.center.y)
|
Dans ce cas, il convient d'inverser le sens de progression verticale de la balle pour qu'elle ne sorte pas de la vue :
Code : Objective-C | vitesseBalle.y = -vitesseBalle.y;
|
Pour l'instant, nous n'allons pas gérer la perte de la balle. Cela se fera un peu plus loin dans le tutoriel.
Exécutez l'application. Ça y est, la balle rebondit sur la raquette. Bravo !
Il est temps de passer à l'étape suivante.
Musique de fond et bruits événementiels
Pour ajouter une musique de fond à l'application, vous utiliserez la technique étudiée dans la section
« Jouer des éléments audio » du chapitre consacré au son. Reportez-vous à cette section pour avoir des renseignements détaillés sur sa mise en œuvre.
Une musique de fond, c'est bien, mais ce serait vraiment mieux si la balle produisait un bruit de balle lorsqu'elle frappe sur la raquette ou sur une brique. Pour cela, vous allez devoir utiliser un objet
SystemSoundID.
Télécharger le son
Ajoutez un son au format CAF dans les ressources de l'application.
Cliquez sur le fichier
ViewController.h dans le volet de navigation et ajoutez l'instruction suivante dans la définition de l'interface :
Code : Objective-C
Rendez-vous dans le fichier
ViewController.m et ajoutez cette instruction dans la méthode
viewDidLoad :
Code : Objective-C | AudioServicesCreateSystemSoundID(CFBundleCopyResourceURL(CFBundleGetMainBundle(), CFSTR("pong"), CFSTR("caf"), NULL), &bruit);
|
Cette instruction met en relation le fichier audio à jouer (« pong.caf » dans cet exemple) et un objet
SystemSoundID qui sera utilisé pour jouer le son (
bruit dans cet exemple).
Maintenant, vous devez encore ajouter quelques instructions pour jouer le son « pong.caf » lorsque la balle entre en contact avec la raquette ou avec une brique.
Repérez le test de collision entre la balle et la raquette dans la méthode
boucleJeu et complétez-le comme suit :
Code : Objective-C | if(CGRectIntersectsRect(balle.frame,raquette.frame))
{
if(balle.center.y < raquette.center.y)
{
vitesseBalle.y = -vitesseBalle.y;
AudioServicesPlaySystemSound(bruit);
}
}
|
La ligne 6 joue le son « bruit »
via la méthode
AudioServicesPlaySystemSound.
Vous allez maintenant ajouter la même instruction dans les tests de collision entre la balle et chacune des briques. Voici par exemple à quoi doit ressembler le test de collision entre la balle et la brique 1 :
Code : Objective-C | if ((CGRectIntersectsRect(balle.frame,brique1.frame)) && (brique1.hidden == NO))
{
brique1.hidden=YES;
vitesseBalle.y = -vitesseBalle.y;
AudioServicesPlaySystemSound(bruit);
}
|
Insérez cette même instruction dans les six autres tests de collision.
Perte de la balle
Je ne sais pas si vous avez remarqué, mais ce jeu présente un (petit) défaut : si le joueur n'arrive pas à rattraper la balle avec la raquette, celle-ci rebondit sur la partie inférieure de l'écran. Que diriez-vous d'ajouter un traitement relatif à la perte de la balle ? Par exemple… émettre un rire pour augmenter le stress du joueur.
Télécharger le son
Commencez par ajouter un son au format
caf dans les ressources de l'application. Pour que ce son puisse être joué, vous devez définir un objet
SystemSoundID et le relier à ce son. Ajoutez l'instruction suivante dans la partie interface du fichier
ViewController.h :
Code : Objective-C
Vous allez maintenant agir sur le code. Cliquez sur
ViewController.h dans le volet de navigation et ajoutez l'instruction suivante un peu avant la fin de la méthode
viewDidLoad :
Code : Objective-C | AudioServicesCreateSystemSoundID(CFBundleCopyResourceURL(CFBundleGetMainBundle(), CFSTR("rire"), CFSTR("caf"), NULL), &rire);
|
Pour savoir si le joueur a laissé passer la balle, ajoutez le test suivant dans la méthode
boucleJeu :
Code : Objective-C | if (balle.center.y > self.view.bounds.size.height)
{
AudioServicesPlaySystemSound(rire);
balle.center = CGPointMake(100.0f, 100.0f);
}
|
Si l'ordonnée de la balle (
balle.center.y) est supérieure à la hauteur de la vue (
self.view.bounds.size.height), les instructions comprises entre les accolades sont exécutées.
L'instruction de la ligne 3 émet un son pour signifier la perte de la balle :
Code : Objective-C | AudioServicesPlaySystemSound(rire);
|
Quant à l'instruction de la ligne 4, elle repositionne la balle pour poursuivre la partie :
Code : Objective-C | balle.center = CGPointMake(100.0f, 100.0f);
|
La touche finale
Pour terminer cette application, nous allons afficher un message dans le
Label lorsque toutes les briques ont été effacées et mettre fin à la boucle de jeu. Rendez-vous dans le fichier
ViewController.m et ajoutez le bloc d'instructions suivant dans la méthode
boucleJeu :
Code : Objective-C | if ((brique1.hidden == YES) && (brique2.hidden == YES) && (brique3.hidden == YES) && (brique4.hidden == YES) && (brique5.hidden == YES) && (brique6.hidden == YES) && (brique7.hidden == YES))
{
stop3s.text = @"Bravo !";
[timer1 invalidate];
}
|
Ça y est, l'application est entièrement fonctionnelle. Amusez-vous bien !