Projet 1 : Ninja Runner
Infinite Runner
Préparation
Créez un dossier projet1 qui doit contenir un dossier images et un dossier sounds
Créez un fichier projet1.py avec Thonny et enregistrez-le dans le dossier projet1 (voir ci-dessous)
import pgzrun
WIDTH=800
HEIGHT=600
pgzrun.go()
Méthode : Background
On peut dessiner différents types d'objets avec l'indtruction screen.draw
.
screen.draw.line()
screen.draw.circle()
screen.draw.filled_circle()
screen.draw.rect()
screen.draw.filled_rect()
Pour en savoir plus sur ces fonctions voir ici
Pour notre fond nous utiliserons screen.draw.filled_rect() qui permet de dessiner un rectangle avec une couleur de fond
Pour le ciel (ligne 7)
pour le sol (ligne 8)
Rect(0,0,800,400) signifie un rectangle dont les coordonnées du coin supérieur gauche sont (0,0) de largeur 800px et de hauteur 400px
(163, 232, 254) est la couleur de remplissage ( bleu ciel ici)
screen.draw.filled_rect(Rect(0,0,800,400), (163, 232, 254)) , signifie dessine le rectangle avec cette couleur
Vous devriez obtenir cet affichage avec le programme ci-dessous.

import pgzrun
WIDTH=800
HEIGHT=600
def draw():
screen.draw.filled_rect(Rect(0,0,800,400), (163, 232, 254))
screen.draw.filled_rect(Rect(0,400,800,200), (88, 242, 152))
pgzrun.go()
Méthode : Notre personnage et son animation
Nous allons utiliser un autre module pgzhelper.py
qui facilite la création d'animation pour notre personnage
Il faut dans un premier temps télécharger le fichier pgzhelper.py
, le décompresser et le placer dans le dossier projet1
Puis complétez le programme comme ci-dessous, exécutez le pour voir si tout s'est bien déroulé.
import pgzrun
from pgzhelper import * # import du nouveau module
WIDTH=800
HEIGHT=600
def draw():
screen.draw.filled_rect(Rect(0,0,800,400), (163, 232, 254))
screen.draw.filled_rect(Rect(0,400,800,200), (88, 242, 152))
pgzrun.go()
Dans le pack d'images [zip][1] ( déjà téléchargé dans le projet 0) vous trouverez des sprites de ninja pour la course ( run_000.png à run_009.png).
Copiez-les et collez-les dans le dossier images de votre projet

le programme suivant permet d'afficher notre runner et de l'animer en utilisant les sprites
import pgzrun
from pgzhelper import *
WIDTH=800
HEIGHT=600
# création du runner
runner = Actor('run__000')
# création d'une liste d'images
runner.images = ['run__000', 'run__001', 'run__002', 'run__003', 'run__004', 'run__005', 'run__006', 'run__007', 'run__008', 'run__009']
# position du runner
runner.x = 100
runner.y = 400
def update():# fonction mise à jour
runner.next_image() #changement des images
def draw():
screen.draw.filled_rect(Rect(0,0,800,400), (163, 232, 254)) # ciel
screen.draw.filled_rect(Rect(0,400,800,200), (88, 242, 152)) # sol
runner.draw() # affichage du runner
pgzrun.go()
Méthode : Notre personnage saute...
Nous allons simuler le saut du personnage en tenant compte de la gravité...
Commençons par créer deux variables globales vitesse_y
et gravite
initialisées à 0 et 1 respectivement
import pgzrun
from pgzhelper import *
WIDTH=800
HEIGHT=600
vitesse_y = 0
gravite = 1
On crée une fonction : saut_runner()
Modifiez le programme comme ci-dessous ( les lignes sont commentées )
Ajoutez l'image jump__004.png
au dossier images ( pour changer l'image lors du saut )
def saut_runner():
global vitesse_y # on précise que l'on souhaite modifier la variable globale vitesse_y
if keyboard.up:# si on appuie sur la flèche haut
runner.image = 'jump__004'
vitesse_y = -15 # vitesse_y prend la valeur de -15
runner.y = runner.y + vitesse_y # le runner monte
vitesse_y = vitesse_y + gravite # on y ajout de la gravité ce qui fait baisser sa vitesse
if runner.y > 400:# si on rentre dans le sol
vitesse_y = 0# on doit rester au sol
runner.y = 400
def update():# fonction mise à jour
runner.next_image() #chagement des images
saut_runner()# mise à jour du saut
Méthode : On ajoute des obstacles...
Les obstacles seront des cactus.
Récupérez l'image du cactus et placez-la dans le dossier images.
Ici nous utiliserons une liste obstacles qui contiendra les cactus à afficher , ceux-ci seront créé dans une fonction lorsque l'on aura atteint un seuil de temps (timeout)
On crée les variables obstacles
(liste vide) et obstacles_timeout
initialisé à 0 (le compteur)
On crée une fonction cactus()
Complétez le programme comme suit ( les lignes sont commentées )
import pgzrun
from pgzhelper import *
from random import randint # on importe la méthode randint du module random
WIDTH=800
HEIGHT=600
vitesse_y = 0
gravite = 1
# création du runner
runner = Actor('run__000')
# création d'une liste d'images
runner.images = ['run__000', 'run__001', 'run__002', 'run__003', 'run__004', 'run__005', 'run__006', 'run__007', 'run__008', 'run__009']
# position du runner
runner.x = 100
runner.y = 400
obstacles = []
obstacles_timeout = 0
def cactus():
global obstacles_timeout # on souhaite modifier obstacles_timeout
if obstacles_timeout > randint(45,55): # si elle est supérieure à un entier aléatoirement choisi entre 45 et 55
actor = Actor('cactus') # on crée l'obstacle
actor.x = 850# coordonnées
actor.y = 430
obstacles.append(actor)# on l'ajoute à la liste
obstacles_timeout = 0 # on remet le compteur à zéro
for actor in obstacles:# mouvement des cactus
actor.x -= 8
for actor in obstacles:# on les supprime de la liste s'ils quittent l'écran
if actor.x < -50:
obstacles.remove(actor)
def update():# fonction mise à jour
global obstacles_timeout
obstacles_timeout = obstacles_timeout + 1
runner.next_image() #chagement des images
saut_runner()# mise à jour du saut
cactus() # mise à jour de cactus
def draw():
screen.draw.filled_rect(Rect(0,0,800,400), (163, 232, 254)) # ciel
screen.draw.filled_rect(Rect(0,400,800,200), (88, 242, 152)) # sol
runner.draw() # affichage du runner
for actor in obstacles: # affichage des cactus
actor.draw()
Méthode : Ajoutons un score...
On crée une variable globale score initialisée à 0
On l'incrémente de 1 à chaque fois que l'on supprime un cactus
On l'affiche dans le draw()
Modifiez le programme comme suit
obstacles = []
obstacles_timeout = 0
score = 0 # création de score
def cactus():
global obstacles_timeout,score # on souhaite modifier obstacles_timeout et score
if obstacles_timeout > randint(45,55): # si elle est supérieure à un entier aléatoirement choisi entre 45 et 55
actor = Actor('cactus') # on crée l'obstacle
actor.x = 850# coordonnées
actor.y = 430
obstacles.append(actor)# on l'ajoute à la liste
obstacles_timeout = 0 # on remet le compteur à zéro
for actor in obstacles:# mouvement des cactus
actor.x -= 8
for actor in obstacles:# on les supprime de la liste s'ils quittent l'écran
if actor.x < -50:
obstacles.remove(actor)
score = score + 1 # on ajoute 1 au score
def draw():
screen.draw.filled_rect(Rect(0,0,800,400), (163, 232, 254)) # ciel
screen.draw.filled_rect(Rect(0,400,800,200), (88, 242, 152)) # sol
runner.draw() # affichage du runner
for actor in obstacles: # affichage des cactus
actor.draw()
screen.draw.text('Score: ' + str(score), (15,10), color=(0,0,0), fontsize=30) # on affiche le score
Méthode : Game Over ...
On a perdu si le coureur touche un cactus.
On crée une variable globale
game_over
intialisée àFalse
Dans la fonction
cactus()
on teste la collision du runner et des cactus si oui alors on passe la variablegame_over
àTrue
Dans la fonction
draw()
on gère l'affichage en fonction de la variablegame_over
Modifiez le programme comme suit
obstacles = []
obstacles_timeout = 0
score = 0
game_over = False # création
def cactus():
global obstacles_timeout,score,game_over # on souhaite modifier obstacles_timeout et score
if obstacles_timeout > randint(45,55): # si elle est supérieure à un entier aléatoirement choisi entre 45 et 55
actor = Actor('cactus') # on crée l'obstacle
actor.x = 850# coordonnées
actor.y = 430
obstacles.append(actor)# on l'ajoute à la liste
obstacles_timeout = 0 # on remet le compteur à zéro
for actor in obstacles:# mouvement des cactus
actor.x -= 8
for actor in obstacles:# on les supprime de la liste s'ils quittent l'écran
if actor.x < -50:
obstacles.remove(actor)
score = score + 1
# méthode spécifique aux modules importés ( on teste la collision avec n'importe quel objet d'une liste)
# la méthode collidelist renvoie l'indice de l'élément de la liste qui est détecté en collision et -1 sinon
if runner.collidelist(obstacles) != -1: # détection des collisions
game_over = True
def draw():
screen.draw.filled_rect(Rect(0,0,800,400), (163, 232, 254)) # ciel
screen.draw.filled_rect(Rect(0,400,800,200), (88, 242, 152)) # sol
if game_over == False: # le jeu tourne si c'est False
runner.draw() # affichage du runner
for actor in obstacles: # affichage des cactus
actor.draw()
screen.draw.text('Score: ' + str(score), (15,10), color=(0,0,0), fontsize=30)
else:# sinon on stoppe et on affiche game over et le score
screen.draw.text('Game Over', centerx=400, centery=270, color=(255,255,255), fontsize=60)
screen.draw.text('Score: ' + str(score), centerx=400, centery=330, color=(255,255,255), fontsize=60)
Complément : Et...Pour finir
Il y a 2 choses à améliorer :
Faire en sorte que le runner ne puisse sauter que s'il est au sol
Lorsque le runner a perdu, le score continue d'augmenter ( c'est un bug..) , il faut corriger cela
En plus vous pouvez ajouter une musique de fond, voici un pack de sons et musiques
rappel pour une musique de fond, mettre la musique (par exemple level1.ogg) dans le dossier sounds et dans le programme (au début), utiliser l'instruction sounds.level1.play()