- Sol sur pygame
- Note clé de sol - Télécharger - Création musicale
- Partition piano avec do re mi fa sol ✓ - Forum Loisirs / Divertissements
- Joyeux anniversaire do re mi fa sol partition piano facile - Forum Musique / Radio / Clip
- Modulenotfounderror: no module named 'pygame' ✓ - Forum Python
- Musique avec do re mi fa sol ✓ - Forum Loisirs / Divertissements
1 réponse
Salut,
Lorsque tu présentes du code, assure-toi au moins que ce soit visualisable, si on lance ton script, on a à peine le temps de voir quelque chose que la fenêtre se ferme directement.
J''imagine que seules les formes planes seront lesquelles où le joueur pourra se déplacer, donc il faut en 1er lieu ajouter un attribut (walkable dans le code) à la classe obstacle, ainsi lorsque le joueur tombera, il pourra se réceptionner sur ces surfaces.
Idem pour les obstacles que peuvent percuter le joueur, peuvent-ils tous le faire ? Un autre attribut (solid dans le code) pourrait s'avérer nécessaire afin de le savoir.
Sachant que le joueur doit avoir connaissance des éléments qui l'entourent, donc des surfaces “ walkable ”, on peut lui fournir dans l'instance du joueur en fourrant cela dans un groupe pygame (bien plus simple à gérer).
Pour faciliter la détection que le joueur est sur un sol, on peut se créer un autre Rect sous le joueur, et s'en servir pour détecter les collisions avec les sols.
Pourquoi ne pas avoir gardé la modification avec la mise à jour du score ?
Voici un code gérant les sauts et chutes du joueur, il y aurait encore des choses à peaufiner et revoir, mais le principe est là, je suis parti du précédent code que je t'avais exposé dans ton sujet précédent.
Il y a aussi désormais une classe gérant l'application.
import random import pygame FPS = 60 # Ajustement de la gravité en fonction de l'IPS GRAVITY = 0.38 * (60 / FPS) ** 2 SCREEN_WIDTH = 800 SCREEN_HEIGHT = 600 SCREEN_COLOR = 120, 170, 255 GROUND_Y = 550 PLAYER_COLOR = 255, 255, 255 BAR_WIDTH = 800 BAR_HEIGHT = 30 EVENT_OBSTACLE_SPAWN = pygame.USEREVENT EVENT_SCORE_UPDATE = pygame.USEREVENT + 1 EVENT_PROGRESS_BAR = pygame.USEREVENT + 2 class Ground(pygame.sprite.Sprite): def __init__(self, y): super().__init__() self.image = pygame.Surface((SCREEN_WIDTH, 100)) self.image.fill((100, 200, 80)) self.rect = self.image.get_rect() self.rect.topleft = 0, y class Player(pygame.sprite.Sprite): def __init__(self, x, y, walkable_group): super().__init__() self.image = pygame.Surface((30, 30)) self.image.fill(PLAYER_COLOR) self.rect = self.image.get_rect() self.rect.bottomleft = x, y self._walkable_group = walkable_group self.x = x self.y = y # Création d'un rectangle d'1 px de haut situé sous le joueur # dont on se servira pour déterminer si le joueur est sur une surface # sur laquelle il peut se mouvoir self._sub_rect = pygame.Rect(self.rect.x, self.rect.bottom, self.rect.w, 1) self._jumping = False self._falling = False # Nombre d'étape max. du saut (ne pas toucher) self._vertical_step = 30 / 60 * FPS self._falling_step = 0 self._gravity = GRAVITY def on_solid_surface(self): # Détermine si le joueur est sur un sol # mise à jour de la position y du « sous » rectangle self._sub_rect.top = self.rect.bottom rects = tuple(sprite.rect for sprite in self._walkable_group) # collidelist retourne -1 si aucune collision parmi les rect. fournis # ou l'indice du 1er rect. en collision de la liste fournie return not self._sub_rect.collidelist(rects) == -1 def jump(self): if not self._jumping and not self._falling: self._jumping_step = self._vertical_step self._jumping = True def collide(self, sprites): return tuple(s for s in sprites if s.rect.colliderect(self.rect)) def update(self): if self._jumping: self.rect.y -= self._jumping_step * self._gravity self._jumping_step -= 1 if not self._jumping_step: self._jumping = False self._falling = True self._falling_step = 0 elif self._falling: self.rect.y += self._falling_step * self._gravity if self.on_solid_surface(): self._falling = False # Ajustement de la position du joueur sur la surface en contact rects = tuple(s.rect for s in self._walkable_group) i = self._sub_rect.collidelist(rects) rect = rects[i] self.rect.bottom = rect.top self._falling_step = min(self._vertical_step, self._falling_step + 1) else: if not self.on_solid_surface(): # Donc, le joueur tombe sans avoir sauté, on le fait rechuter self._falling = True self._falling_step = 0 jumping = property(lambda self: self._jumping) class ScoreBoard(pygame.sprite.Sprite): def __init__(self, x, y): super().__init__() self.x = x self.y = y self.score = 0 self.font = pygame.font.SysFont('Arial', 30) self.color = (255, 255, 255) self.image = self.font.render('0', True, self.color) self.rect = self.image.get_rect() self.rect.topright = x, y def add(self, amount): self.score += amount self.image = self.font.render(str(self.score), True, self.color) self.rect = self.image.get_rect() self.rect.topright = self.x, self.y class Obstacle(pygame.sprite.Sprite): def __init__(self, x, y, width, height): super().__init__() self.image = pygame.Surface((width, height), pygame.SRCALPHA) self.rect = self.image.get_rect() self.rect.bottomleft = x, y self.walkable = False # spécifie que le joueur peut se mouvoir dessus self.solid = False # spécifie que le joueur entrera en contact avec lui def update(self): self.rect.x -= 6 class Cercle(Obstacle): def __init__(self, x, y): super().__init__(x, y, 60, 60) # position du cercle sur l'écran pygame.draw.circle(self.image, (0, 155, 155), (30, 30), 30) class Triangle(Obstacle): def __init__(self, x, y): super().__init__(x, y, 60, 60) pygame.draw.polygon(self.image, (0, 255, 0), ((30, 0), (0, 60), (60, 60))) self.solid = True class Rectangle(Obstacle): def __init__(self, x, y): # XXX Aucun intérêt de créer un rect, l'image EST un rectangle # d'autant que ça va poser un problème avec les collisions d'avoir une image # bien plus grande que ce qui est visualisable. # super().__init__(x, y, 70, 135) # pygame.draw.rect(self.image, (12, 48, 255), (5, 5, 130, 130)) super().__init__(x, y, 70, 130) self.image.fill((12, 48, 255)) self.walkable = True class BasPlateforme(Obstacle): def __init__(self, x, y): # XXX Idem qu'au-dessus # super().__init__(x, y, 252, 60) # pygame.draw.rect(self.image, (88, 35, 255), (10, 10, 670, 50)) super().__init__(x, y, 250, 60) self.image.fill((88, 35, 255)) self.walkable = True class DoubleTriangle(Obstacle): def __init__(self, x, y): super().__init__(x, y, 120, 60) pygame.draw.polygon(self.image, (255, 255, 0), ((30, 0), (0, 60), (60, 60))) pygame.draw.polygon(self.image, (255, 255, 0), ((90, 0), (60, 60), (120, 60))) self.rect = self.image.get_rect() self.rect.bottomleft = x, y self.solid = True class Plateforme(Obstacle): def __init__(self, x, y): # XXX Idem que pour Rectangle # super().__init__(x, y, 187, 187) # pygame.draw.rect(self.image, (185, 235, 255), (10, 10, 670, 50)) # TODO voir pour mettre la position y exacte à l'instance plutôt qu'ici super().__init__(x, y - 80, 187, 45) self.image.fill((185, 235, 255)) self.walkable = True class ProgressBar(pygame.sprite.Sprite): def __init__(self, width, height, color): super().__init__() self.image = pygame.Surface((width, height), pygame.SRCALPHA).convert_alpha() self.rect = self.image.get_rect() self.color = color self._percent = 0 def is_full(self): return self._percent == 100 def increase(self, percent): if self._percent == 100: return self._percent += percent self._percent = min(100, self._percent) width = self.rect.w / 100 * self._percent pygame.draw.rect(self.image, self.color, (0, 0, width, self.rect.h)) class Button(pygame.sprite.Sprite): def __init__(self, width, height, text, font, command): super().__init__() self.image = pygame.Surface((width, height)).convert() self.rect = self.image.get_rect() self.image.fill((125, 20, 200)) self._text = font.render(text.upper(), True, (255, 255, 255)) self.image.blit(self._text, self._text.get_rect(center=self.rect.center)) self._command = command self._focus = False def call(self): self._command() @property def focus(self): return self._focus focus.setter def focus(self, bool_): assert bool_ in (0, 1) [self.focus_out, self.focus_in][bool_]() def focus_in(self): if self._focus: return self._focus = True pygame.draw.rect(self.image, (255, 255, 0), (0, 0, *self.rect.size), 5) def focus_out(self): if not self._focus: return self._focus = False pygame.draw.rect(self.image, (125, 20, 200), (0, 0, *self.rect.size), 5) class Menu: def __init__(self, group): self._group = group self._font = pygame.font.SysFont('mono', 24, True) self._buttons = [] self._focus_button = 0 def add_button(self, text, command): button = Button(300, 60, text, self._font, command) self._buttons.append(button) def display(self): rect = self._buttons[0].rect bt_len = len(self._buttons) x = (SCREEN_WIDTH - rect.w) / 2 y = SCREEN_HEIGHT - rect.h * bt_len - 50 * (bt_len - 1) y /= 2 for button in self._buttons: button.rect.topleft = x, y y += rect.h + 70 self._group.add(self._buttons) self._buttons[0].focus(True) def update(self, events): for evt in events: if evt.type == pygame.KEYDOWN: if evt.key == pygame.K_TAB: self._buttons[self._focus_button].focus_out() # Si une touche maj. n'est pas pressée if not evt.mod & pygame.KMOD_SHIFT: self._focus_button += 1 try: self._buttons[self._focus_button].focus_in() except IndexError: self._focus_button = 0 self._buttons[self._focus_button].focus_in() else: self._focus_button -= 1 try: self._buttons[self._focus_button].focus_in() except IndexError: self._focus_button = -1 self._buttons[self._focus_button].focus_in() elif evt.key == pygame.K_RETURN: self._buttons[self._focus_button].call() class Game: def __init__(self, group): self.group = group self.obstacle_group = pygame.sprite.Group() # Groupe des surfaces parcourables que l'on passe au joueur self.walkable_group = pygame.sprite.Group() # Groupe des surfaces qui peuvent enter en contact avec le joueur self.solid_group = pygame.sprite.Group() self.player = Player(100, 0, self.walkable_group) self.ground = Ground(GROUND_Y) self.score = ScoreBoard(SCREEN_WIDTH - 10, 10) self.progress_bar = ProgressBar(BAR_WIDTH, BAR_HEIGHT, (255, 165, 0, 128)) self.progress_bar.rect.topleft = 0, 0 self.group.add(self.ground, self.score, self.progress_bar, self.player) # Ajout du sol dans ce group self.walkable_group.add(self.ground) self._obstacle_spawn_timer() pygame.time.set_timer(EVENT_SCORE_UPDATE, 5000) pygame.time.set_timer(EVENT_PROGRESS_BAR, 100) def _obstacle_spawn_timer(self, ms=-1): if ms == -1: # ms = random.randrange(490, 750) ms = random.randrange(1800, 2500) pygame.time.set_timer(EVENT_OBSTACLE_SPAWN, ms) def obstacle_spawn(self): obstacle_class = random.choices( (Cercle, Triangle, Rectangle, DoubleTriangle, Plateforme, BasPlateforme), # (Plateforme, BasPlateforme), (5, 8, 7, 8, 9, 8), # (5, 8), )[0] obstacle = obstacle_class(SCREEN_WIDTH, GROUND_Y) self.group.add(obstacle) self.obstacle_group.add(obstacle) if obstacle.solid: self.solid_group.add(obstacle) if obstacle.walkable: self.walkable_group.add(obstacle) def update(self, events): for event in events: if event.type == pygame.KEYDOWN: # On ne fait pas : truc == machin or bidule # mais : truc == machin or truc == bidule # ou plus simplement avec un tuple comme ci-dessous if event.key in (pygame.K_UP, pygame.K_SPACE): self.player.jump() elif event.type == EVENT_OBSTACLE_SPAWN: self.obstacle_spawn() self._obstacle_spawn_timer() elif event.type == EVENT_SCORE_UPDATE: self.score.add(5) elif event.type == EVENT_PROGRESS_BAR: self.progress_bar.increase(0.1) if self.progress_bar.is_full(): print('progression réussie !') # Annulation du timer (mise à 0 des ms) pygame.time.set_timer(EVENT_PROGRESS_BAR, 0) # Faire ce qu'il y a à faire. # Par exemple, arrêt des autres animations self._obstacle_spawn_timer(0) pygame.time.set_timer(EVENT_SCORE_UPDATE, 0) # Collision obstacles dur avec le joueur for sprite in self.player.collide(self.solid_group): # suppression du sprite de tous les groupes sprite.kill() # Suppression des sprites hors écran for sprite in self.obstacle_group.sprites(): if sprite.rect.right < 0: sprite.kill() class Application: def __init__(self): pygame.init() self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) # pygame.mouse.set_visible(False) pygame.display.set_caption("Geo") self.clock = pygame.time.Clock() self.draw_group = pygame.sprite.RenderUpdates() self.running = True def _sprites_clear(self, surface, rect): surface.fill(SCREEN_COLOR, rect) def menu(self): self.draw_group.empty() self.interface = Menu(self.draw_group) self.interface.add_button("JOUER", self.play) self.interface.add_button("OPTIONS", lambda: print("à réaliser")) self.interface.add_button("QUITTER", self.quit) self.interface.display() def play(self): self.draw_group.empty() self.interface = Game(self.draw_group) def quit(self): print("À la prochaine !") self.running = False def run(self): self.screen.fill(SCREEN_COLOR) pygame.display.update() while self.running: self.clock.tick(FPS) events = pygame.event.get() for evt in events: if evt.type == pygame.QUIT: return self.quit() self.interface.update(events) self.draw_group.clear(self.screen, self._sprites_clear) self.draw_group.update() rects = self.draw_group.draw(self.screen) pygame.display.update(rects) pygame.quit() if __name__ == '__main__': app = Application() app.menu() app.run()
Touche TAB (et MAJ) pour sélectionner, ENTRÉE pour lancer le jeu.