Coder le jeu Geometry dash en python

Résolu/Fermé
Utilisateur anonyme - Modifié le 3 mars 2023 à 17:19
 Utilisateur anonyme - 7 mars 2023 à 07:10

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

mamiemando Messages postés 33073 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 16 avril 2024 7 748
Modifié le 1 mars 2023 à 12:51

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
mamiemando Messages postés 33073 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 16 avril 2024 7 748
3 mars 2023 à 12:57

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
Utilisateur anonyme
4 mars 2023 à 10:33

Merci Jouat !!!

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

Merci beaucoup !!!

0
Utilisateur anonyme
Modifié le 5 mars 2023 à 20:12

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
Utilisateur anonyme > jouat
6 mars 2023 à 10:50

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 :)

1
jouat > Utilisateur anonyme
6 mars 2023 à 20:57

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.

2
Utilisateur anonyme > jouat
7 mars 2023 à 07:10

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

Merci beaucoup !!

1

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
Utilisateur anonyme
1 mars 2023 à 17:33

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()
0
Utilisateur anonyme
3 mars 2023 à 12:59

Je vais regarder, merci :))

0