Programar el juego Geometry Dash en Python
ResueltoUsuario anónimo -
¡Hola hola!
Estoy haciendo un geometry dash para la escuela y soy de los más novatos.
Por ahora ya tengo a mi personaje moviéndose y saltando. El siguiente paso es crear plataformas en el aire y obstáculos; así como hacer que se desplace el fondo. Empece por los obstáculos y las plataformas pero no logro conectarlos a mi código.
Mi pregunta es la siguiente: ¿alguien tendría un enlace a un tutorial de plataformas y obstáculos que pueda adaptar fácilmente y entender para mi código?
Gracias
6 respuestas
Hola,
Sin ver tu código, es difícil responder.
Pero haciendo una pequeña búsqueda en Google, encontré este video que me parece responde exactamente a tu pregunta, y la descripción proporciona el enlace al código.
El código se basa en ursina.
Buena suerte
Hola,
Si no quieres usar ursina y prefieres usar pygame, puedes mirar este repositorio.
Buena suerte
Hola.
La cosa es que para hacer incluso un juego simple, hay que familiarizarse mucho con la biblioteca que se quiere usar, así que haciendo montones de pruebas, y evidentemente eso lleva tiempo; hacer un juego con obstáculos y plataformas requiere mucho tiempo, ya sea en la mecánica del juego o en la reflexión, la estrategia del juego, el diseño de los mapas, etc. Sin contar que, obviamente, hay que dominar los conceptos primarios de Python, incluido el objeto; hacer un juego complejo sin un modelo orientado a objetos es para mí misión imposible, bueno, es factible pero a costa de la legibilidad y organización del código, sobre todo porque en Python todo es un objeto.
Hacer un juego con pygame es usar sprites; un sprite de pygame debe tener como atributos image y rect, por lo tanto, player, obstáculos, plataformas y todas las entidades deben ser sprites.
No sé a qué te refieres cuando dices “geometry dash”; hacer aparecer obstáculos que el jugador debe evitar o recoger, no es difícil (bueno, para alguien que conozca un poco pygame).
.
Haz a la ligera según tu código.
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): # Retorna los sprites en colisión con el jugador 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.
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 :))
Hola,
Tu clase ScoreBoard toma un único argumento screen_rect (¿por qué?), pero la pasas 2 parámetros cuando la instancias, SCREEN_WIDTH y SCREEN_HEIGHT, así que Python protesta.
Esta clase no necesita nada de eso, solo debe recibir la posición x, y donde debe mostrarse la puntuación, nada más, y aun así podríamos prescindir de ello y gestionar la posición fuera de esta clase.
.
¿Qué se supone que hace lo que hiciste en la colisión del jugador? No entiendo por qué haces salir del juego, ni por qué quieres gestionar la colisión de forma diferente según el tipo de obstáculo.
.
Para cualquier tipo de elemento a mostrar, usa 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 .
Se añade después, como para los otros objetos, al grupo de dibujo.
.
# .... sol = Sol(SOL_Y) draw_group.add(sol) score = ScoreBoard(SCREEN_RECT.right - 10, 10) draw_group.add(score) # ...
.
Y actualización de la puntuación en el bucle del juego cuando el jugador entra en colisión con los obstáculos.
.
# Colisión obstáculos con el jugador for sprite in player.collision(obstacles_group.sprites()): obstacles_group.remove(sprite) draw_group.remove(sprite) score.add(sprite.points)
.
Eso funciona dejando el código del método collision que te había mostrado.
Voy a probar eso más tarde.
Mi objetivo con las colisiones:
Para los triángulos y los círculos, en cuanto se toquen puse un tiempo de pausa para mostrar un mensaje y quitaré el salir/quité más tarde cuando haya implementado una tecla para llegar a la página de inicio o para volver a empezar el juego.
Y para los rectángulos en altura es para que pueda pasar por encima (pero veo que no es la mejor forma :(
Gracias :)
Hola, gracias por responderme :)
Sí, vi ese video
Mi código se parece a esto; pero no sé, no puedo :(
import pygame #tamaño de la pantalla e inicialización LARGEUR_ECRAN = 1600 HAUTEUR_ECRAN = 850 pygame.display.set_caption("test juego") ecran = pygame.display.set_mode([LARGEUR_ECRAN, HAUTEUR_ECRAN]) pygame.init() #salto x = 800 #posición del cubo al inicio y = 600 #posición del cubo al inicio width = 80 height = 80 vel = 15 isJump = False jumpCount = 10 #altura del salto """se pueden poner clases entre aquí y el bucle continua""" #bucle 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() #direcciones if keys[pygame.K_LEFT] and x > vel: x -= vel if keys[pygame.K_RIGHT] and x < 1600 - vel - width: x += vel #salto 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 #llamar a los elementos 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()