Problème collisions pygame

Résolu
_rUBEN__ Messages postés 8 Statut Membre -  
_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

  1. guili
     

    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.

    1
    1. _rUBEN__ Messages postés 8 Statut Membre 1
       

      Du coup au final j'ai réussi merci beaucoup 

      :)

      1
  2. guili
     

    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 ?

    0
  3. _rUBEN__ Messages postés 8 Statut Membre 1
     

    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)".

    0