« Pygame/Concevoir des jeux avec Pygame » : différence entre les versions

Retrait bandeau + batte->raquette
(relecture en survol)
(Retrait bandeau + batte->raquette)
{{ébauche|ressources=http://www.pygame.org/docs/tut/tom/MakeGames.html}}
 
{{Pygame}}
 
En premier lieu, je supposerais que vous ayez lu le tutoriel [[Pygame/Chimp - Ligne par ligne|Chimp - Ligne par ligne]], lequel introduit les bases de Python et de Pygame. Dans le cas contraire, prenez-en connaissance avant de lire la suite, car je ne répèterais pas les bases fournies par cet autre tutoriel (en tous cas, pas dans les détails). Ce tutoriel est destiné à ceux qui savent réaliser un petit jeu ''ridiculement'' simple, et qui aimeraient réaliser un petit jeu ''relativement'' simple comme Pong. Il vous fournira une introduction à quelques concepts, comme l'architecture d'un jeu, quelques notions de mathématiques pour le fonctionnement physique de la balle, ainsi que sur la manière de garder votre jeu facile à maintenir et à améliorer.
 
Tout le code de ce tutoriel est utilisé dans ''TomPongTom's Pong'', un jeu que j'ai écrit. A la fin de ce tutoriel, vous devriez non seulement renforcer votre compréhension de Pygame, mais vous devriez aussi comprendre comment TomPongTom's Pong fonctionne, et comment concevoir votre propre version.
 
Maintenant, faisons une brève revue des bases sur l'utilisation de Pygame. Une méthode répandue d'organisation de code pour un jeu est de le diviser en six parties distinctes :
* '''La boucle principale''' : C'est dans cette boucle que vous placerez la gestion des entrées (c'est à dire l'acquisition des évènements utilisateurs que sont les frappes de clavier/bouton de souris), le code de mise à jour des objets du jeu, et finalement la mise à jour de l'écran.
 
Tous les jeux que vous ferez auront certaines, voire la totalité de ces sections, et probablement d'autres de votre propre cru. Dans le cadre de ce tutoriel, je parlerai de la façon dont TomPongTom's Pong est agencé, et de la façon d'appliquer cette organisation à chaque projet de jeu que vous pourriez avoir. Je supposerai également que vous voudriez garder tout le code dans un seul fichier, mais si vous faites un jeu plutôt conséquent en taille de code, c'est souvent une bonne idée de séparer le jeu en plusieurs modules. Mettre les classes des objets du jeu dans un fichier <tt>objects.py</tt>, par exemple, peut vous aider à séparer la logique du jeu de ses objets. Si vous avez énormément de code pour la manipulation des ressources, il peut également être pratique de le mettre dans un module <tt>ressources.py</tt>. Vous pourrez alors écrire <tt>from objects, resources import *</tt> pour importer toutes les classes et les fonctions.
 
=== Une remarque sur les styles d'écriture ===
# Tom's Pong
# A simple pong game with realistic physics and AI
# http://wwwtom.tomchance.uklinuxacrewoods.net/projects/pong.shtml
#
# Released under the GNU General Public License
== Objets contrôlés par l'utilisateur ==
 
Pour l'instant, vous avez créé une fenêtre Pygame, et fait un rendu d'une balle qui se déplace sur l'écran. La prochaine étape est de créer quelques battesraquettes qui soient sous le contrôle de l'utilisateur. C'est potentiellement plus simple que la balle, car çà ne requiert aucune physique. Toutefois ceci ne se vérifie plus lorsque votre objet possède des déplacements plus complexe que haut et bas, par exemple dans un jeu de plateforme comme Mario, auquel cas vous aurez besoin de mettre en jeu de la physique. Les objets contrôlables sont simples à mettre en oeuvre, remerciez Pygame pour son système de file d'évènements, comme vous pourrez le voir.
 
=== Une simple classe Bat ===
 
Le principe derrière la classe Bat est similaire à la classe Ball. Vous avez besoin d'une méthode <tt>__init__()</tt> pour initialiser la batteraquette (vous pourrez donc créer des instances d'objet pour chaque batteraquette), d'une méthode <tt>update()</tt> pour appliquer les changements sur la batteraquette avant de la blitter à l'écran, et diverses autres méthodes qui définiront ce que fait cette classe. Voici un échantillon du code :
 
<source lang="python">
class Bat(pygame.sprite.Sprite):
"""BatteRaquette mobilede B)'tennis' déplaçable qui peut frapper la balle
Retourne: objet bat
Méthode: reinit, update, moveup, movedown
</source>
 
Comme vous pouvez le voir, cette classe est très similaire à la classe Ball dans sa structure. Mais les différences se situent dans ce que font chaque fonction. Tout d'abord, il y a une méthode <tt>reinit()</tt> qui est utilisée lorsqu'un round est terminé : la batteraquette retourne dans sa position de départ, et chacun de ses attributs à ses valeurs d'origine. Ensuite, la manière dont la batteraquette bouge est un peu plus complexe que la balle, étant donné que ses mouvements sont simples (haut/bas) mais dépendent de ce que désire l'utilisateur, tandis que la balle conservera son mouvement à chaque image. Pour mettre en évidence la façon dont la batteraquette bouge, il est pratique d'examiner ce petit diagramme pour voir la séquence des évènements :
 
{| border = "0"
|}
 
C'est ce qui se passe ici si la personne qui contrôle la batteraquette enfonce la touche qui fait se déplacer la batteraquette vers le haut. A chaque itération de la boucle principale du jeu (à chaque image), si la touche est maintenue enfoncée, alors l'attribut <tt>state</tt> de cet objet batteraquette sera paramétré à "moving", et la méthode <tt>moveup()</tt> sera appelée, causant la réduction de la position Y de la batteraquette d'une valeur correspondant à l'attribut <tt>speed</tt> (dans cet exemple 10). En d'autre mots, tant que la touche reste enfoncée, la batteraquette se déplacera à l'écran de 10 pixels par image. L'attribut <tt>state</tt> n'est pas utilisé ici, mais c'est très utile de le connaître si vous désirez appliquer des effets à la balle, ou si vous utilisez une sortie pour le débugage.
 
==== Diversion 3 : évènements Pygame ====
 
Alors, comment allons nous savoir quand le joueur est entrain d'enfoncer la touche, ou la relâche ? Avec le système de file d'évènement de Pygame, pardi! C'est un système vraiment simple à utiliser et à comprendre, çà ne devrait pas être long :) Vous avez déjà observé la file d'évènement en action dans le programme Pygame de base, où elle était utilisée pour vérifier si l'utilisateur voulait quitter l'application. Le code pour déplacer la batteraquette est aussi simple que çà :
 
<source lang="python">
Ici nous supposons que vous avez déjà créé une instance de <tt>Bat</tt>, et appelé l'objet <tt>player</tt>. Vous pouvez observer la couche familière de la structure <tt>for</tt>, qui produit une itération à chaque évènement trouvé dans la file d'évènement de Pygame, eux-même retrouvés grâce à la fonction <tt>event.get()</tt>. L'utilisateur enfonce une touche, appuie sur le bouton de la souris, ou bouge le joystick, toutes ces actions seront placées dans la file d'évènement de Pygame, et conservées jusqu'à leur utilisation. Donc à chaque itération de la boucle de jeu principale, vous irez faire un tour dans ces évènements vérifier s'il y en a quelques uns que vous pouvez utiliser. La fonction <tt>event.pump()</tt> qui était dans la méthode <tt>Bat.update()</tt> est appelée à chaque itération pour ''pomper'' les vieux évènements et garder la file à jour.
 
D'abord nous vérifions si l'utilisateur veut quitter le programme, et si oui on quitte le programme. Ensuite nous vérifions si une touche est enfoncée, et si oui, nous vérifions si elle correspond à une des touches affectée au déplacement de la batteraquette, si oui alors nous appelons la méthode de déplacement appropriée, et définissons l'état du joueur. A travers les états "moveup" et "movedown", modifiés par les méthodes <tt>moveup()</tt> et <tt>movedown()</tt>, nous produisons un code plus soigné et nous ne cassons pas l'''encapsulation'', ce qui signifie que vous assignez des attributs aux objets eux-mêmes, sans se référer au nom de l'instance de cet objet. Remarquez que nous avons 3 états : "still", "moveup" et "mouvedown". Encore une fois, ils deviennent utiles si vous voulez débugguer ou calculer un effet sur la balle. Nous vérifions aussi si une touche est "partie" (si elle n'est plus enfoncée), et alors si c'est la bonne touche, nous stoppons le déplacement de la batteraquette.
 
== Assembler le tout ==
Ensuite nous vérifions si les rectangles entrent en collision, avec une correction de bug de plus. Remarquez que j'ai commenté toute cette partie de code, c'est toujours préférable d'expliquer une partie du code qui parait obscure, que ce soit pour les autres qui regardent votre code, ou pour vous lorsque vous y reviendrez plus tard. Sans la correction du bug, la balle pourra heurter un coin de la raquette, changer de direction, et l'image d'après, trouver qu'elle est toujours à l'intérieur de la raquette. Alors le programme pensera que la balle est frappée une nouvelle fois et rechangera de direction. Cela peut survenir plusieurs fois, ce qui rendrait le comportement de la balle complètement irréaliste. Donc nous avons une variable, <tt>self.hit</tt> qui sera définie à <tt>true</tt> quand elle sera frappée, et à <tt>false</tt> une image plus tard. Quand nous vérifions si les rectangles sont entrés en collision, nous contrôlons si <tt>self.hit</tt> est à <tt>true</tt> ou <tt>false</tt> pour stopper les rebonds internes.
 
L'imposant code ici est facile à comprendre. Tous les rectangle possèdent une fonction <tt>colliderect()</tt>, dans laquelle vous envoyez le rectangle d'un autre objet, qui retournera <tt>1</tt> (<tt>true</tt>) si les rectangles se chevauchent, et <tt>0</tt> (<tt>false</tt>) dans le cas contraire. En cas de réponse positive, nous pouvons changer la direction en soustrayant l'angle actuel par <math> \boldsymbol{\pi}</math> (encore un petit truc que vous pouvez faire avec les radians, qui ajustera l'angle de 90 degrés et renverra la bonne direction. Vous pourrez trouver qu'à ce stade la compréhension approfondie des radians soit un ordreexigeance!). Pour terminer le contrôle du bug, nous repassons <tt>self.hit</tt> à <tt>false</tt> à partir de l'image qui suit la frappe de la balle.
 
Nous réaffectons alors le vecteur. Vous aimeriez bien sur enlever la même ligne dans la précédente partie de code, mais vous ne pourrez le faire qu'après le test conditionnel <tt>if-else</tt>. Et ce sera tout. Le code assemblé permettra maintenant à la balle de frapper les côtés et les raquettes.
Le produit final, avec toutes les parties de code assemblées grâce à du code glu ressemblera à ceci :
 
:[[Pygame/Tom's Pong|Code final de Tom's Pong]]
 
Comme vous avez pu voir le produit fini, je vais vous rediriger vers ''Tom's Pong'', jeu sur lequel est basé ce tutoriel. Téléchargez-le, étudiez le code source, et vous verrez une implémentation de Pong utilisant tout le code que vous avez vu dans ce tutoriel, ainsi que d'autres ajouts fait dans des versions précédentes, comme des propriétés physique pour les effets et ainsi que d'autres résolutions de bugs.
54

modifications