Partage
  • Partager sur Facebook
  • Partager sur Twitter

collision sur le coté d'un rect, pygame

14 septembre 2015 à 20:49:49

bonjour, je travaille sur un jeu de type portal mais en version 2d ( je sais que ca existe deja mais c'est pour m'entrainer ). mais j'ai un probleme lorsque je veux placer un portail ou plafond, au sol, au mur de droite ou de gauche. Le probleme est que je n'arrive pas a definir l'image a placer en fonction du coté touché par mon laser, quand j'essaie quelque chose, differentes conditions entre en contradiction et ce n'est pas le bon portail qui se place.

Aidez moi SVP

  • Partager sur Facebook
  • Partager sur Twitter
14 septembre 2015 à 21:01:21

Quelle précision exceptionnelle dans la description du problème, on est censés t'aider comment? ;)
  • Partager sur Facebook
  • Partager sur Twitter
14 septembre 2015 à 21:51:39

for wall in walls: # Pour les murs dans les niveaux
                    if self.rect1_droite.colliderect(wall.rect): # Si le rectangle du laser entre ne collision avec un rectangle de mur
                        if self.rect1_droite.top<=wall.rect.bottom and self.rect1_droite.top>=wall.rect.bottom-20:
                            self.portail1_gauche_rect.x=-1000#on retire tous les éventuels anciens portails et les placant hors de l'ecran
                            self.portail1_gauche_rect.y=-1000
                            self.portail1_bas_rect.x=-1000
                            self.portail1_bas_rect.y=-1000
                            self.portail1_droite_rect.x=-1000
                            self.portail1_droite_rect.y=-1000
                            self.rect1_droite.top = wall.rect.bottom
                            self.rect1_droite.right = wall.rect.right # Les coordonnées du côté droit du rectangle du missile = côté gauche du rectangle du mur
                            self.portail1_haut_rect.bottom=wall.rect.bottom#placement du portail sur le mur
                            self.portail1_haut_rect.right=wall.rect.right
                            self.arret=1#arrete=1 pour retirer les laser
                            self.position_portail1="haut"
                        elif self.rect1_droite.bottom>=wall.rect.top:
                            self.portail1_gauche_rect.x=-1000#on retire tous les éventuels anciens portails et les placant hors de l'ecran
                            self.portail1_gauche_rect.y=-1000
                            self.portail1_droite_rect.x=-1000
                            self.portail1_droite_rect.y=-1000
                            self.portail1_haut_rect.x=-1000
                            self.portail1_haut_rect.y=-1000
                            self.rect1_droite.bottom = wall.rect.top
                            self.rect1_droite.right = wall.rect.right # Les coordonnées du côté droit du rectangle du missile = côté gauche du rectangle du mur
                            self.portail1_bas_rect.bottom=wall.rect.bottom#placement du portail sur le mur
                            self.portail1_bas_rect.right=wall.rect.right
                            self.arret=1#arrete=1 pour retirer les laser
                            self.position_portail1="bas"
                        elif self.rect1_droite.right>=wall.rect.left:
                            self.portail1_gauche_rect.x=-1000#on retire tous les éventuels anciens portails et les placant hors de l'ecran
                            self.portail1_gauche_rect.y=-1000
                            self.portail1_bas_rect.x=-1000
                            self.portail1_bas_rect.y=-1000
                            self.portail1_haut_rect.x=-1000
                            self.portail1_haut_rect.y=-1000
                            self.rect1_droite.right = wall.rect.left # Les coordonnées du côté droit du rectangle du missile = côté gauche du rectangle du mur
                            self.portail1_droite_rect.left=wall.rect.left#placement du portail sur le mur
                            self.portail1_droite_rect.top=wall.rect.top
                            self.arret=1#arrete=1 pour retirer les laser
                            self.position_portail1="droite"
  • Partager sur Facebook
  • Partager sur Twitter
14 septembre 2015 à 21:54:33

mince, mon texte n'a pas été envoyé :/ donc je disais que je voulais faire apparaitre une image sur le mur quand mon laser entre en collision avec ce dernier, cependant, l'image doit etre differente en fonction de si le laser touche le mur par la droite ou par la gauche, par le haut ou par le bas.
  • Partager sur Facebook
  • Partager sur Twitter
15 septembre 2015 à 13:25:45

Salut,

Je n'ai pas encore fait de jeu, donc ma réponse sera théorique. Pour combiner la bonne image au mur touché, je ferai en sorte de récupérer la direction du personnage au moment du tir. Ainsi si le personnage "regarde" vers le bas lorsqu'on appuie sur la touche action, alors on mettra l'image correspondant au sol. Idem si lorsqu'on appuie sur la touche action le personnage est dirigé vers la droite alors on mettra l'image pour le mur de droite.

  • Partager sur Facebook
  • Partager sur Twitter
Précepte: Le mieux est l'ennemi du bien
15 septembre 2015 à 14:12:08

j'aurais pu mais j'utilise un viseur pour tirer les portails, ils peuvent donc etre tirés en diagonales et en fonction de la distance du mur, je ne peux pas savoir au prealable s'il va toucher sur le coté ou sur le bas d'un mur ( dans le cas ou je ire en diagonale droite)
  • Partager sur Facebook
  • Partager sur Twitter
15 septembre 2015 à 15:31:53

Ok. Dans ce cas, je regarderai avec quel mur le tir entre en collision (une réorganisation peut s'imposer) pour mettre la bonne image.
  • Partager sur Facebook
  • Partager sur Twitter
Précepte: Le mieux est l'ennemi du bien
15 septembre 2015 à 20:33:31

ethir60 a écrit:

j'aurais pu mais j'utilise un viseur pour tirer les portails, ils peuvent donc etre tirés en diagonales et en fonction de la distance du mur, je ne peux pas savoir au prealable s'il va toucher sur le coté ou sur le bas d'un mur ( dans le cas ou je ire en diagonale droite)


Mmh en première idée qui me vient à l'esprit tu fais des équations de droite et tu les résous non? 

  • Partager sur Facebook
  • Partager sur Twitter
16 septembre 2015 à 10:31:33

En gros tu essaies de faire du Raycasting en 2D. Une méthode est l'algorithme de Liang–Barsky. py-sdl2 en a une implémentation en python.

  • Partager sur Facebook
  • Partager sur Twitter
16 septembre 2015 à 13:40:03

je ne connais absolument pas le raycasting, peux tu m'expliquer rapidement comment cela fonctionne s'il te plait?
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
16 septembre 2015 à 14:35:53

peut tu faire une recherche sur google ?
  • Partager sur Facebook
  • Partager sur Twitter
16 septembre 2015 à 20:25:37

si je demande c'est parceque meme avec ces recherches, je ne comprends pas '--
  • Partager sur Facebook
  • Partager sur Twitter
19 septembre 2015 à 16:37:45

Voici quelques infos complémentaires et une fonction qui te permettra d'accomplir ce que tu recherches.

Alors, pour ce qui est du raycasting, ce n'est ni plus ni moins le fait de lancer un rayon dans un ensemble de formes géométriques et de retourner le point d'intersection le plus proche. La vraie question est: mais comment on fait ça ?

Alors je ne sais pas quel âge tu as, ni quel est ton niveau mathématique. Mais comme l'a dit Derzal, il faut résoudre des équations de droite. Sous forme paramétrique, tu peux écrire l'équation de ton rayon sous la forme \(rayon = A + t\cdot\vec{a}\) où \(A\) est le point d'origine de ton rayon, et \(\vec{a}\) est la direction du rayon. Pour une autre droite avec pour formule \(B + u\cdot\vec{b}\) l'intersection se trouve là où \(A + t\cdot\vec{a} = B + u\cdot\vec{b}\) Si on nomme \(\vec{c} = B - A\) alors on a \(t\cdot\vec{a} = \vec{c} + u\cdot\vec{b}\) L'astuce maintenant est de multiplier les deux termes de cette équation par la perpendiculaire à \(\vec{b}\) que l'on note \(\vec{b}_{\bot}\) (je n'arrive pas à mettre le symbole en haut, alors on s'en contentera en bas... :) Pour rappel le produit scalaire de deux vecteurs perpendiculaires est égale à 0. Donc l'équation donne: \(t\cdot\vec{b}{\bot}\cdot\vec{a} = \vec{b}{\bot}\cdot\vec{c} + 0\) Et donc dans le cas où \(\vec{b}{\bot}\cdot\vec{a}\) n'est pas nul, on trouve pour t: \[t = \frac{ \vec{b}{\bot}\cdot\vec{c}}{\vec{b}_{\bot}\cdot\vec{a}}\]

Dans le cas où on veut trouver l'intersection entre notre rayon et le côtés d'un rectangle, le vecteur \(\vec{b}_{\bot}\) est en fait la normale à la face du rectangle. Donc avec le vecteur représentant notre rayon \(\vec{a}\), le vecteur allant de l'origine du rayon au sommet du rectangle que l'on considère \(\vec{c}\) et la normale à cette face, on peut trouver \(t\).

L'algorithme que j'utilise est pris du code de Box2D mais reconverti en python. Aussi il ne fait que donner le résultat de \(t\) et non la normale comme dans Box2D. Cette implémentation se trouve dans la fonction raycast_rect. J'ai ensuite une fonction raycast qui prend en argument une liste de rectangles et retourne le plus petit \(t\) trouvé, s'il y en a un.

Il y a d'autres choses à dire sur cet algorithme, mais je ne sais pas si c'est vraiment ce que tu veux. Sinon il suffit de le placer dans ton code.

!/usr/bin/env python3

-- coding: utf-8 --

import pygame

def dot(v1, v2):

x1, y1 = v1
x2, y2 = v2
return x1*x2 + y1*y2

def raycast_rect(rect, orig, dest):

vertices, normals = ((rect.bottomleft, rect.bottomright, rect.topright, rect.topleft),
                    ((0, 1), (1, 0), (0, -1), (-1, 0)))
lower, upper = 0.0, 1.0
x0, y0 = orig
x1, y1 = dest
ray = (x1-x0, y1-y0)
intersection = False

for vertice, normal in zip(vertices, normals):
    orig_to_vertice = (vertice[0]-x0, vertice[1]-y0)
    n = normal

    numerator = dot(n, orig_to_vertice)
    denominator = dot(n, ray)
    if denominator == 0.0:
        if numerator < 0.:
            return None
    else:
        if denominator < 0. and numerator < lower * denominator:
            intersection = True
            lower = numerator / denominator
        elif denominator > 0. and numerator < upper * denominator:
            upper = numerator / denominator

    if upper < lower:
        return None

if intersection:
    return lower
return None

def raycast(rects, orig, dest):

fractions = (raycast_rect(rect, orig, dest) for rect in rects)
fraction = min((fraction for fraction in fractions if fraction is not None), default=None) # python 3.4
# Use the following if python version < 3.4
# try:
#     fraction = min(fraction for fraction in fractions if fraction is not None)
# except ValueError: # min on empty list
#     return None

return fraction

if name == 'main':

from pygame.locals import *
import math
import random

pygame.init()
taille_fenetre = (800, 600)
fenetre_rect = pygame.Rect((0, 0), taille_fenetre)
screen_surface = pygame.display.set_mode(taille_fenetre)
timer = pygame.time.Clock()

rects = [pygame.Rect(i*200, j*200, 100, 100) for i in range(4) for j in range(3) if not (i == 2 and j == 1)]

angle = 0
orig = (400, 300)
dest = (400 + 200 * math.cos(angle), 300 + 200 * math.sin(angle))

continuer = True
while continuer:
    for event in pygame.event.get():
        if event.type == QUIT:
            continuer = False

    timer.tick(30)
    angle += 0.01
    angle = angle % (2 * math.pi)
    dest = (400 + 400 * math.cos(angle), 300 + 400 * math.sin(angle))

    fraction = raycast(rects, orig, dest)

    screen_surface.fill(0)
    for rect in rects:
        pygame.draw.rect(screen_surface, (255, 0, 0), rect, 1)
    pygame.draw.line(screen_surface, (255, 0, 0), orig, dest)
    
    if fraction:
        xo, yo = orig
        xd, yd = dest
        intersection = (int(xo + fraction * (xd-xo)), int(yo + fraction * (yd-yo)))
        pygame.draw.circle(screen_surface, (0, 0, 255), intersection, 3)

    pygame.display.flip()
pygame.quit()
</pre>

-
Edité par Dan737 19 septembre 2015 à 17:19:43

  • Partager sur Facebook
  • Partager sur Twitter
20 septembre 2015 à 21:04:11

merci beaucoup de ton aide, je vais essayer de comprendre ce code et l'ajouter a mon jeu si je m'en sort ^^.
  • Partager sur Facebook
  • Partager sur Twitter
24 septembre 2021 à 18:15:43

Je pensais peut-être aussi utiliser la méthode colliderect()...
  • Partager sur Facebook
  • Partager sur Twitter
24 septembre 2021 à 18:20:34

ça remonte à 6 ans quand même ^^
  • Partager sur Facebook
  • Partager sur Twitter

Python c'est bon, mangez-en. 

24 septembre 2021 à 18:25:06

@Younès Bendimerad Bonjour, merci de ne pas déterrer d'ancien sujet.

Déterrage

Citation des règles générales du forum :

Avant de poster un message, vérifiez la date du sujet dans lequel vous comptiez intervenir.

Si le dernier message sur le sujet date de plus de deux mois, mieux vaut ne pas répondre.
En effet, le déterrage d'un sujet nuit au bon fonctionnement du forum, et l'informatique pouvant grandement changer en quelques mois il n'est donc que rarement pertinent de déterrer un vieux sujet.

Au lieu de déterrer un sujet il est préférable :

  • soit de contacter directement le membre voulu par messagerie privée en cliquant sur son pseudonyme pour accéder à sa page profil, puis sur le lien "Ecrire un message"
  • soit de créer un nouveau sujet décrivant votre propre contexte
  • ne pas répondre à un déterrage et le signaler à la modération

Je ferme ce sujet. En cas de désaccord, me contacter par MP.

  • Partager sur Facebook
  • Partager sur Twitter