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.
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"
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.
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.
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)
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?
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()
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.
Python c'est bon, mangez-en.