Aller au contenu

Rebonds des projectiles

On veut que nos projectiles rebondissent contre les bords de l'écran.

Idée de rebond

Quand un projectile arrive contre un bord de l'écran, on limite son mouvement comme pour le personnage et on inverse sa vitesse perpendiculairement au bord touché.

TODO (prof) schema ici

Simulation

On peut donc écrire une fonction faire_rebondir_projectiles sur le même modèle que la fonction limiter_deplacements_personnage.

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

On l'appelle dans la simulation, après avoir déplacé les projectiles.

def avancer_simulation(etat, t):
    deplacer_personnage(etat, t)
    limiter_deplacement_personnage(etat)
    deplacer_projectiles(etat, t)
    faire_rebondir_projectiles(etat)
    detecter_collisions(etat)

Si on execute le jeu, et qu'on evite un peu les projectiles, on peut constater que les projectiles rebondissent.

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

## Rendu

COULEUR_ARRIERE_PLAN = (0, 0, 0)
COULEUR_PERSONNAGE = (255, 146, 205)
COULEUR_PROJECTILES = (146, 255, 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)
        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


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 avancer_simulation(etat, t):
    deplacer_personnage(etat, t)
    limiter_deplacement_personnage(etat)
    deplacer_projectiles(etat, t)
    faire_rebondir_projectiles(etat)
    detecter_collisions(etat)

####################
####   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 reinitialiser_rendu(etat):
    ecran = etat.rendu.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 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 afficher_rendu(etat, t):
    ecran = etat.rendu.ecran
    ecran.fill(COULEUR_ARRIERE_PLAN)

    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)

        if not etat.simulation.collision:
            avancer_simulation(etat, t)

        afficher_rendu(etat, t)

    pygame.quit()

if __name__ == "__main__":
    main()