Coder le jeu Geometry dash en python

Résolu

Bonjour bonjour !!

Je fais un geometry dash pour l'école et je suis de ce qu'il y a de plus débutant.

J'ai pour l'instant mon personnage qui se déplace et qui saute. L'étape suivant est de créer des plateformes dans l'air et des obstacles ; ainsi que de faire défiler le background. Je commence par les obstacles, et les plateformes mais j'arrive pas a les connecter a mon code.

Ma question est la suivante ; est-ce que quelqu'un aurait un lien vers un tutoriel de plateforme et d'obstacles que je pourrai facilement adapter et comprendre pour mon code ?

Merci

6 réponses

  1. Modérateur

    Bonjour,

    Sans voir ton code, difficile de réponder.

    Mais en faisant une petite recherche google, j'ai trouvé cette vidéo qui me paraît répondre exactement à ta question, et la description fournit le lien vers le code.

    Le code est basé sur ursina.

    Bonne chance

    1. Salut merci de me répondre :)

      Ouais j'ai vu cette vidéo 
      Mon code ressemble à ça; mais je sais pas j'arrive pas :(

      import pygame
      
      #taille de l'ecran et initialisation
      LARGEUR_ECRAN = 1600
      HAUTEUR_ECRAN = 850
      
      
      pygame.display.set_caption("test jeu")
      ecran = pygame.display.set_mode([LARGEUR_ECRAN, HAUTEUR_ECRAN])
      
      pygame.init()
      
      #saut
      x = 800 #position du cube au début
      y = 600 #position du cube au début
      width = 80
      height = 80
      vel = 15
      
      isJump = False
      jumpCount = 10 #hauteur du saut
      
      """on peut mettre des classes entre là et la boucle continuelle"""
      
      #boucle
      run = True
      
      while run:
          pygame.time.delay(30) #clock
      
          for event in pygame.event.get():
              if event.type == pygame.QUIT:
                  run = False
      
          keys = pygame.key.get_pressed()
      
          #directions
          if keys[pygame.K_LEFT] and x > vel:
              x -= vel
          if keys[pygame.K_RIGHT] and x < 1600 - vel - width:
              x += vel
      
          #saut
          if not (isJump):
      
              if keys[pygame.K_SPACE]:
                  isJump = True
          else:
              if jumpCount >= -10:
                  y -= (jumpCount * abs(jumpCount)) * 0.5
                  jumpCount -= 1
              else:
                  jumpCount = 10
                  isJump = False
      
      
          #appeler les elements
          ecran.fill((120, 170, 255))
          pygame.draw.rect(ecran, (100, 200, 80), (0, 680, 1600, 350))
          pygame.draw.rect(ecran, (255, 255, 255), (x, y, width, height))
          pygame.display.update()
      
      pygame.quit()
      1. Modérateur

        Bonjour,

        Si tu ne veux pas utiliser ursina et que tu préfère utiliser pygame, tu peux regarder ce dépôt.

        Bonne chance

        1. Salut.

          Le truc, c'est que pour faire même un simple jeu, il faut beaucoup se familiariser avec la bibliothèque dont on veut se servir, donc en faisant des tas de tests, et bien évidemment cela prend du temps, faire un jeu avec obstacles et plateformes demande beaucoup de temps, que ce soit sur la mécanique du jeu ou sur la réflexion, stratégie du jeu, conception des maps, etc. Sans compter qu'évidemment il faut maitriser les concepts primaires de python, y compris l'objet, faire un jeu complexe sans modèle objet est pour moi mission impossible, enfin, c'est faisable mais au détriment de la lisibilité et organisation du code, surtout qu'en python tout est objet.
          Faire un jeu avec pygame, c'est utiliser des sprites, un sprite pygame doit avoir comme attributs image et rect, donc player, obstacles, plateformes et toutes entités doivent être des sprites.


          Je ne sais ce que tu nommes par "geometry dash", faire apparaitre des obstacles que le joueur doit éviter ou collecter, ce n'est pas difficile (enfin pour si peu que l'on connaisse un peu pygame).

          .

          Fais à la va vite selon ton code.

          import pygame
          import random
          
          SCREEN_WIDTH = 800
          SCREEN_HEIGHT = 600
          SCREEN_COLOR = 120, 170, 255
          
          FLOOR_Y = 550
          
          PLAYER_COLOR = 255, 255, 255
          
          EVENT_OBSTACLE_SPAWN = pygame.USEREVENT + 1
          
          FPS = 60
          
          
          class Floor(pygame.sprite.Sprite):
              def __init__(self, y):
                  super().__init__()
                  self.image = pygame.Surface((SCREEN_RECT.w, 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):
                  super().__init__()
                  self.image = pygame.Surface((50, 50))
                  self.image.fill(PLAYER_COLOR)
                  self.rect = self.image.get_rect()
                  self.rect.bottomleft = x, y
                  self.x = x
                  self.y = y
                  self.jumping = False
                  self.jump_count = 20
          
              def move(self, amount):
                  self.rect.x += amount
          
              def jump(self):
                  self.jumping = True
          
              def collide(self, sprites):
                  # Retourne les sprites en collision avec le joueur
                  return tuple(sprites[i] for i in self.rect.collidelistall(sprites))
          
              def update(self):
                  if self.jumping:
                      # step = -(0.5 * self.jump_count ** 2)
                      step = -(0.05 * self.jump_count ** 2) * (60 / FPS)
                      if self.jump_count > 0:
                          step *= -1
                      self.rect.y -= step
                      self.jump_count -= 1
                      if self.rect.bottom >= self.y:
                          self.rect.bottom = self.y
                          self.jumping = False
                          self.jump_count = 20
          
          
          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
          
              def update(self):
                  self.rect.x -= 5
          
          
          class Friend(Obstacle):
              points = 10
              def __init__(self, x, y):
                  super().__init__(x, y, 60, 60)
                  pygame.draw.circle(self.image, (0, 155, 155), (30, 30), 30)
          
          
          class Ennemy(Obstacle):
              points = -15
              def __init__(self, x, y):
                  super().__init__(x, y, 60, 60)
                  pygame.draw.polygon(self.image, (0, 0, 0), ((30, 0), (0, 60), (60, 60)))
          
          
          def obstacle_spawn():
              # Choix d'un obstacle (30% ami, 70% ennemi)
              obstacle_class = random.choices((Friend, Ennemy), (3, 7))[0]
              obstacle = obstacle_class(SCREEN_RECT.w, FLOOR_Y)
              draw_group.add(obstacle)
              obstacles_group.add(obstacle)
          
          
          def spawn_timer():
              ms = random.randrange(500, 1100)
              pygame.time.set_timer(EVENT_OBSTACLE_SPAWN, ms)
          
          
          def sprites_clear(surface, rect):
              surface.fill(SCREEN_COLOR, rect)
          
          
          def pause():
              clock = pygame.time.Clock()
              while True:
                  clock.tick(FPS)
                  for event in pygame.event.get():
                      if event.type == pygame.KEYDOWN and event.key == pygame.K_p:
                          return
          
          
          screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
          SCREEN_RECT = screen.get_rect()
          pygame.mouse.set_visible(False)
          pygame.display.set_caption("test jeu")
          
          draw_group = pygame.sprite.RenderUpdates()
          obstacles_group = pygame.sprite.Group()
          
          player = Player(150, FLOOR_Y)
          draw_group.add(player)
          
          floor = Floor(FLOOR_Y)
          draw_group.add(floor)
          
          screen.fill(SCREEN_COLOR)
          pygame.display.update()
          
          clock = pygame.time.Clock()
          running = True
          points = 0
          spawn_timer()
          game_paused = False
          
          while running:
              clock.tick(FPS)
              for event in pygame.event.get():
                  if event.type == pygame.KEYDOWN:
                      if event.key == pygame.K_UP and not player.jumping:
                          player.jump()
                      elif event.key == pygame.K_p:
                          game_paused = not game_paused
                          if game_paused:
                              pause()
                              game_paused = False
                      elif event.key == pygame.K_ESCAPE:
                          running = False
                  elif event.type == EVENT_OBSTACLE_SPAWN:
                      obstacle_spawn()
                      spawn_timer()
                      draw_group.remove(player)
                      draw_group.add(player)
                  elif event.type == pygame.QUIT:
                      running = False
          
              # Collision obstacles avec le joueur
              for sprite in player.collide(obstacles_group.sprites()):
                  points += sprite.points
                  obstacles_group.remove(sprite)
                  draw_group.remove(sprite)
                  print("points : ", points)
          
              # Suppression des sprites hors écran
              for sprite in obstacles_group.copy().sprites():
                  if sprite.rect.right < SCREEN_RECT.x:
                      obstacles_group.remove(sprite)
                      draw_group.remove(sprite)
          
              draw_group.clear(screen, sprites_clear)
              draw_group.update()
              rects = draw_group.draw(screen)
              pygame.display.update(rects)
          
          
          pygame.quit()
          


          Maintenant si tu veux créer également des plateformes avec des ennemis et des objets à collecter, cela ne va pas pouvoir se faire à la volée, il va falloir créer une map (ou plusieurs si niveaux), gérer la vie, le score, et tout ce qui est nécessaire au bon fonctionnement du jeu.

          .

          Alors pour un simple projet d'étudiant, peut-être devrais-tu revoir tes prétentions à la baisse et conceptualiser un simple jeu 2d plus simple, pas forcément un pong (qui même pour un novice n'est pas si simple que ça), mais quelque chose à près similaire niveau complexité.

          .
          Bon courage.

          1. Merci Jouat !!!

            Oui je vais très certainement redescendre un petit peu au niveau du jeu xd

            Merci beaucoup !!!

        2. Bonsoir, c'est encore moi (et la dernière fois je pense)

          J'ai un dernier problème : j'essaie désespérément de faire afficher le score en haut de l'écran  en fonction du temps, et y a un problème que je ne comprends pas ligne 139

          import pygame
          import random
          
          
          pygame.init()
          
          
          
          SCREEN_WIDTH = 800
          SCREEN_HEIGHT = 600
          SCREEN_COLOR = 120, 170, 255
          
          SOL_Y = 550
          
          PLAYER_COLOR = 255, 255, 255
          PLAYER_HEIGHT = 30
          PLAYER_WIDTH = 30
          EVENT_OBSTACLE_SPAWN = pygame.USEREVENT + 1
          
          
          FPS = 60
          
          
          
          class Sol(pygame.sprite.Sprite):
              def __init__(self, y):
                  super().__init__()
                  self.image = pygame.Surface((SCREEN_RECT.w, 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):
                  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.x = x
                  self.y = y
                  self.jumping = False
                  self.jump_count = 20
          
              def move(self, amount):
                  self.rect.x += amount
          
              def jump(self):
                  self.jumping = True
          
              def collision(self, sprites):
                  for sprite in sprites:
                      if isinstance(sprite, Cercle):
                          if self.rect.colliderect(sprite.rect):
                              if self.rect.bottom <= sprite.rect.centery:
          
                                  self.rect.bottom = sprite.rect.top
                              elif self.rect.top >= sprite.rect.centery:
                                  pygame.time.wait(2000)
                                  quit()
          
          
                              else:
          
                                  if self.rect.centerx < sprite.rect.centerx:
                                      self.rect.right = sprite.rect.left
          
                                  else:
                                      self.rect.left = sprite.rect.right
          
                      elif isinstance(sprite, Triangle):
                          if self.rect.colliderect(sprite.rect):
                              if self.rect.bottom <= sprite.rect.centery:
                                  self.rect.bottom = sprite.rect.top
          
                              elif self.rect.top >= sprite.rect.centery:
                                  pygame.time.wait(2000)
                                  quit()
          
                              else:
          
                                  if self.rect.centerx < sprite.rect.centerx:
                                      self.rect.right = sprite.rect.left
          
                                  else:
                                      self.rect.left = sprite.rect.right
          
                      elif isinstance(sprite, Rectangle):
                          if self.rect.colliderect(sprite.rect):
                              if self.rect.bottom <= sprite.rect.centery:
                                  self.rect.bottom = sprite.rect.top
          
                              elif self.rect.top >= sprite.rect.centery:
                                  pygame.time.wait(2000)
                                  quit()
          
                              else:
                                  if self.rect.centerx < sprite.rect.centerx:
                                      self.rect.right = sprite.rect.left
          
                                  else:
          
                                      self.rect.left = sprite.rect.right
          
                  # Retourne les sprites en collision avec le joueur
          
                  return tuple(s for s in sprites if s.rect.colliderect(self.rect))
          
          
              def update(self):
                  if self.jumping:
                      step = -(0.05 * self.jump_count ** 2) * (60 / FPS)
                      if self.jump_count > 0:
                          step *= -1
                      self.rect.y -= step
                      self.jump_count -= 1
                      if self.rect.bottom >= self.y:
                          self.rect.bottom = self.y
                          self.jumping = False
                          self.jump_count = 20
          
          
          class ScoreBoard:
              def __init__(self, screen_rect):
                  self.screen_rect = screen_rect
                  self.score = 0
                  self.font = pygame.font.SysFont('Arial', 30)
                  self.color = (255, 255, 255)
          
              def update1(self, time):
                  self.score = int(time / 1000)
          
              def draw(self, screen):
                  text_surface = self.font.render(f"Score: {self.score}", True, self.color)
                  text_rect = text_surface.get_rect(topleft=(0, 0))
                  screen.blit(text_surface, text_rect)
          
          score_board = ScoreBoard(SCREEN_WIDTH, SCREEN_HEIGHT)
          SCREEN_HEIGHT = 600
          
          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
          
              def update(self):
                  self.rect.x -= 6
          
          
          class Cercle(Obstacle):
              points = 10
          
              def __init__(self, x, y):
                  super().__init__(x, y, 60, 60)
                  pygame.draw.circle(self.image, (0, 155, 155), (30, 30), 30)
          
          
          class Triangle(Obstacle):
              points = -15
          
              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)))  # formation du triangle
          
          
          class Rectangle(Obstacle):
              points = 10
          
              def __init__(self, x, y):
                  super().__init__(x, y, 70, 135)
                  pygame.draw.rect(self.image, (12, 48, 255), (5, 5, 130, 130))
          
          
          class LongRectangle(Obstacle):
              points = 10
          
              def __init__(self, x, y):
                  super().__init__(x, y, 620, 65)
                  pygame.draw.rect(self.image, (255, 255, 0), (15, 15, 230, 230))
          
          
          # le spawn ; c'est une sorte de création en direct d'objets ou de personnages (ici ça va être les obstacles)
          def obstacle_spawn():
              # Choix de l'obstacle (en mode tu choisis si tu veux plus de triangle ou de cercles
              obstacle_class = random.choices((Cercle, Triangle, Rectangle, LongRectangle), (10, 14, 6, 3))[0]
              obstacle = obstacle_class(SCREEN_RECT.w, SOL_Y)
              draw_group.add(obstacle)
              obstacles_group.add(obstacle)
          
          
          def spawn_timer():
              ms = random.randrange(500, 1100)
              pygame.time.set_timer(EVENT_OBSTACLE_SPAWN, ms)
          
          
          def sprites_clear(surface, rect):
              surface.fill(SCREEN_COLOR, rect)
          
          
          # applicatio,
          screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
          SCREEN_RECT = screen.get_rect()
          pygame.mouse.set_visible(False)
          pygame.display.set_caption("Geo update")
          
          draw_group = pygame.sprite.RenderUpdates()
          obstacles_group = pygame.sprite.Group()
          
          player = Player(100, SOL_Y)
          draw_group.add(player)
          
          sol = Sol(SOL_Y)
          draw_group.add(sol)
          
          screen.fill(SCREEN_COLOR)
          pygame.display.update()
          
          clock = pygame.time.Clock()
          running = True
          points = 0
          spawn_timer()
          
          # boucle principale
          running = True
          while running:
              clock.tick(FPS)
              score_board.update1(pygame.time.get_ticks())
          
              sprites_clear(screen, SCREEN_WIDTH, SCREEN_HEIGHT)
              score_board.draw(screen)
              draw_group.update()
              draw_group.draw(screen)
              obstacles_group.update()
              pygame.display.update(SCREEN_HEIGHT, SCREEN_WIDTH)
          
              for event in pygame.event.get():
                  if event.type == pygame.QUIT:
                      running = False
                  elif event.type == EVENT_OBSTACLE_SPAWN:
                      obstacle_spawn()
                      spawn_timer()
          
          
                  if event.type == pygame.KEYDOWN:
                      if event.key == pygame.K_UP or pygame.K_SPACE and not player.jumping:
                          player.jump()
          
                  elif event.type == EVENT_OBSTACLE_SPAWN:
                      obstacle_spawn()
                      spawn_timer()
                      draw_group.remove(player)
                      draw_group.add(player)
          
              if pygame.sprite.spritecollide(player, obstacles_group, False):
                  running = False
          
              # Collision obstacles avec le joueur
              for sprite in player.collision(obstacles_group.sprites()):
                  # systeme de points à changer
                  points += sprite.points
                  obstacles_group.remove(sprite)
                  draw_group.remove(sprite)
                  print("points : ", points)
          
              # Suppression des sprites hors écran
              for sprite in obstacles_group.copy().sprites():
                  if sprite.rect.right < SCREEN_RECT.x:
                      obstacles_group.remove(sprite)
                      draw_group.remove(sprite)
          
              draw_group.clear(screen, sprites_clear)
              pygame.display.update()
          
          pygame.quit()
          

          donc j'ai créer une classe pour le score, et je l'ai aussi appelé dans la boucle principale mais je ne comprends pas le problème

          Pourquoi ??

          Merci :))

          1. Salut,

            Ta classe ScoreBoard prend un seul argument screen_rect (pourquoi ?), or tu lui passes 2 paramètres lorsque tu l'instancies, SCREEN_WIDTH et SCREEN_HEIGHT, donc forcément python bronche.

            Cette classe n'a nul besoin de cela, elle doit juste recevoir la position x, y où le score doit être affiché, rien de plus, et encore, on pourrait même s'en passer et gérer la position hors cette classe.

            .
            Qu'est censée faire ce que tu as fait dans la collision du joueur ? Je ne comprends pas pourquoi tu fais quitter le jeu, ni pourquoi tu veux gérer la collision différemment en fonction du type d'obstacle.

            .

            Pour n'importe quel type d'élément à afficher, utilise un sprite.

            .

            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

            .

            On l'ajoute ensuite, comme pour les autres objets, au groupe d'affichage.

            .

            # ....
            
            sol = Sol(SOL_Y)
            draw_group.add(sol)
            
            score = ScoreBoard(SCREEN_RECT.right - 10, 10)
            draw_group.add(score)
            
            # ...

            .
            Et update du score dans la boucle du jeu lorsque le joueur entre en collision avec les obstacles.

            .

            # Collision obstacles avec le joueur
            for sprite in player.collision(obstacles_group.sprites()):
                obstacles_group.remove(sprite)
                draw_group.remove(sprite)
                score.add(sprite.points)

            .
            Cela fonctionne en laissant le code de la méthode collision que je t'avais montré.

          2. @jouat

            Jvais essayer ça tout à l'heure. 

            Mon but avec les collisions :

            Pour les triangles et les cercles des qu'il le touche j'ai mis un temps d'arrêt pour faire afficher un message et j'enlèverai le quit plus tard lorsque j'aurais mis de presser une touche pour arriver à la page d'accueil ou pour recommencer le jeu 

            Et pour les rectangles en hauteur c'est pour qu'il puisse passer dessus (mais je vois que c'est pas le bon moyen :(

            Merci :)

          3. @Utilisateur anonyme

            Bonsoir.

            .

            Si tu souhaites que le joueur puisse se balader sur les obstacles, alors il va falloir revoir la façon dont le joueur saute, c-à-d gérer cela en 2 phases, une d'ascension, celle-là est simple, et donc une  phase de chute tant que le joueur n'a pas touché une surface sur laquelle il peut se mouvoir.

            .

            Il faudra donc détecter lorsque le joueur chute qu'il entre en collision avec un élément solide, les collisions est une des choses les plus difficiles à gérer dans un jeu, cela peut rapidement se complexifier et devenir un cauchemar ^^

            .

            Tu devrais réduire ton code au plus simple pour créer ton système de jump,  un sol, un simple bloc (voir plusieurs à différentes hauteurs), et évidemment le joueur. Il y a pas mal d'exemples sur internet concernant la mécanique des sauts.

          4. @jouat

            Oui je vais suivre ton conseil, ça paraît plus clair que ce que je commençais à faire. 

            Merci beaucoup !!