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...
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.
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...
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
@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 .
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
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.
🚀 Software Engineer | https://lyro.fr
🚀 Software Engineer | https://lyro.fr