Le mouvement gauche - droite ne pose pas de problème. C'est le mouvement vertical qu'il faut travailler.
Un mouvement vertical réaliste sera obtenu grâce à trois paramètres :
Quand le joueur a les pieds sur terre, $y$ ne change pas, $v_y = 0$ et $g$ n'agit pas.
Quand le joueur saute, au début de sont saut, $v_y = V$, où $V < 0$ est une vitesse de saut à choisir pour un saut réaliste. Puis à chaque période de temps, on a : $y \leftarrow y + v_y$ et $v_y \leftarrow v_y + g$.
Ainsi quand $v_y < 0$, le sprite monte à l'écran et quand $v_y > 0$ il descend. L'action de $g$ est qu'au début du saut, le sprite monte. Puis il perd de la vitesse, atteint son sommet et se met à descendre.
def jump(self):
"""
amorce un saut
"""
self.vy = self.JUMP_VELOCITY
...
def update_position(self, plateformes):
"""
Calcule la nouvelle position tenant compte de la vitesse
courante et sans dépasser les limites
"""
self.rect.centerx += self.vx
self.vy += self.G #pesanteur
self.rect.centery += self.vy
if self.rect.centerx > self.xmax:
self.rect.centerx = self.xmax
elif self.rect.centerx < self.xmin:
self.rect.centerx = self.xmin
self.vx *= 0.2
if abs(self.vx) < 1:
self.vx = 0
Si on fait ce qui précède, les plates-formes sont ignorées et le sprite tombe aussitôt à travers le sol.
Pour résoudre ce problème, on va détecter si les pieds du joueurs ont traversé la ligne supérieure d'une plate-forme en descendant. Le personnage sera toujours considéré en chute libre. Si sa chute lui fait traverser une plate-forme, on arrête la chute.
self.vy > 0… seulement si le sprite descendpf,pf au début de son mouvementself.rect.bottom - self.vy ⇐ pf.rect.toppf à la fin de son mouvementself.rect.bottom > pf.rect.topNotez que l'on fait toujours tomber le sprite. Comme on le ramène aussitôt s'il traverse la plate-forme, on ne le voit pas traverser, il semble immobile.
def update_position(self, plateformes):
"""
plateformes: liste des plates-formes
Calcule la nouvelle position tenant compte de la vitesse
courante et sans dépasser les limites
"""
self.rect.centerx += self.vx
self.vy += self.G #pesanteur
self.rect.centery += self.vy
if self.rect.centerx > self.xmax:
self.rect.centerx = self.xmax
elif self.rect.centerx < self.xmin:
self.rect.centerx = self.xmin
self.vx *= 0.2
if self.vy > 0:
for pf in pygame.sprite.spritecollide(self, plateformes, False):
if self.rect.bottom - self.vy <= pf.rect.top < self.rect.bottom:
self.vy = 0
self.rect.bottom = pf.rect.top
break
if abs(self.vx) < 1:
self.vx = 0
On ne souhaite pas que le sprite puisse sauter s'il est déjà en train de sauter ou qu'il est en train de chuter. On rajoute alors une marqueur jumping qui passe à True chaque fois que le sprite saute ou tombe.
# fichier player.py
import pygame
class Player(pygame.sprite.Sprite):
COLOR = (255, 255, 0)
VELOCITY = 10
JUMP_VELOCITY = -20
HEIGHT = 50
WIDTH = 20
G = 2
def __init__(self, x0, y0, xmin, xmax):
"""
x0, y0: position initiale du coin inférieur gauche
xmin, xmax: valeurs à ne pas dépasser
"""
super().__init__()
self.image = pygame.Surface((self.WIDTH, self.HEIGHT))
self.image.fill(self.COLOR)
self.rect = self.image.get_rect()
self.rect.left = x0
self.rect.bottom = y0
self.xmin = xmin
self.xmax = xmax
self.jumping = False
self.vx = 0
self.vy = 0
def move_left(self):
"""
amorce un mouvement vers la gauche
"""
self.vx = -self.VELOCITY
def move_right(self):
"""
amorce un mouvement vers la droite
"""
self.vx = self.VELOCITY
def jump(self):
"""
amorce un saut
"""
if not self.jumping:
self.vy = self.JUMP_VELOCITY
self.jumping = True
def update_position(self, plateformes):
"""
plateformes: liste des plates-formes
Calcule la nouvelle position tenant compte de la vitesse
courante et sans dépasser les limites
"""
self.rect.centerx += self.vx
self.vy += self.G #pesanteur
self.rect.centery += self.vy
if self.rect.centerx > self.xmax:
self.rect.centerx = self.xmax
elif self.rect.centerx < self.xmin:
self.rect.centerx = self.xmin
self.vx *= 0.2
if self.vy > 0:
self.jumping = True
for pf in pygame.sprite.spritecollide(self, plateformes, False):
if self.rect.bottom - self.vy <= pf.rect.top < self.rect.bottom:
self.vy = 0
self.rect.bottom = pf.rect.top
self.jumping = False
break
if abs(self.vx) < 1:
self.vx = 0