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

Un livre de Wikilivres.
Contenu supprimé Contenu ajouté
NewBorn (discussion | contributions)
NewBorn (discussion | contributions)
Ligne 89 : Ligne 89 :
=== Objets Pygame de base ===
=== Objets Pygame de base ===


Comme vous pouvez le constater, le code se divise en trois catégories principales : la fenêtre d'affichage ''(screen)'', l'arrière-plan ''(background)'' et le texte ''(text)''. Chacun de ces objets a pu être créé grâce à l'appel en premier lieu de la méthode <tt>pygame.init()</tt>, que nous avons modifiée ensuite pour qu'elle convienne à nos besoins. La fenêtre d'affichage est un cas un peu spécial, car elle modifie l'affichage à travers les appels <tt>pygame</tt>, plutôt que d'appeler les méthodes appartenant aux objets de l'écran. Mais pour tous les autres objets Pygame, nous créons d'abord l'objet comme une copie d'un objet Pygame, en lui affectant certains attributs, et développons les objets de notre jeu à partir de celui-ci.
Comme vous pouvez le constater, le code se divise en trois catégories principales : la fenêtre d'affichage (<tt>screen</tt>), l'arrière-plan (<tt>background</tt>) et le texte (<tt>text</tt>). Chacun de ces objets a pu être créé grâce à l'appel en premier lieu de la méthode <tt>pygame.init()</tt>, que nous avons modifiée ensuite pour qu'elle convienne à nos besoins. La fenêtre d'affichage est un cas un peu spécial, car elle modifie l'affichage à travers les appels <tt>pygame</tt>, plutôt que d'appeler les méthodes appartenant aux objets de l'écran. Mais pour tous les autres objets Pygame, nous créons d'abord l'objet comme une copie d'un objet Pygame, en lui affectant certains attributs, et développons les objets de notre jeu à partir de celui-ci.


Pour l'arrière-plan, nous créons d'abord un objet Surface et lui donnons la taille de la fenêtre. Nous utilisons ensuite la méthode <tt>convert()</tt> pour convertir la Surface en un unique [[w:Espace colorimétrique|Espace colorimétrique]]. C'est particulièrement recommandé lorsque nous manipulons plusieurs images et surfaces, toutes dans un espace colorimétrique différent, sinon cela ralentirait de beaucoup le rendu. En convertissant toutes les surfaces, nous pouvons accélérer drastiquement les temps de rendu. Enfin nous remplissons la surface d'arrière-plan en blanc (255, 255, 255). Ces valeurs sont en RGB et nous pouvons les retrouver à partir de n'importe quel bon programme de dessin.
Pour l'arrière-plan, nous créons d'abord un objet Surface et lui donnons la taille de la fenêtre. Nous utilisons ensuite la méthode <tt>convert()</tt> pour convertir la Surface en un unique [[w:Espace colorimétrique|espace colorimétrique]]. C'est particulièrement recommandé lorsque nous manipulons plusieurs images et surfaces, toutes dans un espace colorimétrique différent, sinon cela ralentirait de beaucoup le rendu. En convertissant toutes les surfaces, nous pouvons accélérer drastiquement les temps de rendu. Enfin nous remplissons la surface d'arrière-plan en blanc (255, 255, 255). Ces valeurs sont en RGB et nous pouvons les retrouver à partir de n'importe quel bon programme de dessin.


En ce qui concerne le texte, nous avons besoin de plus d'un objet. D'abord nous créons un objet <tt>font</tt>, qui définira quelle police nous utiliserons, ainsi que sa taille. Ensuite nous créons un objet <tt>text</tt>, en utilisant la méthode de rendu de notre objet <tt>font</tt> et en lui fournissant trois arguments : le texte à faire le rendu, qui sera ou non anti-aliasé (1=oui, 0= non), ainsi que la couleur du texte (toujours dans un format RGB). Ensuite nous créons un troisième objet texte qui fournira le rectangle du texte. La manière la plus simple à comprendre est de s'imaginer en train de dessiner un rectangle qui englobera tout le texte. Vous pourrez alors utiliser ce rectangle afin d'obtenir ou de définir la position du texte sur la fenêtre d'affichage. Ainsi dans cet exemple nous avons le rectangle, et définissons ses attributs <tt>centerx</tt> et <tt>centery</tt> pour correspondre aux <tt>centerx</tt> et <tt>centery</tt> de l'arrière-plan, alors le texte aura le même centre que l'arrière-plan. Dans cet exemple, le texte sera centré sur les axes <tt>x</tt> et <tt>y</tt> de la fenêtre d'affichage.
En ce qui concerne le texte, nous avons besoin de plus d'un objet. D'abord nous créons un objet <tt>font</tt>, qui définira quelle police nous utiliserons, ainsi que sa taille. Ensuite nous créons un objet <tt>text</tt>, en utilisant la méthode de rendu de notre objet <tt>font</tt> et en lui fournissant trois arguments : le texte à faire le rendu, qui sera ou non anti-aliasé (1=oui, 0= non), ainsi que la couleur du texte (toujours dans un format RGB). Ensuite nous créons un troisième objet texte qui fournira le rectangle du texte. La manière la plus simple à comprendre est de s'imaginer en train de dessiner un rectangle qui englobera tout le texte. Vous pourrez alors utiliser ce rectangle afin d'obtenir ou de définir la position du texte sur la fenêtre d'affichage. Ainsi dans cet exemple nous avons le rectangle, et définissons ses attributs <tt>centerx</tt> et <tt>centery</tt> pour correspondre aux <tt>centerx</tt> et <tt>centery</tt> de l'arrière-plan, alors le texte aura le même centre que l'arrière-plan. Dans cet exemple, le texte sera centré sur les axes <tt>x</tt> et <tt>y</tt> de la fenêtre d'affichage.


===Blitting===
=== Blitting ===

===The event loop===
Maintenant que nous avons créé les objets de notre jeu, nous avons besoin d'en faire le rendu. Si nous ne le faisons pas et que nous exécutons le programme, nous ne verrons q'une fenêtre blanche, et nos objets resterons invisibles. Le terme employé pour faire un rendu des objets est le ''blitting'' (que nous franciserons par ''blit''), qui correspond à la copie de pixels d'un objet source vers un objet de destination. Ainsi pour faire une rendu de l'objet <tt>background</tt>, vous le blitez sur l'objet <tt>screen</tt>. Dans cet exemple, pour faire les choses simples, nous blitons le texte sur l'arrière-plan (donc l'arrière-plan possède une copie du texte sur lui), et ensuite nous blitons l'arrière-plan sur l'écran.
===Ta-da!===

Le blit est une des opérations les plus lentes dans un jeu, vous devez donc faire attention à ne pas trop faire de blit sur l'écran pour chaque image. Par exemple, si vous avez une image d'arrière-plan, et une balle se déplaçant à travers l'écran, alors vous pouvez bliter l'arrière-plan en entier et ensuite la balle, tout ceci à chaque image, ce qui recouvrira la position précédente de la balle et fera un rendu de la nouvelle balle, mais ce sera plutôt lent. Une meilleure solution consiste à bliter une partie de l'arrière-plan sur la zone occupée par la balle à l'image précédente, qui peut être trouvée grâce au <tt>rect</tt> de la balle précédente, et ensuite bliter la nouvelle balle, ce qui aura pour effet de bliter seulement deux petites zones.

=== La boucle d'évènement ===

Une fois que vous avez défini le jeu, vous avez besoin de le mettre dans une boucle qui s'exécutera en continu jusqu'à ce que l'utilisateur signale qu'il veuille quitter. Vous démarrerez donc une boucle ouverte, et à chaque itération de la boucle, qui sera chaque image du jeu, vous actualiserez le jeu. La première chose à contrôler pour chaque évènement, est de savoir si l'utilisateur à enfoncé une touche du clavier, cliqué un bouton de la souris, déplacé le joystick, redimensionné la fenêtre, ou tenté de la fermée. Dans ce cas, nous voudrons simplement examiner si l'utilisateur à essayer de fermer la fenêtre, auquel cas le jeu engendrera un <tt>return</tt>, ce qui terminera la boucle <tt>while</tt>. Alors nous aurons simplement besoin de re-bliter l'arrière-plan, et faire un ''flip'' (actualisation de l'affichage) de l'écran pour que chaque chose soit redessinée. D'accord, étant donné que rien ne se passe ou se déplace dans cet exemple, nous n'avons aucunement besoin de re-bliter l'arrière-plan à chaque itération, mais je le met parce que si certaines choses se déplacent à travers l'écran, vous aurez besoin de faire tous vos blits ici.

=== Ta-da! ===

Et voilà, votre jeu Pyagme le plus basique. Tous les jeux prendront une forme similaire, mais avec beaucoup plus de code concernant les fonctions de jeu elles-mêmes, celles-ci seront plus à coder par vous au moment de la programmation, et seront moins guidée dans leur structure par le fonctionnement de Pygame. C'est la vraie raison de ce tutoriel, et nous rentrons maintenant dans le vif du sujet.


==Kicking things off==
==Kicking things off==

Version du 9 mars 2008 à 17:30

Cette page est considérée comme une ébauche à compléter . Si vous possédez quelques connaissances sur le sujet, vous pouvez les partager en éditant dès à présent cette page (en cliquant sur le lien « modifier »).

Ressources suggérées : http://www.pygame.org/docs/tut/tom/MakeGames.html


Introduction

En premier lieu, je supposerais que vous ayez lu le tutoriel 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 TomPong, 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 TomPong 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 :

  • Le chargement des modules qui sont utilisés dans le jeu : Tous les modules standards, excepté les importations locales des espaces de nommage de Pygame et le module Pygame lui-même.
  • Les classes de manipulation des ressources : La définition des classes gérant la plupart des ressources de base, que sont le chargement des images et des sons, ainsi que les procédures de connexion/déconnexion au réseau, le chargement des sauvegardes de jeu, et toutes les autres ressources que vous pouvez utiliser dans votre jeu.
  • Les classes des objets du jeu : Cette partie devra contenir les définitions de classes pour les objets de votre jeu. Dans l'exemple de Pong, ce sera un objet pour la raquette du joueur (que vous pouvez initialiser plusieurs fois, une pour chaque joueur dans le jeu), et une pour la balle (laquelle peut aussi avoir de multiples instances). Si vous souhaitez avoir un menu sympa pour votre jeu, c'est aussi une bonne idée de faire une classe pour le menu.
  • Toutes les autres fonctions du jeu : Dans cette partie seront contenues toutes les autres fonctions nécessaires à la bonne marche du jeu, comme par exemple celles qui définissent le tableau de scores, la gestion du menu, etc. Tout le code que vous pourriez mettre dans la logique de jeu principal, mais qui rendrait sa logique difficilement lisible et peu cohérente, devrait être contenu dans cette partie. Par exemple, tracer le tableau de scores ne relève pas du jeu en lui-même, cela devrait être fait par une fonction particulière située dans cette partie.
  • L'initialisation du jeu : Cela inclut les objets Pygame eux-mêmes, l'arrière-plan, les objets du jeu (initialisation des instances de classe) et les autres petits morceaux de code que vous pourriez vouloir ajouter.
  • 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 TomPong 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 objects.py, 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 ressources.py. Vous pourrez alors écrire from objects, resources import * pour importer toutes les classes et les fonctions.

Une remarque sur les styles d'écriture

La première chose à laquelle il faut penser, lors de l'approche d'un projet de programmation, est de décider d'un style d'écriture, et de le conserver. Python en lui-même facilite cela, à cause de son interprétation stricte des espaces et de l'indentation, mais cela ne vous empêche pas de choisir la largeur de votre indentation, de quelle manière vous placerez les importations, comment vous allez commenter le code, etc. Vous verrez comment je fais tout cela dans mes exemples de code, mais quelque soit le style que vous adopterez, conservez-le tout au long de votre code. Essayez également de documenter toutes vos classes, et de commenter tous les morceaux de code qui peuvent sembler obscurs. Par ailleurs, il ne sert à rien de commenter ce qui est évident. J'ai vu beaucoup de personnes faire la chose suivante :

player1.score += scoreup	# Add scoreup to player1 score

Ce n'est pas très grave, mais un peu inutile. Un mauvais code est mal agencé, avec des changements aléatoires dans le style d'écriture, et une maigre documentation. Ce mauvais code ne sera pas seulement ennuyeux pour les autres personnes, mais il sera également difficile à maintenir pour vous.

Révision : les fondamentaux de Pygame

Le jeu Pygame de base

Pour la révision (ça ne peut pas faire de mal), et pour s'assurer que vous êtes familier avec la structure d'un programme Pygame standard, je vais brièvement parcourir un programme Pygame simple, qui n'affichera rien de plus qu'une fenêtre avec un peu de texte à l'intérieur, et qui devrait finalement ressembler à ça (naturellement, la décoration de la fenêtre pourra être différente sur votre système) :

Dans cet exemple, le code complet ressemble à ça :

#!/usr/bin/python
# coding: utf-8

import pygame
from pygame.locals import *

def main():
    # Initialisation de la fenêtre d'affichage
    pygame.init()
    screen = pygame.display.set_mode((300, 50))
    pygame.display.set_caption('Programme Pygame de base')

    # Remplissage de l'arrière-plan
    background = pygame.Surface(screen.get_size())
    background = background.convert()
    background.fill((250, 250, 250))

    # Affichage d'un texte
    font = pygame.font.Font(None, 36)
    text = font.render("Salut tout le monde", 1, (10, 10, 10))
    textpos = text.get_rect()
    textpos.centerx = background.get_rect().centerx
    textpos.centery = background.get_rect().centery
    background.blit(text, textpos)

    # Blitter le tout dans la fenêtre
    screen.blit(background, (0, 0))
    pygame.display.flip()

    # Boucle d'évènements
    while 1:
        for event in pygame.event.get():
            if event.type == QUIT:
                return

        screen.blit(background, (0, 0))
        pygame.display.flip()

if __name__ == '__main__': main()

Objets Pygame de base

Comme vous pouvez le constater, le code se divise en trois catégories principales : la fenêtre d'affichage (screen), l'arrière-plan (background) et le texte (text). Chacun de ces objets a pu être créé grâce à l'appel en premier lieu de la méthode pygame.init(), que nous avons modifiée ensuite pour qu'elle convienne à nos besoins. La fenêtre d'affichage est un cas un peu spécial, car elle modifie l'affichage à travers les appels pygame, plutôt que d'appeler les méthodes appartenant aux objets de l'écran. Mais pour tous les autres objets Pygame, nous créons d'abord l'objet comme une copie d'un objet Pygame, en lui affectant certains attributs, et développons les objets de notre jeu à partir de celui-ci.

Pour l'arrière-plan, nous créons d'abord un objet Surface et lui donnons la taille de la fenêtre. Nous utilisons ensuite la méthode convert() pour convertir la Surface en un unique espace colorimétrique. C'est particulièrement recommandé lorsque nous manipulons plusieurs images et surfaces, toutes dans un espace colorimétrique différent, sinon cela ralentirait de beaucoup le rendu. En convertissant toutes les surfaces, nous pouvons accélérer drastiquement les temps de rendu. Enfin nous remplissons la surface d'arrière-plan en blanc (255, 255, 255). Ces valeurs sont en RGB et nous pouvons les retrouver à partir de n'importe quel bon programme de dessin.

En ce qui concerne le texte, nous avons besoin de plus d'un objet. D'abord nous créons un objet font, qui définira quelle police nous utiliserons, ainsi que sa taille. Ensuite nous créons un objet text, en utilisant la méthode de rendu de notre objet font et en lui fournissant trois arguments : le texte à faire le rendu, qui sera ou non anti-aliasé (1=oui, 0= non), ainsi que la couleur du texte (toujours dans un format RGB). Ensuite nous créons un troisième objet texte qui fournira le rectangle du texte. La manière la plus simple à comprendre est de s'imaginer en train de dessiner un rectangle qui englobera tout le texte. Vous pourrez alors utiliser ce rectangle afin d'obtenir ou de définir la position du texte sur la fenêtre d'affichage. Ainsi dans cet exemple nous avons le rectangle, et définissons ses attributs centerx et centery pour correspondre aux centerx et centery de l'arrière-plan, alors le texte aura le même centre que l'arrière-plan. Dans cet exemple, le texte sera centré sur les axes x et y de la fenêtre d'affichage.

Blitting

Maintenant que nous avons créé les objets de notre jeu, nous avons besoin d'en faire le rendu. Si nous ne le faisons pas et que nous exécutons le programme, nous ne verrons q'une fenêtre blanche, et nos objets resterons invisibles. Le terme employé pour faire un rendu des objets est le blitting (que nous franciserons par blit), qui correspond à la copie de pixels d'un objet source vers un objet de destination. Ainsi pour faire une rendu de l'objet background, vous le blitez sur l'objet screen. Dans cet exemple, pour faire les choses simples, nous blitons le texte sur l'arrière-plan (donc l'arrière-plan possède une copie du texte sur lui), et ensuite nous blitons l'arrière-plan sur l'écran.

Le blit est une des opérations les plus lentes dans un jeu, vous devez donc faire attention à ne pas trop faire de blit sur l'écran pour chaque image. Par exemple, si vous avez une image d'arrière-plan, et une balle se déplaçant à travers l'écran, alors vous pouvez bliter l'arrière-plan en entier et ensuite la balle, tout ceci à chaque image, ce qui recouvrira la position précédente de la balle et fera un rendu de la nouvelle balle, mais ce sera plutôt lent. Une meilleure solution consiste à bliter une partie de l'arrière-plan sur la zone occupée par la balle à l'image précédente, qui peut être trouvée grâce au rect de la balle précédente, et ensuite bliter la nouvelle balle, ce qui aura pour effet de bliter seulement deux petites zones.

La boucle d'évènement

Une fois que vous avez défini le jeu, vous avez besoin de le mettre dans une boucle qui s'exécutera en continu jusqu'à ce que l'utilisateur signale qu'il veuille quitter. Vous démarrerez donc une boucle ouverte, et à chaque itération de la boucle, qui sera chaque image du jeu, vous actualiserez le jeu. La première chose à contrôler pour chaque évènement, est de savoir si l'utilisateur à enfoncé une touche du clavier, cliqué un bouton de la souris, déplacé le joystick, redimensionné la fenêtre, ou tenté de la fermée. Dans ce cas, nous voudrons simplement examiner si l'utilisateur à essayer de fermer la fenêtre, auquel cas le jeu engendrera un return, ce qui terminera la boucle while. Alors nous aurons simplement besoin de re-bliter l'arrière-plan, et faire un flip (actualisation de l'affichage) de l'écran pour que chaque chose soit redessinée. D'accord, étant donné que rien ne se passe ou se déplace dans cet exemple, nous n'avons aucunement besoin de re-bliter l'arrière-plan à chaque itération, mais je le met parce que si certaines choses se déplacent à travers l'écran, vous aurez besoin de faire tous vos blits ici.

Ta-da!

Et voilà, votre jeu Pyagme le plus basique. Tous les jeux prendront une forme similaire, mais avec beaucoup plus de code concernant les fonctions de jeu elles-mêmes, celles-ci seront plus à coder par vous au moment de la programmation, et seront moins guidée dans leur structure par le fonctionnement de Pygame. C'est la vraie raison de ce tutoriel, et nous rentrons maintenant dans le vif du sujet.

Kicking things off

The first lines, and loading modules

Resource handling functions

Game object classes

A simple ball class

Diversion 1: Sprites

Diversion 2: Vector physics

User-controllable objects

A simple bat class

Diversion 3: Pygame events

Putting it all together

Let the ball hit sides

Let the ball hit bats

The Finished product