Problème collisions pygame
Résolu_rUBEN__ Messages postés 8 Statut Membre -
Bonjour, j'ai commencé à coder un petit jeu de plateforme, mais je rencontre un problème avec la détection de collisions, je n'arrive pas à faire tomber le joueur quand il n'est plus sur une plateforme:
il y a la map et comment elle est dessinée :
map1 = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
]
for lineIndex in range(len(map1)):
for blockIndex in range(len(map1[lineIndex])):
blockValue = map1[lineIndex][blockIndex]
x = blockIndex * blockSize
y = lineIndex * blockSize
rect = pygame.Rect(x,y,blockSize,blockSize)
if blockValue == 0:
pass
elif blockValue == 1:
pygame.draw.rect(screen, (0, 200, 0), (x, y, blockSize, blockSize))
et les collisions avec le joueur :
if not blockValue == 0 :
if (
player.x + player.w > x
and player.x < x + blockSize
and player.y + player.h > y
and player.y + player.h < y + 20
and player.jump == False
):
player.fall = False
player.velocity = 0
player.y = y - player.h
else:
player.fall =True
if (
player.x + player.w > x
and player.x < x + blockSize
and player.y < y + blockSize
and player.y > y + blockSize - 20
):
player.y = y + blockSize
player.jump=False
player.fall=True
player.velocity = 0
elif (
player.x + player.w > x
and player.x + player.w < x + player.speed + 2
and player.y + player.h > y
and player.y < y + blockSize
):
player.x -= player.speed
elif (
player.x < x + blockSize
and player.x > x + blockSize - player.speed - 2
and player.y + player.h > y
and player.y < y + blockSize
):
player.x += player.speed
C'est la ligne
else:
player.fall =True
qui pose problème car le joueur ne peux plus sauter du tout.
Voila voila
Merci si qqun peut m'aider

Windows / Chrome 122.0.0.0
3 réponses
Salut,
Il n'y a pas de méthodes magiques, c'est à toi de gérer ta collision en fonction de l'état et position du joueur par rapport aux éléments.
Un exemple très simplifié de la gestion du joueur en fonction de s'il est en mouvement, en chute, test de la « collision » avec les plateformes.
Si ça peut te donner une idée de comment faire ça avec des sprites.
import pygame
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
FPS = 60
ACCELERATION = 0.458
SCREEN_COLOR = pygame.Color(10, 10, 155, 255)
GROUND_COLOR = pygame.Color(101, 67, 33, 255)
PLATFORM_COLOR = pygame.Color('#ff0000')
PLAYER_BG_COLOR = pygame.Color('lightGreen')
PLAYER_FG_COLOR = pygame.Color('limeGreen')
def sprites_clear(surface, rects):
surface.fill(SCREEN_COLOR, rects)
def get_sprite_below(top_sprite, sprites):
'''
Reourne le sprite parmi "sprites" se situant en dessous
de top_sprite ou None si aucun n'y est.
'''
rect = pygame.Rect(
top_sprite.rect.x,
top_sprite.rect.bottom + 1,
top_sprite.rect.w,
1,
)
for sprite in sprites:
if rect.colliderect(sprite.rect):
return sprite
class Ground(pygame.sprite.Sprite):
def __init__(self, width, height):
super().__init__()
self.image = pygame.Surface((width, height)).convert()
self.image.fill(GROUND_COLOR)
self.rect = self.image.get_rect()
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface(
(24, 24), pygame.SRCALPHA
).convert_alpha()
self.image.fill(PLAYER_BG_COLOR)
self.rect = self.image.get_rect()
# Yeux
eye_size = self.rect.w // 6
for x in (eye_size, self.rect.right - eye_size * 2):
pygame.draw.rect(
self.image, PLAYER_FG_COLOR, (x, 6, 4, 4)
)
# Bouche
mouth_rect = pygame.Rect(
eye_size,
self.rect.bottom - eye_size * 2,
self.rect.w - eye_size * 2,
4,
)
pygame.draw.rect(self.image, PLAYER_FG_COLOR, mouth_rect)
self.jumping = False
self.falling = False
self.horizontal_step = 6
self.vertical_step = 30
self.acceleration = ACCELERATION
def move(self, amount):
self.rect.x += amount
def move_left(self):
self.move(-self.horizontal_step)
def move_right(self):
self.move(self.horizontal_step)
def jump(self):
if not self.jumping and not self.falling:
self.jumping_step = self.vertical_step
self.jumping = True
def update(self):
if self.jumping:
self.rect.y -= self.jumping_step * self.acceleration
self.jumping_step -= 1
if self.jumping_step == 0:
self.jumping = False
self.falling = True
self.falling_step = 1
elif self.falling:
self.rect.y += self.falling_step * self.acceleration
self.falling_step = min(
self.vertical_step, self.falling_step + 1
)
class Platform(pygame.sprite.Sprite):
def __init__(self, x, y, width, height=16):
super().__init__()
self.image = pygame.Surface(
(width, height), pygame.SRCALPHA
).convert_alpha()
radius = height // 2
pygame.draw.circle(
self.image, PLATFORM_COLOR, (radius, radius), radius
)
pygame.draw.circle(
self.image,
PLATFORM_COLOR,
(width - radius, radius),
radius,
)
pygame.draw.rect(
self.image,
PLATFORM_COLOR,
(radius, 0, width - radius * 2, height),
)
self.rect = self.image.get_rect()
self.rect.topleft = x, y
class Game:
def __init__(self, title):
self.screen = pygame.display.set_mode(
(SCREEN_WIDTH, SCREEN_HEIGHT)
)
pygame.display.set_caption(title)
self.screen.fill(SCREEN_COLOR)
pygame.display.update()
# Rect de la fenêtre
self.rect = self.screen.get_rect()
# Groupes de sprites
self.groups = dict(
# Groupe de sprites sur lesquels le joueur peut se balader
walkable=pygame.sprite.Group(),
# Sprites à afficher à l'écran
display=pygame.sprite.RenderUpdates(),
)
self._init_sprites()
# Liaisons mouvements latéraux => fonctions joueur
self._player_motions = {
pygame.K_LEFT: self.player.move_left,
pygame.K_RIGHT: self.player.move_right,
}
def _init_sprites(self):
# Inialisations des éléments du jeu
ground = Ground(self.rect.w, 50)
ground.rect.bottom = self.rect.bottom
self.groups['display'].add(ground)
self.groups['walkable'].add(ground)
for x, y, width in (
(150, ground.rect.top - 150, 100),
(300, ground.rect.top - 300, 100),
(450, ground.rect.top - 500, 80),
(400, 320, 65),
(500, 305, 65),
(600, 290, 65),
):
platform = Platform(x, y, width)
self.groups['walkable'].add(platform)
self.groups['display'].add(platform)
self.player = Player()
self.groups['display'].add(self.player)
# Position initiale du joueur
self.player.rect.bottomleft = 100, ground.rect.top
def _manage_player(self, pressed_keys):
for key in self._player_motions:
if pressed_keys[key]:
# Appel de la fonction de déplacement
self._player_motions[key]()
# Repositionnement joueur si hors écran
if self.player.rect.x < 0:
self.player.rect.x = 0
elif self.player.rect.right > self.rect.right:
self.player.rect.right = self.rect.right
if not self.player.falling:
# On regarde s'il y a une surface sous ses pieds
below_sprite = get_sprite_below(
self.player, self.groups['walkable']
)
if not below_sprite:
self.player.falling = True
break
if self.player.falling:
below_sprite = get_sprite_below(
self.player, self.groups['walkable']
)
# le joueur est arrivé sur une surface
if below_sprite:
self.player.falling = False
self.player.rect.bottom = below_sprite.rect.top
def run(self):
clock = pygame.time.Clock()
running = True
while running:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
self.player.jump()
elif event.key == pygame.K_ESCAPE:
running = False
keys = pygame.key.get_pressed()
self._manage_player(keys)
self.groups['display'].clear(self.screen, sprites_clear)
# Appel de la méthode update de tous les sprites du groupe display
self.groups['display'].update()
# Affichage des sprites du groupes
rects = self.groups['display'].draw(self.screen)
# Mise à jour des rectangles ayant eu un changement
pygame.display.update(rects)
game = Game('Hop hop hop')
game.run()
pygame.quit()
La gestion des positions s'effectue donc avec les rect des sprites.
Là, cela fonctionne pas trop mal, mais ce n'est pas géré super bien, il suffit d'accélérer la vitesse de chute ou de réduire l'épaisseur des plateformes pour que le joueur lors de sa chute passe à travers, l'idée pour y pallier serait alors de faire comme en général dans les jeux, avoir un rect de « hitbox » un peu plus grand que la taille effective de l'image de façon à détecter une collision plus facilement.
On pourrait utiliser pour ce faire spritecollide qui permet de passer sa propre fonction de test via le paramètre collided.
https://www.pygame.org/docs/ref/sprite.html#pygame.sprite.spritecollide
Ou alors avec une méthode dans la classe Player (ou Plateform) afin d'utiliser le Rect de la hitbox au lieu du Rect de l'image.
Bonne continuation.
Salut, ça me semble mal embarqué.
Les collisions dans un jeu, ce n'est pas ce qu'il y a de plus facile à gérer, encore moins lorsqu'il y a un mécanisme de saut et chute libre.
Pygame fournit des méthodes et fonctions pour tester les collisions, dans la classe Rect et le module sprite.
Faire un jeu, même des plus simples, sans se servir de sprites, de groupe de sprites, de rect, est tout de même dommage, cela simplifie des tas de choses.
Comment ton mécanisme de chute est-il implémenté, chute auto du joueur tant que joueur.fall est à True ?
Comment point de vue algo gèrerais-tu déplacement, saut et chute du joueur ?
Bonjour,
Voici la partie du code de la classe Player :
class Player:
def __init__(self,x,y):
self.x = x
self.y = y
self.w = 100
self.h = 140
self.speed = 5
self.velocity = 0
self.maxVelocity = 15
self.jumpSpeed, self.fallSpeed = 1, 1
self.jump = False
self.fall = False
self.rect = pygame.Rect(self.x, self.y, self.w, self.h)
self.collisionRight, self.collisionLeft = False, False
self.canMove = False
self.show = False
self.position = "idle"
self.direction = "right"
self.rowX = 0
self.rowY = 0
self.timeR = 0
self.timeL = 0
self.maxTime = 10
def events(self, keyPressed):
if self.show:
self.rect = pygame.Rect(self.x, self.y, self.w, self.h)
screen.blit(playeR, (self.x, self.y), (self.rowX * self.w, self.rowY * self.h, self.w, self.h))
if self.direction == "right":
self.timeR += 1
if self.timeR >= self.maxTime:
self.timeR = 0
self.rowX += 1
if self.position == "idle":
self.rowY = 0
if self.rowX >= 9:
self.rowX = 0
elif self.position=="walk":
self.rowY = 1
if self.rowX >= 5:
self.rowX = 0
elif self.direction == "left":
self.timeL += 1
if self.timeL >= self.maxTime:
self.timeL = 0
self.rowX += 1
if self.position == "idle":
self.rowY = 2
if self.rowX >= 9:
self.rowX = 0
elif self.position == "walk":
self.rowY = 3
if self.rowX >= 5:
self.rowX = 0
if self.canMove:
self.y -= self.velocity
if keyPressed[K_d] and not self.collisionRight:
self.x += self.speed
self.direction="right"
self.position="walk"
elif keyPressed[K_q] and not self.collisionLeft:
self.x -= self.speed
self.direction="left"
self.position="walk"
else:self.position="idle"
if keyPressed[K_z] and not self.jump and not self.fall:
self.jump = True
self.position="idle"
if self.jump:
if self.velocity < self.maxVelocity:
self.velocity += self.jumpSpeed
else:
self.velocity = 0
self.jump=False
self.fall=True
elif self.fall:
self.velocity -= self.fallSpeed
Et pour les méthodes de pygame, je n'ai pas trouvé comment détecter si le joueur touche la droite, le haut ou la gauche d'un bloc, je n'ai que "rect1.colliderect(rect2)".
Du coup au final j'ai réussi merci beaucoup
:)