Partage
  • Partager sur Facebook
  • Partager sur Twitter

Créer un obstacle avec Pygame

Comment empêcher un vaisseau spatial de s'écraser sur une météorite ?

17 février 2016 à 22:44:33

Bonsoir à tous !

Je suis en pleine séance de codage d'un jeu que j'ai à développer pour mon projet ISN, en classe de Terminale, et j'ai un léger problème à un certain stade de mon développement. Pour information, mon jeu est un space shooter 1v1 contre un autre joueur en local pour l'instant.

J'ai une fonction, nommée Espace, et qui gère l'intégralité de ce qui se passe lorsque le jeu lui même est lancé. Une série de fonction préliminaires permettent de déterminer le fond du niveau, le vaisseau avec lequel on va jouer, etc...

Voici la fonction en question :

def Espace (fenetre, fond_image, v_left, v_right, v_up, v_down) : #Fonction des niveaux, avec fenetre en variable, et les sprites gauche, droite, haut et bas dans cet ordre.
    fenetre = pygame.display.set_mode((1280,720),) # Définition de la fenetre
    #Chargement et collage du fond
    fond = fond_image
    fenetre.blit(fond, (0,0))

    # Actualisation de l'affichage
    pygame.display.flip()
    # 10 fps
    clock.tick(10)

    #Chargement et collage du personnage
    perso = pygame.image.load("Images/vaisseau1_right.png").convert_alpha()
    position_perso = pygame.Rect((10,360), (52,52))
    fenetre.blit(perso, position_perso)

    #musique_bataille = pygame.mixer.music.load("musiques/bataille.mp3")
    #pygame.mixer.music.play()

    asteroide1 = pygame.image.load("Images/asteroide1.png").convert_alpha()
    position_asteroide1 = pygame.Rect(850, 460, 150, 150)
    asteroide2 = pygame.image.load("Images/asteroide1.png").convert_alpha()
    position_asteroide2 = pygame.Rect(526, 335, 150, 150)
    asteroide3 = pygame.image.load("Images/asteroide1.png").convert_alpha()
    position_asteroide3 = pygame.Rect(260, 160, 150, 150)
    asteroide4 = pygame.image.load("Images/asteroide1.png").convert_alpha()
    position_asteroide4 = pygame.Rect(240, 420, 150, 150)
    asteroide5 = pygame.image.load("Images/asteroide1.png").convert_alpha()
    position_asteroide5 = pygame.Rect(758, 205, 150, 150)

    #Liste des objets colliders
    mescollisions = position_asteroide1, position_asteroide2, position_asteroide3, position_asteroide4, position_asteroide5

    #Rafraîchissement de l'écran
    pygame.key.set_repeat(10,10)
    pygame.display.flip()

    #BOUCLE INFINIE
    continuer = 1
    while continuer:
        for event in pygame.event.get():	#Attente des événements
            if event.type == QUIT:
                continuer = 0
            if event.type == KEYDOWN :

                if event.key == K_DOWN and position_perso.y<=654:
                    if position_perso.colliderect(position_asteroide1) :
                        position_perso = position_perso.move(0,-2)
                    else :
                        position_perso = position_perso.move(0,2)
                        perso = v_down

                if event.key == K_UP and position_perso.y>3:
                    if position_perso.colliderect(position_asteroide1) :
                        position_perso = position_perso.move(0,2)
                    else :
                        position_perso = position_perso.move(0,-2)
                        perso = v_up

                if event.key == K_LEFT and position_perso.x>3:
                    if position_perso.colliderect(position_asteroide1) :
                        position_perso = position_perso.move(2,0)
                    else :
                        position_perso = position_perso.move(-2,0)
                        perso = v_left


                if event.key == K_RIGHT and position_perso.x<=1216:
                    if position_perso.colliderect(position_asteroide1) :
                        position_perso = position_perso.move(-2,0)
                    else :
                        position_perso = position_perso.move(2,0)
                        perso = v_right


    	#Re-collage
        fenetre.blit(fond, (0,0))
        fenetre.blit(perso, position_perso)
        fenetre.blit(asteroide1, (850,460))
        fenetre.blit(asteroide2, (526,335))
        fenetre.blit(asteroide3, (260,160))
        fenetre.blit(asteroide4, (240,420))
        fenetre.blit(asteroide5, (758,205))

    	#Rafraichissement
        pygame.display.flip()
    pygame.quit()

Voilà pour le gros de ma fonction. Un passage particulièrement me pose problème : celui où je gère les collisions avec l'astéroïde 1, qui me sert actuellement de test.

J'avais rencontré un gros problème déjà, car j'avais utilisé la fonction rect de pygame, et j'avais écrit cette ligne :

if position_perso.colliderect(position_asteroide1) :
    position_perso = position_perso.move(0,0)

Le problème de ce code était que si le vaisseau était en collision avec l'astéroïde, il devenait immobile. Or, s'il est immobile, il ne peux plus se déplacer, donc il reste en collision, et donc cercle vicieux...

Pour palier à cela, j'ai pensé à faire quelque chose de pas très beau, mais qui aurait pu avoir eu le mérite de fonctionner :

if position_perso.colliderect(position_asteroide1) :
    position_perso = position_perso.move(0,-2)

Ici, lorsqu'il y a collision, le vaisseau est automatiquement reculé de deux pixels, ce qui devrait faire qu'il n'est pas en collision et qu'il peut donc bouger. Problème ? Et bien gros problème : Le vaisseau glitch très facilement cette limite. En effet, quand le sprite est en collision, tous ses mouvements sont inversés, et donc s'il recule, petit problème : le vaisseau traverse l'astéroïde !

Alors j'en appelle à l'aide, car c'est un gros problème pour moi en ce moment, et malgré énormément de recherches, je n'ai pas trouvé de solutions simples à ce problème...

Merci à tous, cordialement,

Lyro



  • Partager sur Facebook
  • Partager sur Twitter

🚀 Software Engineer | https://lyro.fr

Anonyme
18 février 2016 à 0:01:31

Salut,

Je n'ai pas regardé ton code en détail, mais dans le principe pour faire de la collision, tu vas plutôt d'abord vérifier si ton vaisseau ne va pas rentrer dans un astéroïde, et si c'est le cas le déplacer. Car là tu découvres que ton vaisseau est dans un astéroïde, et tu ne sais plus comment l'en sortir. Ou alors, tu le déplaces mais tu vérifies directement la collision et si elle a lieu, tu le refait le mouvement en sens inverse.

Deuxième solution plus simple, tu fais exploser le vaisseau quand il entre en collision :-°

Après, si ton astéroïde bouge aussi, c'est un autre problème. Imagine que ton vaisseau ne bouge pas et que l'astéroïde vient sur toi, ce que je t'ai dit ne fonctionne plus, alors tu devras trouver autre chose, par exemple bouger le vaisseau d'autant qu'à bougé l'astéroïde s'il les deux entrent en collision.

Si tu tiens à rester sur ta méthode, tu pourrais garder ta vitesse et ton accélération dans des variables et si tu entres en collision, tu fais bouger le vaisseau suivant le vecteur opposé à celui de sa vitesse, petit à petit jusqu'à ce qu'il n'y a pas collision.

Bonne chance pour l'ISN ^^

  • Partager sur Facebook
  • Partager sur Twitter
18 février 2016 à 10:31:17

Merci pour ta réponse :)

Le problème c'est que j'utilise mes obstacles comme des sortes de boucliers pour protéger mon vaisseau. Donc je ne veux pas que mon vaisseau explose dessus, je veux juste qu'il ne puisse pas le traverser...

Mais je vais chercher, merci encore !

  • Partager sur Facebook
  • Partager sur Twitter

🚀 Software Engineer | https://lyro.fr

18 février 2016 à 10:48:03

Et ce que je t'ai dit qur ton post précedent ne fonctionne pas?

En code, ça donnerait un truc du genre:

deplacements = {"g": (-1,0), "d":(1,0), "h":(0,-1), "b":(0,1)}

if collision_a_gauche:
    del deplacements["g"]#on supprime la possibilité de se déplacer à gauche

if collision_a_droite:
    del deplacements["d"]

#etc... Et à la fin des tests de collision, on remet les 4 déplacements:
deplacements = {"g": (-1,0), "d":(1,0), "h":(0,-1), "b":(0,1)}

Bien sûr il faudrait faire une classe vaisseau, mais ça reste facilement adaptable. Et tu n'es pas obligé d'utiliser un dictionnaire (peut-être que tu ne connais pas), c'est juste plus simple/clair que d'utiliser une liste

  • Partager sur Facebook
  • Partager sur Twitter
18 février 2016 à 11:00:42

T'as regardé mes vidéos sur pygame ? La dernière parle de collisions et de comment ne pas rentrer dans l'obstacle.

  • Partager sur Facebook
  • Partager sur Twitter
18 février 2016 à 11:19:13

@Lyro: Je pense que tu es passé à côté de la technique de Yksuh (qui correspond à celle que je t'avais proposé dans un autre post), à savoir de SIMULER le déplacement de ton vaisseau (ce que j'appelais étape intermédiaire). Cette simulation te permet de voir si ton vaisseau entre en contact avec l'astéroïde. Si c'est le cas tu ne le déplaces pas, sinon tu lui attribues la nouvelle position.

Il suffit juste de créer un nouveau Rect qui sera aux nouvelles coordonnées de ton vaisseau. Si ce Rect n'entre pas en collision avec ton astéroïde, tu peux déplacer ton vaisseau (en lui assignant la valeur de Rect). Au contraire, s'il rentre en collison tu supprimes Rect.

Bref, ça donnerai un truc du style (à toi de l'adapter car je ne connais pratiquement pas pygame):

rect_intermediaire = position_perso.move(dx, dy)
if not rect_intermediaire.colliderect(position_asteroide1):
    position_perso = rect_intermediaire
else:
    del rect_intermediaire


PS: Si tu cherches 2 sources en français, il y a Dan qui a fait des vidéos sur Youtube et il y a Folaelfolc qui est en train de faire un tuto sur ZdS. Je ne sais pas s'ils traitent de la gestion des collisions mais ce qui est sûr c'est qu'ils ont de meilleures connaissances que moi sur le sujet ^^.

Edit: Grilled :p

-
Edité par Olygrim 18 février 2016 à 11:19:43

  • Partager sur Facebook
  • Partager sur Twitter
Précepte: Le mieux est l'ennemi du bien
Anonyme
18 février 2016 à 11:33:37

Yop.

Perso, je ferais la simulation, comme Olygrim. J'utilise ça dans tous mes projets, même si c'est un peu chiant à mettre en place parfois x)

Pour bien faire, je ferais comme ceci :

new_mvt = perso.move(dx, dy)
can_move = True
for asteroide_rect in asteroides:
    if new_mvt.colliderect(asteroide_rect):
        can_move = False
        break
if can_move:
    perso = new_mvt
else:
    """
    todo:
    -ajouter une détection de collision par côté
    -si on collide, on se rapproche de l'asteroide pour le coller, dans la direction qui collide
    """

Je crois que c'est ce que Dan explique dans une de ses videos ^^

  • Partager sur Facebook
  • Partager sur Twitter
18 février 2016 à 12:42:56

Non moi je n'explique pas ça. Cette méthode a un désavantage. Si l'objet (le vaisseau) est à 3 pixels de l'obstacle et qu'il se déplace de 5 pixels, avec votre méthode, vous laissez le vaisseau à 3 pixels car vous ne le déplacez pas.

Moi je reconnais qu'il y a collision, je cherche de quel côté du rectangle on est entré en collision et je viens replacer le vaisseau juste à côté de l'obstacle. C'est donc différent. :)

  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
18 février 2016 à 13:20:20

Nope. Pas dans ma solution justement car tu vas faire une petite soustraction pour coller l'obstacle :) Donc c'est quasiment pareil en fait ;)
  • Partager sur Facebook
  • Partager sur Twitter