1. Pong

Pong est un des premiers jeux vidéo d’arcade et le premier jeu vidéo d’arcade de sport. Il a été imaginé par l’Américain Nolan Bushnell et développé par Allan Alcorn, et la société Atari le commercialise à partir de novembre 1972. Bien que d’autres jeux vidéo aient été inventés précédemment, comme Computer Space, Pong est le premier à devenir populaire.

Le jeu est inspiré du tennis de table en vue de dessus, et chaque joueur s’affronte en déplaçant la raquette virtuelle de haut en bas, via un bouton rotatif, de façon à garder la balle dans le terrain de jeu. Le joueur peut changer la direction de la balle en fonction de l’endroit où celle-ci tape sur la raquette, alors que sa vitesse augmente graduellement au cours de la manche. Un score est affiché pour la partie en cours et des bruitages accompagnent la frappe de la balle sur les raquettes.

Pong est à l’origine un exercice demandé par Bushnell à Alcorn en guise d’entraînement. Une version similaire a été créée précédemment par Ralph Baer pour la console de jeu Odyssey de Magnavox, mais son existence reste peu connue. Surpris par la qualité du résultat, Bushnell et Ted Dabney, fondateurs d’Atari, décident de commercialiser le jeu dès 1972. La copie du concept entraîne d’ailleurs une poursuite en justice de Magnavox contre Atari pour violation de brevet en 1976.

Mise sur le marché fin 1972, la borne d’arcade est un succès : elle est vendue à près de 8 000 exemplaires l’année suivante et engrange jusqu’à 40 millions de dollars de chiffres d’affaires en 1975, dépassant toutes les prédictions de Bushnell et Dabney. Ce succès incite de nombreuses sociétés à se lancer dans le jeu vidéo en copiant le concept, notamment sur console de salon. À la suite des bons chiffres de la borne d’arcade et de l’engouement généré par les concurrents pour le jeu de salon, Pong est porté sur une console de salon dédiée, sous le nom Home Pong à partir de 1975, commercialisée par Sears puis directement par Atari un an après. Ce double succès est considéré comme l’évènement précurseur de l’industrie du jeu vidéo, avec une forte augmentation de l’offre, des centaines de consoles de salon reprenant le concept.

— wikipedia
https://fr.wikipedia.org/wiki/Pong
pong

2. Objectif

Notre objectif est de développer une version Python de ce jeu en s’appuyant sur les concepts de programmation que nous avons appris jusqu’à maintenant :

  • Utilisation de modules

  • les fonctions

  • les dictionnaires

  • les tests conditionnels

  • les boucles

Notre jeu se limitera à un joueur unique. Cette version du jeu est connue sous le nom Pong Survivor ou Single Player Pong

Le développement du jeu est basé sur l’utilisation de la bibliothèque Pygame

myPong

3. Au travail…​

  • Sur github classroom, clonez le dépot PythonSinglePlayerPong : https://classroom.github.com/a/P-ZwpAK5

  • Créez un nouveau projet associé à votre dépot dans votre environnement de développement sur Pycharm ou sur repl.it (new repl Python

  • Ne pas oublier de comiter puis de pousser le code après chaque étape du développement.

3.1. Pygame

3.1.1. Qu’est ce que c’est ?

Pygame est une bibliothèque libre multiplate-forme qui facilite le développement de jeux vidéo temps réel avec le langage de programmation Python.

Elle est distribuée selon les termes de la licence GNU LGPL.

Construite sur la bibliothèque SDL, elle permet de programmer la partie multimédia (graphismes, son et entrées au clavier, à la souris ou au joystick), sans se heurter aux difficultés des langages de bas niveaux comme le C et ses dérivés. Cela se fonde sur la supposition que la partie multimédia, souvent la plus contraignante à programmer dans un tel jeu, est suffisamment indépendante de la logique même du jeu pour qu’on puisse utiliser un langage de haut niveau (en l’occurrence le Python) pour la structure du jeu.

Pygame, en plus d’adapter la SDL au Python, fournit également un petit nombre de fonctions spécifiques au développement de jeux.

On peut aussi remarquer que Pygame n’est plus utilisée exclusivement pour des jeux vidéo, mais également pour des applications diverses nécessitant du graphisme.

— wikipedia
https://fr.wikipedia.org/wiki/Curses
Pygame Logo

3.1.2. Utilisation

Utilisation basic de Pygame

Les méthodes principales sont très simples :

  • init() initialise le module Pygame.

  • display module qui permet de gérer l’affichage de la fenêtre de jeu

    • display.set_mode(largeur, hauteur) initialisation de la fenêtre de jeu

    • display.set_caption(title) Titre de la fenêtre de jeu

    • display.update() met à jour la fenêtre de jeu

  • draw module qui permet de dessiner des formes élémentaires

    • draw.circle(screen, color, (x, y), r) dessiner un cercle dans la fenêtre screen de rayon r à la position x, y pour son centre et rempli de couleur color

    • draw.rect(screen, color, (x, y, w, h) dessiner un rectangle dans la fenêtre screen de largeur w, de hauteur h à la position x, y pour le coin supérieur gauche et rempli de couleur color

  • fill(couleur) remplissage d’une surface avec une couleur

  • event.get() Fourni les événements pour le contrôle du jeu

3.1.3. Codage d’une fenêtre de jeu

  • Ouvrez le fichier pong1.py

  • Modifiez le programme pour que la fenêtre ait pour dimensions 600x400

  • Modifez le titre de la fenêtre : "My Single Player Pong"

  • Changez la couleur du fond en noir

Le jeu est contenu dans une boucle qui s’achève lorsque le booléen end est vrai, ce qui arrivera lorsque le joueur cliquera sur la croix de fermeture de la fenêtre (événement QUIT).

import pygame

pygame.init()

WIDTH = 300
HEIGHT = 200
screen = pygame.display.set_mode((WIDTH, HEIGHT))

pygame.display.set_caption('My Game')

WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 255)

screen.fill(RED)
pygame.display.update()

end = False
while not end:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            end = True

pygame.quit()

3.2. Ajouter une forme

Pygame permet de dessiner des formes élémentaires comme des rectangles, des cercles, …​

Nous allons maintenant dessiner un cercle qui servira plus tart à la représentation de la balle :

  • Ouvrez le fichier pong2.py

  • Ajouter un cercle rempli de couleur blanche de rayon radius, en plein centre de la fenêtre de jeu.

radius = 25
pygame.draw.circle(___, ___, (___, ___), ___)

3.3. Gérer les événements

Le contôle du jeu est assuré par les touches de direction LEFT, RIGHT et Escape pour quitter la partie en cours. On ajoute généralement une touche pour assurer la mise en pause du jeu. Ce rôle est souvent attribué à la touche SPACE. On utilisera également la touche ENTER pour reprendre la partie.

Commençons par découvrir comment acquérir et gérer les événements liés à l’utlisation des touches :

3.3.1. Gestion des touches du clavier

Le type d’événement créé lorsque l’on appuie sur une touche est KEYDOWN, (ou KEYUP au relâchement de la touche).

Le module key possède une méthode get_pressed() qui fourni un tableau de valeurs à 0 pour une touche non appuyée et 1 pour une touche appuyée.

Il fourni également des constantes qui identifies toutes les touches du clavier :

Lettres:
K_a ... K_z

Nombres:
K_0 ... K_9
key = pygame.key.get_pressed()
if key[pygame.K_a]:
    # do something
  • Ouvrez le fichier pong3.py

  • Codez l’utilisation des touches A, Z, Q et S pour déplacer le cercle dans la fenêtre de jeu :

    • A : dans le coin supérieur gauche

    • Z : dans le coin supérieur droit

    • Q : dans le coin inférieur gauche

    • S : dans le coin inférieur droit

eventKeys

3.3.2. Déplacement d’un objet

Comme nous venons de le voir, déplacer le cercle revient à calculer les coordonnées x, y de son centre. Nous pouvons donc recalculer ses coordonnées à chaque itération de la boucle, effacer l’écran, redessiner le cercle à la nouvelle position, rafraichir la fenêtre de jeu et attendre (un peu) avant de recommencer :

  • Lorsque le cercle se déplace latéralement de la gauche vers la droite :

    • la coordonnée x de son centre augmente

    • le coordonnée y reste inchangée.

  • Lorsque le cercle se déplace latéralement de la droite vers la gauche :

    • la coordonnée x de son centre diminue

    • le coordonnée y reste inchangée.

  • Lorsque le cercle se déplace verticalement du haut vers le bas :

    • la coordonnée y de son centre augmente

    • le coordonnée x reste inchangée.

  • Lorsque le cercle se déplace verticalement du bas vers le haut :

    • la coordonnée y de son centre diminue

    • le coordonnée x reste inchangée.

  • Lorsque les déplacements sont combinés, les coordonnées en x et en y changent.

Les variations de positions sur les axes x et y sont nommées dans le schéma ci-dessous : Δx et Δy

deplacement

La durée de la boucle est définie par la durée des actions à exécuter et à l’attente qu’on lui imposera avant de recommencer.

Cette attente est obtenue par l’uilisation de la méthode delay(ms) du module time de Pygame.

Comme vous avez du l’apprendre en Sciences Physique :

  • un déplacement Δx sur l’axe x pendant un temps Δt se fait à la vitesse Vx = Δx / Δt

  • un déplacement Δy sur l’axe y pendant un temps Δt se fait à la vitesse Vy = Δy / Δt

Si l’on choisi de déplacer identiquement sur les deux axes notre cercle, les quantités Δx et Δy sont égales.

Comme la durée d’une itération de boucle est fixée, la vitesse ne dépend que de cette quantité.

On peut donc établir les nouvelles coordonnées du centre du cercle comme suit :

  • \$x_(i+1) = x_i ± speed\$

  • \$y_(i+1) = y_i ± speed\$

  • avec \$speed = Δx = Δy\$

Le sens de déplacement est matérialisé par l’opérateur ±. On réalise cette opération de la façon suivante :

  • \$sens = 1\$ : déplacement dans le sens croissant (vers le bas ou à droite)

  • \$sens = -1\$ : déplacement dans le sens décroissant (vers le haut ou à gauche)

ce qui donne les équations :

  • \$x_(i+1) = x_i + sens_x * speed\$

  • \$y_(i+1) = y_i + sens_y * speed\$

  • Ouvrez le fichier pong4.py

  • Complétez le code pour que le cercle puisse se déplacer manuellement comme précédemment et en utilisant également les touches UP, DOWN, RIGHT et LEFT pour générer des déplacements fins. Le cercle doit s’arrêter lorsqu’il est en contact avec un bord.

  • Complétez le code pour que le cercle puisse se déplacer automatiquement lorsque l’on a utilisé la touche O (O comme Automatique !) et manuellement lorsqu’on utilise la touche kbd[M] (M comme Manuel !)

Attention : Il faudra gérer les rebonds sur les bords !

Résultat à obtenir
speed = 1
x_sens = y_sens = 1
auto = True
end = False

while not end:
    SCREEN.fill(RED)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            end = True

    key = pygame.key.get_pressed()

    if key[pygame.K_o]:
        auto = ___

    if key[pygame.K_m]:
        auto = ___

    if not auto:
        # Manual mode :
        #  - use A, Z, Q, S to move circle on the corners
        #  - use UP, DOWN, LEFT, RIGHT to move circle by <speed> pixel


    else:
        # if the circle touches the right and left edges
        # reverse direction on x-axis
        if ____:
            x_sens = ____

        # if the circle touches the lower and upper edges
        # reverse direction on y-axis
        if ___:
            y_sens = ___

        # compute new coordonates
        x = x + ___
        y = y + ___


    pygame.draw.circle(SCREEN, WHITE, (x, y), radius)

    pygame.display.update()

    # wait before trying it again
    pygame.time.delay(10)

pygame.quit()

3.4. Première version

3.4.1. Le jeu brut

On sait maintenant :

  • Créer une fenêtre de jeu

  • Dessiner une forme

  • Contrôler les déplacements

Il ne nous reste plus qu’à dessiner une raquette (un simple rectangle), gérer des déplacements et les collisions avec la balle. Si la balle ne touche pas la raquette lorsqu’elle est en bas, le jeu s’arrête.

Résultat à obtenir
  • Ouvrez le fichier pong5.py

  • Modifiez les caractéristiques de la fenêtre de jeu : Fond noir, dimensions 800*600

  • Modifiez les caractéristiques de la balle pour quelle soit blanche et de rayon 10 pixels

  • Modifiez les coordonnées de départ de la balle pour qu’elle tombe depuis le haut de l’écran au milieu

  • Ajouter un rectangle de couleur bleu et de dimensions 200x20 centré en bas de la fenêtre de jeu. Les caractéristiques de ce rectangle seront rassemblées dans un dictionnaire :

paddle = {}
  • Ajoutez dans les contrôles du jeu un mode pause accessible avec la touche SPACE. La reprise du jeu se fait avec la touche ENTER

  • Ajoutez le contrôle de la raquette à l’aide des touches LEFT et RIGHT. La raquette ne doit pas sortir de la fenêtre de jeu.

  • Gérez les collisions avec les bords de gauche, du haut et de droite qui doivent occasionner un rebond.

  • Gérez les collisions avec la raquette qui doit également occasionner un rebond, sinon le jeu s’arrête.

3.4.2. Améliorations

Le jeu fonctionne mais il n’est pas très pratique :

  • Il démarre dès l’exécution du programme : panique !

  • On n’a pas d’indicateur de réussite

Il nous faut donc ajouter du texte pour :

  • Retarder le démarrage de la partie avec un décompte par exemple : 3…​ 2…​ 1…​ 0 secondes.

  • Afficher un score qui pourrait être défini comme le nombre de balles sauvées.

Résultat à obtenir
  • Ouvrez le fichier pong6.py

  • Observez l’utilisation des méthodes :

    • SysFont() du module font

    • render() de l’objet myfont

    • blit() de l’objet screen

  • Identifiez le rôle de chaque méthode pour afficher un texte à un emplacement particulier de l’écran, dans une police, une taille et une couleur spécifique.

  • Ajoutez le décompte des secondes 3…​ 2…​ 1…​ 0 avant le démarrage du jeu

  • Ajouter l’affichage du score qui doit être incrémenté chaque fois que la balle tape la raquette.

import pygame

pygame.init()

...

myfont = pygame.font.SysFont('monospace', 50)

print("pong6")
screen.fill(BLACK)
title = myfont.render("Single Player Pong:", False, GREEN)
screen.blit(title, (WIDTH // 2 - title.get_width() // 2, HEIGHT // 2 - title.get_height() * 2))
pygame.display.update()
pygame.time.delay(1000)

# countdown before start game
# loop from 3 to 0 and write the number in the middle of the screen
???

...

pause = False
end = False
while not end:
    screen.fill(BLACK)
    # Control the game
    # Past your code from pong5.py

    pygame.draw.circle(screen, WHITE, (x, y), radius)
    pygame.draw.rect(screen, paddle["color"], (paddle["x"], paddle["y"], paddle["width"], paddle["height"]))

    # Display the score in position (10, 0) (top left on the screen)

    pygame.display.update()
    pygame.time.delay(10)

# Wait a bit to be sure the player knows his score
pygame.time.delay(2000)
pygame.quit()

3.5. Deuxième version

3.5.1. On complique un peu le jeu ?

Il y a plusieurs façon de compliquer le jeu. En modifiant la géométrie de l’aire de jeu (compliqué) ou plus simplement en accélérant la balle en cours de jeu, ou encore en diminuant la taille de la raquette. C’est ce que nous allons faire ici.

  • Toutes les 5 balles sauvées, la vitesse est incrémentée

  • Toutes les 10 balles sauvées, la taille de la raquette est diminuée de 20 pixels sans accélérer la balle.

Résultat à obtenir

3.5.2. Garder le meilleur score

Le jeu s’arrête dès que le joueur a perdu. Donnons au joueur la possibilité de rejouer et conservons au passage le meilleurs score :

Résultat à obtenir
  • La touche R doit être utilisée pour rejouer.

4. On joue !

Amusez vous, quand vous en aurez assez, modifiez le scénario, par exemple, vous pouvez :

  • ajouter un peu d’aléatoire dans les rebonds de la balle sur le bord supérieur de la fenêtre de jeux,

  • offrir au joueur la possibilité de doubler son score en faisant apparaite une balle bonus rouge avec une probabilité d’une chance sur 10,

  • …​. Faites preuve d’imagination !

Exemple avec rebonds aléatoires et balle bonus

the_end.jpg