Attente du début de partie
Dans cette étape, on met en place une attente : la partie commence uniquement quand le joueur se déplace.
Classe de simulation
Dans la classe de simulation, ajoutons un booléen qui dit si la partie a commencé ou non.
class Simulation:
def __init__(self, personnage):
self.personnage = personnage
self.projectiles = [
Projectile(Vector2(200, 300), Vector2(1.0, 1.0)),
Projectile(Vector2(400, 120), Vector2(1.0, 0)),
Projectile(Vector2(800, 600), Vector2(-1.1, -0.3)),
Projectile(Vector2(200, 600), Vector2(0.2, -0.1)),
]
self.collision = False
self.decompte_victoire = DECOMPTE_VICTOIRE
self.attente = True
On crée une fonction de simulation, qui regarde simplement si la direction du contrôleur a une valeur différente de 0, et qui met attente
à False
si besoin.
Fonction d'attente
def verifier_attente(etat):
if etat.simulation.attente and etat.controles.direction.length() != 0.0:
etat.simulation.attente = False
Fonction de simulation et boucle principale
On déplace les conditions de la boucle principale dans la simulation pour alléger la boucle principale. On tient compte de l'attente dans la simulation.
while not etat.controles.quitter:
if etat.controles.recommencer:
reinitialiser_controles(etat)
reinitialiser_simulation(etat)
reinitialiser_rendu(etat)
t = clock.tick(FPS)/1000
traiter_controles(etat, t)
avancer_simulation(etat,t)
afficher_rendu(etat, t)
def avancer_simulation(etat, t):
if etat.simulation.attente:
verifier_attente(etat)
elif not etat.simulation.collision and etat.simulation.decompte_victoire != 0.0:
deplacer_personnage(etat, t)
limiter_deplacement_personnage(etat)
deplacer_projectiles(etat, t)
faire_rebondir_projectiles(etat)
detecter_collisions(etat)
decompter_victoire(etat, t)
Si on lance le jeu, on peut constater l'attente jusqu'à ce que le joueur déplace le personnage.
Le code complet de l'étape se trouve dans le dépliant ci-dessous :
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
TOUCHE_RECOMMENCER = pygame.K_r
## Simulation
VITESSE_PERSONNAGE = 300
TAILLE_PERSONNAGE = 10
TAILLE_PROJECTILES = 10
VITESSE_PROJECTILES = 200
DECOMPTE_VICTOIRE = 5.0
## Rendu
COULEUR_ARRIERE_PLAN = (0, 0, 0)
COULEUR_PERSONNAGE = (255, 146, 205)
COULEUR_PROJECTILES = (146, 255, 205)
COULEUR_CHRONOMETRE = (150, 150, 170)
TAILLE_CHRONOMETRE = 30
MARGES_CHRONOMETRE = 20
TAILLE_FIN_DE_PARTIE = 150
COULEUR_FIN_DE_PARTIE = (150, 150, 170)
####################
#### 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)
self.recommencer = False
def initialiser_controles(etat):
etat.controles = Controles()
def reinitialiser_controles(etat):
etat.controles = Controles()
def traiter_controles(etat, t):
for e in pygame.event.get():
if e.type == pygame.KEYDOWN:
if e.key == TOUCHE_QUITTER:
etat.controles.quitter = True
if e.key == TOUCHE_RECOMMENCER:
etat.controles.recommencer = 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
if direction.length() != 0:
direction.normalize_ip()
etat.controles.direction = direction
####################
#### SIMULATION ####
####################
class Personnage:
def __init__(self, position):
self.position = position
class Projectile:
def __init__(self, position, direction):
self.position = position
self.direction = direction
if self.direction.length != 0:
self.direction.normalize_ip()
class Simulation:
def __init__(self, personnage):
self.personnage = personnage
self.projectiles = [
Projectile(Vector2(200, 300), Vector2(1.0, 1.0)),
Projectile(Vector2(400, 120), Vector2(1.0, 0)),
Projectile(Vector2(800, 600), Vector2(-1.1, -0.3)),
Projectile(Vector2(200, 600), Vector2(0.2, -0.1)),
]
self.collision = False
self.decompte_victoire = DECOMPTE_VICTOIRE
self.attente = True
def initialiser_simulation(etat):
pos = Vector2(LARGEUR_FENETRE/2, HAUTEUR_FENETRE/2)
p = Personnage(pos)
etat.simulation = Simulation(p)
def reinitialiser_simulation(etat):
initialiser_simulation(etat)
def intersection(c1, r1, c2, r2):
return c1.distance_to(c2) <= r1 + r2
def detecter_collisions(etat):
perso = etat.simulation.personnage
for p in etat.simulation.projectiles:
if intersection(perso.position, TAILLE_PERSONNAGE, p.position, TAILLE_PROJECTILES):
etat.simulation.collision = True
def deplacer_personnage(etat, t):
direction = etat.controles.direction
etat.simulation.personnage.position += direction * VITESSE_PERSONNAGE * t
def limiter_deplacement_personnage(etat):
p = etat.simulation.personnage
if p.position.x < TAILLE_PERSONNAGE:
p.position.x = TAILLE_PERSONNAGE
if p.position.x > LARGEUR_FENETRE - TAILLE_PERSONNAGE:
p.position.x = LARGEUR_FENETRE - TAILLE_PERSONNAGE
if p.position.y < TAILLE_PERSONNAGE:
p.position.y = TAILLE_PERSONNAGE
if p.position.y > HAUTEUR_FENETRE - TAILLE_PERSONNAGE:
p.position.y = HAUTEUR_FENETRE - TAILLE_PERSONNAGE
def deplacer_projectiles(etat, t):
for p in etat.simulation.projectiles:
p.position += p.direction * VITESSE_PROJECTILES * t
def faire_rebondir_projectiles(etat):
for p in etat.simulation.projectiles:
if p.position.x < TAILLE_PROJECTILES:
p.position.x = TAILLE_PROJECTILES
p.direction.x *= -1
if p.position.x > LARGEUR_FENETRE - TAILLE_PROJECTILES:
p.position.x = LARGEUR_FENETRE - TAILLE_PROJECTILES
p.direction.x *= -1
if p.position.y < TAILLE_PROJECTILES:
p.position.y = TAILLE_PROJECTILES
p.direction.y *= -1
if p.position.y > HAUTEUR_FENETRE - TAILLE_PROJECTILES:
p.position.y = HAUTEUR_FENETRE - TAILLE_PROJECTILES
p.direction.y *= -1
def decompter_victoire(etat, t):
etat.simulation.decompte_victoire -= t
if etat.simulation.decompte_victoire < 0:
etat.simulation.decompte_victoire = 0.0
def verifier_attente(etat):
if etat.simulation.attente and etat.controles.direction.length() != 0.0:
etat.simulation.attente = False
def avancer_simulation(etat, t):
if etat.simulation.attente:
verifier_attente(etat)
elif not etat.simulation.collision and etat.simulation.decompte_victoire != 0.0:
deplacer_personnage(etat, t)
limiter_deplacement_personnage(etat)
deplacer_projectiles(etat, t)
faire_rebondir_projectiles(etat)
detecter_collisions(etat)
decompter_victoire(etat, t)
####################
#### RENDU ####
####################
def images_caracteres(fonte, caracteres, couleur):
cars = {}
for c in caracteres:
cars[c] = fonte.render(c, True, couleur)
return cars
class Rendu:
def __init__(self, ecran):
self.ecran = ecran
fonte_chrono = pygame.font.SysFont("Mono", TAILLE_CHRONOMETRE, True, False)
self.caracteres_chrono = images_caracteres(fonte_chrono, "0123456789.", COULEUR_CHRONOMETRE)
fonte_fin = pygame.font.SysFont("Mono", TAILLE_FIN_DE_PARTIE, True, False)
self.texte_victoire = fonte_fin.render("VICTOIRE", True, COULEUR_FIN_DE_PARTIE)
self.texte_defaite = fonte_fin.render("DEFAITE", True, COULEUR_FIN_DE_PARTIE)
def ref_sve(v):
return Vector2(v.x, HAUTEUR_FENETRE - v.y)
def initialiser_rendu(etat, ecran):
etat.rendu = Rendu(ecran)
def reinitialiser_rendu(etat):
pass
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 rendre_projectiles(etat):
ecran = etat.rendu.ecran
for p in etat.simulation.projectiles:
position = p.position
pygame.draw.circle(ecran, COULEUR_PROJECTILES, ref_sve(position), TAILLE_PROJECTILES)
def rendre_chronometre(etat):
chrono = f"{etat.simulation.decompte_victoire:06.2f}" #Conversion en chaine
caracteres = etat.rendu.caracteres_chrono
x = MARGES_CHRONOMETRE #x de départ (référentiel écran)
y = MARGES_CHRONOMETRE #y de départ (référentiel écran)
for c in chrono:
etat.rendu.ecran.blit(caracteres[c], (x, y))
#la methode get_width de l'objet image renvoie la largeur de l'image.
x += caracteres[c].get_width()
def rendre_image_centree(ecran, image):
centre_ecran = Vector2(LARGEUR_FENETRE/2, HAUTEUR_FENETRE/2)
centre_image = Vector2(image.get_width()/2, image.get_height()/2)
ecran.blit(image, centre_ecran - centre_image)
def rendre_fin_de_partie(etat):
ecran = etat.rendu.ecran
if etat.simulation.collision :
#si on a perdu
rendre_image_centree(ecran, etat.rendu.texte_defaite)
elif etat.simulation.decompte_victoire == 0.0:
#si la partie est finie et qu'on a pas perdu
rendre_image_centree(ecran, etat.rendu.texte_victoire)
def afficher_rendu(etat, t):
ecran = etat.rendu.ecran
ecran.fill(COULEUR_ARRIERE_PLAN)
rendre_chronometre(etat)
rendre_fin_de_partie(etat)
rendre_projectiles(etat)
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:
if etat.controles.recommencer:
reinitialiser_controles(etat)
reinitialiser_simulation(etat)
reinitialiser_rendu(etat)
t = clock.tick(FPS)/1000
traiter_controles(etat, t)
avancer_simulation(etat,t)
afficher_rendu(etat, t)
pygame.quit()
if __name__ == "__main__":
main()