Aller au contenu

Rendu

Bon c'est bien rigolo, mais on veut voir quelque chose bouger à l'écran !

Rendu naïf

Préparons le moteur de rendu. La couleur du personnage sera enregistrée dans une constante.

COULEUR_PERSONNAGE = (255, 146, 205)

Le moteur de rendu fait un rendu à l'écran. Pour ça, on a besoin de connaître la fenêtre crée à partir de la fonction main. On le rajoutera en paramètre de la fonction d'initialisation:

class Rendu:
    def __init__(self, ecran):
        self.ecran = ecran

On ajoute donc l'objet au gamestate quand on initialise :

def initialiser_rendu(etat, ecran):
    etat.rendu = Rendu(ecran)

Il nous faut modifier la fonction main en conséquences :

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

initialiser_controles(etat)
initialiser_simulation(etat)
initialiser_rendu(etat, fenetre)

Finalement, quand on fait le rendu et met à jour l'affichage, il nous suffit de :

  • remplir l'écran d'une couleur unie pour effacer la frame précédente
  • dessiner un cercle qui représente le personnage. On peut directement passer sa position à la fonction circle.
  • appeler display.flip() pour répercuter les changements sur l'écran

On va définir une couleur d'arrière plan sous forme d'une constante

COULEUR_ARRIERE_PLAN = (0, 0, 0)

On définit une fonction supplémentaire pour l'affichage du personnage, que l'on appelle dans le rendu. Cette fonction supplémentaire permet de garder le code plus clair, et de différencier ce qui relève de l'affichage général et ce qui relève de l'affichage du personnage.

def rendre_personnage(etat):
    ecran = etat.rendu.ecran
    position = etat.simulation.personnage.position
    pygame.draw.circle(ecran, COULEUR_PERSONNAGE, position, TAILLE_PERSONNAGE)


def afficher_rendu(etat):
    ecran = etat.rendu.ecran
    ecran.fill(COULEUR_ARRIERE_PLAN)

    rendre_personnage(etat)

    pygame.display.flip()    

Le code complet se trouve dans le dépliant :

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

VITESSE_PERSONNAGE = 5
TAILLE_PERSONNAGE = 10

## Rendu

COULEUR_ARRIERE_PLAN = (0, 0, 0)
COULEUR_PERSONNAGE = (255, 146, 205)

####################
####  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 ####
####################

class Personnage:

    def __init__(self, position):
        self.position = position

class Simulation:

    def __init__(self, personnage):
        self.personnage = personnage


def initialiser_simulation(etat):
    pos = Vector2(LARGEUR_FENETRE/2, HAUTEUR_FENETRE/2)
    p = Personnage(pos)
    etat.simulation = Simulation(p)

def avancer_simulation(etat):
    direction = etat.controles.direction
    etat.simulation.personnage.position += direction * VITESSE_PERSONNAGE

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

class Rendu:
    def __init__(self, ecran):
        self.ecran = ecran

def initialiser_rendu(etat, ecran):
    etat.rendu = Rendu(ecran)

def rendre_personnage(etat):
    ecran = etat.rendu.ecran
    position = etat.simulation.personnage.position
    pygame.draw.circle(ecran, COULEUR_PERSONNAGE, position, TAILLE_PERSONNAGE)

def afficher_rendu(etat):
    ecran = etat.rendu.ecran
    ecran.fill(COULEUR_ARRIERE_PLAN)

    rendre_personnage(etat)

    pygame.display.flip() 

####################
####    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, fenetre)

    clock = pygame.time.Clock()

    while not etat.controles.quitter:

        clock.tick(FPS)

        traiter_controles(etat)

        avancer_simulation(etat)

        afficher_rendu(etat)

    pygame.quit()

if __name__ == "__main__":
    main()

Référentiels

Si on execute le code, on le personnage se déplace bien, mais les directions haut et bas semblent inversées !

C'est parce que dans notre simulation, nous utilisons un référentiel avec axe vertical qui croit vers le "haut", alors que l'écran utilise référentiel avec un axe vertical qui croît vers le bas.

TODO (prof) schema ici omg

C'est bien plus intuitif d'avoir un axe vertical vers le "haut" dans la simulation, parce qu'en science, par exemple en Physique, on a l'habitude d'une telle représentation. Adaptons donc le rendu en transformant chaque coordonnée y en HAUTEUR_FENETRE - y (c'est le changement de référentiel), à l'aide d'une fonction

def ref_sve(v): #sve pour Simulation Vers Ecran
    return Vector2(v.x, HAUTEUR_FENETRE - v.y)

On modifie la fonction rendre_personnage en conséquences :

def rendre_personnage(etat):
    ecran = etat.rendu.ecran
    position = etat.simulation.personnage.position
    pygame.draw.circle(ecran, COULEUR_PERSONNAGE, ref_sve(position), TAILLE_PERSONNAGE)

Le code complet se trouve dans ce dépliant :

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

VITESSE_PERSONNAGE = 5
TAILLE_PERSONNAGE = 10

## Rendu

COULEUR_ARRIERE_PLAN = (0, 0, 0)
COULEUR_PERSONNAGE = (255, 146, 205)

####################
####  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 ####
####################

class Personnage:

    def __init__(self, position):
        self.position = position

class Simulation:

    def __init__(self, personnage):
        self.personnage = personnage


def initialiser_simulation(etat):
    pos = Vector2(LARGEUR_FENETRE/2, HAUTEUR_FENETRE/2)
    p = Personnage(pos)
    etat.simulation = Simulation(p)

def avancer_simulation(etat):
    direction = etat.controles.direction
    etat.simulation.personnage.position += direction * VITESSE_PERSONNAGE

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

class Rendu:
    def __init__(self, ecran):
        self.ecran = ecran

def ref_sve(v):
    return Vector2(v.x, HAUTEUR_FENETRE - v.y)

def initialiser_rendu(etat, ecran):
    etat.rendu = Rendu(ecran)

def rendre_personnage(etat):
    ecran = etat.rendu.ecran
    position = etat.simulation.personnage.position
    pygame.draw.circle(ecran, COULEUR_PERSONNAGE, ref_sve(position), TAILLE_PERSONNAGE)

def afficher_rendu(etat):
    ecran = etat.rendu.ecran
    ecran.fill(COULEUR_ARRIERE_PLAN)

    rendre_personnage(etat)

    pygame.display.flip() 

####################
####    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, fenetre)

    clock = pygame.time.Clock()

    while not etat.controles.quitter:

        clock.tick(FPS)

        traiter_controles(etat)

        avancer_simulation(etat)

        afficher_rendu(etat)

    pygame.quit()


if __name__ == "__main__":
    main()

Si on l'execute, le personnage se déplace bien dans les bonnes directions.

TODO (prof) AJOUTER DES LIMITES AU DEPLACEMENT !!! plutot plus tard ?