Problème collisions pygame
Résolu_rUBEN__ Messages postés 6 Date d'inscription samedi 2 mars 2024 Statut Membre Dernière intervention 6 novembre 2024 - 4 mars 2024 à 20:24
- Problème collisions pygame
- Sprite sheet pygame - Forum Python
- Problème affichage pygame - Forum Python
- Collisions SHA-1 - Forum Programmation
- Pygame rotation - Forum Python
- Get_pressed pygame ✓ - Forum Python
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 ?
3 mars 2024 à 13:46
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)".
4 mars 2024 à 20:24
Du coup au final j'ai réussi merci beaucoup
:)