Projet 1 : Ninja Runner

Infinite Runner

Un infinite Runner est un jeu qui ne se termine jamais. L'objectif est de durer le plus longtemps possible, et plus vous durez, plus votre score est élevé.

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)

1
import pgzrun
2
3
WIDTH=800
4
HEIGHT=600
5
6
pgzrun.go()

MéthodeBackground

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.

1
import pgzrun
2
3
WIDTH=800
4
HEIGHT=600
5
6
def draw():
7
  screen.draw.filled_rect(Rect(0,0,800,400), (163, 232, 254))
8
  screen.draw.filled_rect(Rect(0,400,800,200), (88, 242, 152))
9
10
pgzrun.go()

MéthodeNotre 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é.

1
import pgzrun
2
from pgzhelper import * # import du nouveau module
3
4
WIDTH=800
5
HEIGHT=600
6
7
def draw():
8
  screen.draw.filled_rect(Rect(0,0,800,400), (163, 232, 254))
9
  screen.draw.filled_rect(Rect(0,400,800,200), (88, 242, 152))
10
11
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

1
import pgzrun
2
from pgzhelper import *
3
4
WIDTH=800
5
HEIGHT=600
6
7
# création du runner
8
runner = Actor('run__000')
9
# création d'une liste d'images
10
runner.images = ['run__000', 'run__001', 'run__002', 'run__003', 'run__004', 'run__005', 'run__006', 'run__007', 'run__008', 'run__009']
11
# position du runner
12
runner.x = 100 
13
runner.y = 400
14
15
def update():# fonction mise à jour 
16
    runner.next_image() #changement des images
17
18
def draw():
19
  screen.draw.filled_rect(Rect(0,0,800,400), (163, 232, 254)) # ciel
20
  screen.draw.filled_rect(Rect(0,400,800,200), (88, 242, 152)) # sol
21
  runner.draw() # affichage du runner
22
23
pgzrun.go()

MéthodeNotre 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

1
import pgzrun
2
from pgzhelper import *
3
4
WIDTH=800
5
HEIGHT=600
6
vitesse_y = 0
7
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 )

1
def saut_runner():
2
    global vitesse_y # on précise que l'on souhaite modifier la variable globale vitesse_y
3
    if keyboard.up:# si on appuie sur la flèche haut
4
        runner.image = 'jump__004'
5
        vitesse_y = -15 # vitesse_y prend la valeur de -15
6
    runner.y = runner.y + vitesse_y # le runner monte
7
    vitesse_y = vitesse_y + gravite # on y ajout de la gravité ce qui fait baisser sa vitesse
8
    if runner.y > 400:# si on rentre dans le sol
9
        vitesse_y = 0# on doit rester au sol
10
        runner.y = 400
11
        
12
def update():# fonction mise à jour 
13
    runner.next_image() #chagement des images
14
    saut_runner()# mise à jour du saut
15

MéthodeOn 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 )

1
import pgzrun
2
from pgzhelper import *
3
from random import randint # on importe la méthode randint du module random
4
5
WIDTH=800
6
HEIGHT=600
7
vitesse_y = 0
8
gravite = 1
9
10
# création du runner
11
runner = Actor('run__000')
12
# création d'une liste d'images
13
runner.images = ['run__000', 'run__001', 'run__002', 'run__003', 'run__004', 'run__005', 'run__006', 'run__007', 'run__008', 'run__009']
14
# position du runner
15
runner.x = 100 
16
runner.y = 400
17
18
obstacles = []
19
obstacles_timeout = 0
1
def cactus():
2
    global obstacles_timeout # on souhaite modifier obstacles_timeout
3
    if obstacles_timeout > randint(45,55): # si elle est supérieure à un entier aléatoirement choisi entre 45 et 55
4
        actor = Actor('cactus') # on crée l'obstacle
5
        actor.x = 850# coordonnées
6
        actor.y = 430
7
        obstacles.append(actor)# on l'ajoute à la liste
8
        obstacles_timeout = 0 # on remet le compteur à zéro
9
    for actor in obstacles:# mouvement des cactus
10
        actor.x -= 8
11
    for actor in obstacles:# on les supprime de la liste s'ils quittent l'écran
12
        if actor.x < -50:
13
            obstacles.remove(actor)
14
    
15
16
def update():# fonction mise à jour
17
    global obstacles_timeout
18
    obstacles_timeout = obstacles_timeout + 1
19
    runner.next_image() #chagement des images
20
    saut_runner()# mise à jour du saut
21
    cactus() # mise à jour de cactus
22
23
def draw():
24
  screen.draw.filled_rect(Rect(0,0,800,400), (163, 232, 254)) # ciel
25
  screen.draw.filled_rect(Rect(0,400,800,200), (88, 242, 152)) # sol
26
  runner.draw() # affichage du runner
27
  for actor in obstacles: # affichage des cactus
28
    actor.draw()

MéthodeAjoutons 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

1
obstacles = []
2
obstacles_timeout = 0
3
score = 0 # création de score
1
def cactus():
2
    global obstacles_timeout,score # on souhaite modifier obstacles_timeout et score
3
    if obstacles_timeout > randint(45,55): # si elle est supérieure à un entier aléatoirement choisi entre 45 et 55
4
        actor = Actor('cactus') # on crée l'obstacle
5
        actor.x = 850# coordonnées
6
        actor.y = 430
7
        obstacles.append(actor)# on l'ajoute à la liste
8
        obstacles_timeout = 0 # on remet le compteur à zéro
9
    for actor in obstacles:# mouvement des cactus
10
        actor.x -= 8
11
    for actor in obstacles:# on les supprime de la liste s'ils quittent l'écran
12
        if actor.x < -50:
13
            obstacles.remove(actor)
14
            score = score + 1 # on ajoute 1 au score
15
    
1
def draw():
2
  screen.draw.filled_rect(Rect(0,0,800,400), (163, 232, 254)) # ciel
3
  screen.draw.filled_rect(Rect(0,400,800,200), (88, 242, 152)) # sol
4
  runner.draw() # affichage du runner
5
  for actor in obstacles: # affichage des cactus
6
    actor.draw()
7
  screen.draw.text('Score: ' + str(score), (15,10), color=(0,0,0), fontsize=30) # on affiche le score

MéthodeGame 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 variable game_over à True

  • Dans la fonction draw() on gère l'affichage en fonction de la variable game_over

Modifiez le programme comme suit

1
obstacles = []
2
obstacles_timeout = 0
3
score = 0
4
game_over = False # création
1
def cactus():
2
    global obstacles_timeout,score,game_over # on souhaite modifier obstacles_timeout et score
3
    if obstacles_timeout > randint(45,55): # si elle est supérieure à un entier aléatoirement choisi entre 45 et 55
4
        actor = Actor('cactus') # on crée l'obstacle
5
        actor.x = 850# coordonnées
6
        actor.y = 430
7
        obstacles.append(actor)# on l'ajoute à la liste
8
        obstacles_timeout = 0 # on remet le compteur à zéro
9
    for actor in obstacles:# mouvement des cactus
10
        actor.x -= 8
11
    for actor in obstacles:# on les supprime de la liste s'ils quittent l'écran
12
        if actor.x < -50:
13
            obstacles.remove(actor)
14
            score = score + 1
15
    # méthode spécifique aux modules importés ( on teste la collision avec n'importe quel objet d'une liste)
16
    # la méthode collidelist renvoie l'indice de l'élément de la liste qui est détecté en collision et -1 sinon
17
    if runner.collidelist(obstacles) != -1: # détection des collisions
18
        game_over = True
1
def draw():
2
    screen.draw.filled_rect(Rect(0,0,800,400), (163, 232, 254)) # ciel
3
    screen.draw.filled_rect(Rect(0,400,800,200), (88, 242, 152)) # sol
4
    if game_over == False: # le jeu tourne si c'est False
5
        runner.draw() # affichage du runner
6
        for actor in obstacles: # affichage des cactus
7
            actor.draw()
8
        screen.draw.text('Score: ' + str(score), (15,10), color=(0,0,0), fontsize=30)
9
    else:# sinon on stoppe et on affiche game over et le score
10
        screen.draw.text('Game Over', centerx=400, centery=270, color=(255,255,255), fontsize=60)
11
        screen.draw.text('Score: ' + str(score), centerx=400, centery=330, color=(255,255,255), fontsize=60)

ComplémentEt...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()