Aller au contenu

Contrôles : touches de déplacement

Commençons par implanter le contrôleur. Le contrôleur traite les actions du joueur et enregistre des commandes dans son état. Ce sont les commandes qui seront traitées par la simulation.

On va créer un contrôleur basique, avec deux commandes :

  • Une commande quitter, sous forme d'un booléen valant True si on doit quitter, False sinon.

  • Une commande deplacement, sous forme d'un vecteur indiquant dans quelle direction le joueur veut déplacer le personnage.

Pour pouvoir changer facilement les touches associées à une commande, on va les spécifier sous forme de constantes.

TOUCHE_QUITTER = pygame.K_ESCAPE
TOUCHE_HAUT = pygame.K_UP #K_UP est la flèche haut sur le clavier. 
TOUCHE_BAS = pygame.K_DOWN #K_DOWN = flèche bas
TOUCHE_DROITE = pygame.K_RIGHT #K_RIGHT = flèche droite
TOUCHE_GAUCHE = pygame.K_LEFT #K_LEFT = flèche gauche

Classe Controles

On peut créer une classe pour l'état du controleur. Pour représenter un vecteur, on va utiliser la classe Vector2 du module math de pygame, qui supporte des opérations de base (addition, soustraction, ...) qui nous seront bien pratiques par la suite. On va l'importer directement pour ne pas avoir à toujours écrire pygame.math.Vector2.

from pygame.math import Vector2

class Controles :
    def __init__(self):
        self.quitter = False
        self.direction = pygame.math.Vector2(0,0)

Dans la fonction d'initialisation des controles, on ajoute l'objet correspondant au gamestate :

def initialiser_controles(etat):
    etat.controles = Controles()

Quitter

Dans la fonction d'opération, on regarde les évènements de la dernière frame, et on met à jour en conséquences. Commençons par la commande quitter. A quelque détails prets, on peu reprendre le code que l'on a écrit lors de notre découverte de pygame.

def traiter_controles(etat):

    for e in pygame.event.get():
            if e.type == pygame.KEYDOWN: 
                if e.key == TOUCHE_QUITTER:
                    etat.controles.quitter = True

Direction

Pour la direction, elle va dépendre des touches qui sont enfoncées, et non des touches qui sont appuyées. On va donc regarder directement l'état du clavier au lieu de passer par des évènements. Pour ça, on peut utiliser la fonction get_pressed du module key. Cette fonction renvoie une liste de booléns. La valeur à l'indice k indique si la touche de code k est pressée (True) ou non (False).

Par exemple, pour afficher si la touche K_ESCAPE est enfoncée, on fait :

clavier = pygame.key.get_pressed()
print(clavier[pygame.K_ESCAPE])

Comme on utilise nos constantes au lieu de mettre directement les touches, ça donne :

clavier = pygame.key.get_pressed()
if clavier[TOUCHE_HAUT]:
    # on veut aller en haut
if clavier[TOUCHE_BAS]:
    # on veut aller en bas
if clavier[TOUCHE_DROITE]:
    # on veut aller à droite
if clavier[TOUCHE_GAUCHE]:
    # on veut aller à gauche

Remarquez que c'est des if et non des elif, parce que les quatres touches peuvent être enfoncées en même temps. Pour créer notre direction, on part d'un vecteur nul (0,0), et on ajoute ou retire 1.0 de manière à le faire "pointer" dans la direction choisie avec les touches.

TODO (prof) SCHEMA, parler des axes

On peut utiliser l'opérateur +=, qui permet de faire une addition dite en place.

a = a + 1 #Ceci 
a += 1 #peut s'écrire comme suit 
direction = Vector2(0, 0)

clavier = pygame.key.get_pressed()
if clavier[TOUCHE_HAUT]:
    direction.y += 1.0
if clavier[TOUCHE_BAS]:
    direction.y -= 1.0
if clavier[TOUCHE_DROITE]:
    direction.x += 1.0
if clavier[TOUCHE_GAUCHE]:
    direction.x -= 1.0

Ce qui est bien avec cette méthode, c'est que deux touche opposées s'annulent : appuyer à la fois sur haut et bas ne génère pas de déplacement.

On peut ajouter ça à notre code de controleur, sans oublier de mettre à jour l'état :

def traiter_controles(etat):

    for e in pygame.event.get():
            if e.type == pygame.KEYDOWN: 
                if e.key == TOUCHE_QUITTER:
                    etat.controles.quitter = True

    direction = Vector2(0, 0)

    clavier = pygame.key.get_pressed()
    if clavier[TOUCHE_HAUT]:
        direction.y += 1.0
    if clavier[TOUCHE_BAS]:
        direction.y -= 1.0
    if clavier[TOUCHE_DROITE]:
        direction.x += 1.0
    if clavier[TOUCHE_GAUCHE]:
        direction.x -= 1.0

    etat.controles.direction = direction

Affichage en console

Pour vérifier, on va afficher la direction dans la console à chaque tour de boucle principale. Pour que les évènements clavier soient accessibles, il nous faut aussi créer une fenêtre, ce que l'on va également faire dans la fonction main.

## les constantes vont dans la partie config, en haut du fichier
LARGEUR_FENETRE = 1280
HAUTEUR_FENETRE = 720

def main():

    pygame.init()

    etat = GameState()

    fenetre = pygame.display.set_mode([LARGEUR_FENETRE, HAUTEUR_FENETRE])

    initialiser_controles(etat)
    initialiser_simulation(etat)
    initialiser_rendu(etat)

    clock = pygame.time.Clock()

    while not etat.controles.quitter:

        clock.tick(FPS)

        traiter_controles(etat)

        avancer_simulation(etat)

        afficher_rendu(etat)

        print(etat.controles.direction)

    pygame.quit()

if __name__ == "__main__":
    main()

En exécutant le programme, et en jouant avec les flèches directionnelles du clavier, on obtient une sortie console de ce type, qui change en fonction des touches que l'on maintient :

[1, 1]
[0, 0]
[0, 0]
[0, 0]
[-1, 0]
[-1, 0]
[-1, 0]
[0, 0]
[0, 0]
[0, 0]

Vous trouverez ci-dessous le code complet de notre jeu avec son contrôleur.

Code complet
import pygame
from pygame.math import Vector2

####################
####   CONFIG   ####
####################

## Général 

FPS = 60

LARGEUR_FENETRE = 1280
HAUTEUR_FENETRE = 720


## Controles

TOUCHE_QUITTER = pygame.K_ESCAPE
TOUCHE_HAUT = pygame.K_UP  
TOUCHE_BAS = pygame.K_DOWN 
TOUCHE_DROITE = pygame.K_RIGHT 
TOUCHE_GAUCHE = pygame.K_LEFT 

## Simulation


## Rendu

####################
####  GENERAL   ####
####################

class GameState :

    def __init__(self):
        self.controles = None # on met a None parce que ces attributs seront initialisés plus tard
        self.simulation = None
        self.rendu = None

####################
#### CONTROLEUR ####
####################

class Controles :
    def __init__(self):
        self.quitter = False
        self.direction = Vector2(0,0)

def initialiser_controles(etat):
    etat.controles = Controles()

def traiter_controles(etat):

    for e in pygame.event.get():
            if e.type == pygame.KEYDOWN: 
                if e.key == TOUCHE_QUITTER:
                    etat.controles.quitter = True

    direction = Vector2(0, 0)

    clavier = pygame.key.get_pressed()
    if clavier[TOUCHE_HAUT]:
        direction.y += 1.0
    if clavier[TOUCHE_BAS]:
        direction.y -= 1.0
    if clavier[TOUCHE_DROITE]:
        direction.x += 1.0
    if clavier[TOUCHE_GAUCHE]:
        direction.x -= 1.0

    etat.controles.direction = direction

####################
#### SIMULATION ####
####################

def initialiser_simulation(etat):
    pass

def avancer_simulation(etat):
    pass

####################
####   RENDU    ####
####################

def initialiser_rendu(etat):
    pass

def afficher_rendu(etat):
    pass

####################
####    MAIN    ####
####################

def main():

    pygame.init()

    etat = GameState()

    fenetre = pygame.display.set_mode([LARGEUR_FENETRE, HAUTEUR_FENETRE])

    initialiser_controles(etat)
    initialiser_simulation(etat)
    initialiser_rendu(etat)

    clock = pygame.time.Clock()

    while not etat.controles.quitter:

        clock.tick(FPS)

        traiter_controles(etat)

        avancer_simulation(etat)

        afficher_rendu(etat)

        print(etat.controles.direction)

    pygame.quit()

if __name__ == "__main__":
    main()