Big Tuto SFML 2 : Rabidja v. 3.0

Annexe 6 : Collisions lumineuses : la pratique !

Tutoriel présenté par : Skrool
Relecture et corrections : Jérémie F. Bellanger (Jay81)

Date d'écriture : 24 avril 2015
Date de révision : 20 mars 2016

      Introduction

  Voilà, maintenant que nous avons vu la théorie dans le chapitre précédent, place au code !angel

   Et on est reparti ! wink

   Commençons tout d'abord par créer notre struct Wall : 

Fichier : lightEntity.h, rajouter dans la classe lightEntity :

class lightEntity // La lumière en elle-même
{
public:
 
struct Wall // Struct pour gérer les murs. Un mur = 2 points, rien de plus simple
{
sf::Vector2f Pt1, Pt2;
};
 
// Constructeur et destructeur
lightEntity();
~lightEntity();
 
   Complexité 0. cheeky On crée une struct avec deux points.
   Ensuite, créons notre fonction AddShape() :
 

Fichier : lightEntity.h, rajouter :

//Fonction pour ajouter un triangle (sera plus tard couplée avec une autre fonction)
void AddTriangle(sf::Vector2f pt1, sf::Vector2f pt2, Map &map);
 
//Rajouter :
//Fonction pour ajouter un sous triangle
void AddShape(sf::Vector2f pt1, sf::Vector2f pt2, int minimumWall, std::vector <Wall> &mur);
 
 
//Code coupé...
 
//Tile source, pour replacer la tile de lumière à sa place quand elle sortira de l'écran pour économiser du CPU
int m_sourceX, m_sourceY, m_sourceID, m_couche;
 
//Constante
const float M_PI = 3.14;
 
//Rajouter :
 
/** VALEURS DES TILES **/
//On ne peut pas utiliser const à cause du tableau dynamique
 
int TILE_SIZE = 32;
int BLANK_TILE = 99;
int TILE_TRAVERSABLE = 80;
 
   On en profite pour mettre nos valeurs de tiles. On ne peut pas mettre de const à cause du tableau dynamique, mais un simple int suffit. wink
   On notera aussi les deux paramètres minimumWall et Mur, utiles lors des collisions. Pas besoin de mettre map en paramètre. cheeky
   Passons maintenant au fichier lightEntity.cpp, et modifions notre fonction AddTriangle() puis ajoutons la fonction nouvelle AddShape() : 
 

Fichier : lightEntity.cpp : Modifier / Rajouter :

// Ajout d'un triangle
void lightEntity::AddTriangle(sf::Vector2f pt1, sf::Vector2f pt2, Map &map)
{
// Création de notre tableau de murs
std::vector <Wall> mur;
 
// On calcule notre triangle
AddShape(pt1, pt2, 0, mur);
}
 
 
 
//Ajout d'un sous triangle
void lightEntity::AddShape(sf::Vector2f pt1, sf::Vector2f pt2, int minimumWall, std::vector <Wall> &mur)
{
// Variable qui contiendra l'intensité calculée, pour le dégradé
float intensity;
 
// On ajoute un shape
m_triangle.push_back(sf::VertexArray());
m_triangle.back().setPrimitiveType(sf::Triangles);
 
 
// On lui donne comme point de départ (0,0), le centre de la lumière, avec la couleur et intensité maximal
m_triangle.back().append(sf::Vertex(m_position,
sf::Color((int)(m_intensity*m_color.r / 255),
(int)(m_intensity*m_color.g / 255),
(int)(m_intensity*m_color.b / 255))));
 
// On calcul ou l'on se trouve par rapport au centre, pour savoir à quel intensité on est
intensity = m_intensity - sqrt(pt1.x*pt1.x + pt1.y*pt1.y)*m_intensity / m_radius;
// Et on ajoute un point au shape
m_triangle.back().append(sf::Vertex(m_position + pt1,
sf::Color((int)(intensity*m_color.r / 255),
(int)(intensity*m_color.g / 255),
(int)(intensity*m_color.b / 255))));
 
// Idem
intensity = m_intensity - sqrt(pt2.x*pt2.x + pt2.y*pt2.y)*m_intensity / m_radius;
m_triangle.back().append(sf::Vertex(m_position + pt2,
sf::Color((int)(intensity*m_color.r / 255),
(int)(intensity*m_color.g / 255),
(int)(intensity*m_color.b / 255))));
}
 
   Comme promis, nous avons déplacé notre code se trouvant dans AddTriangle(), dans la fonction AddShape(), toute nouvelle et nous appelons une fois AddShape() à la fin de AddTriangle().
   Maintenant, partitionnons notre espace, en modifiant à nouveau la fonction AddTriangle()
 

Fichier : lightEntity.cpp : Modifier :

// Ajout d'un triangle
void lightEntity::AddTriangle(sf::Vector2f pt1, sf::Vector2f pt2, Map &map)
{
// Création de notre tableau de murs
std::vector <Wall> mur;
 
// Création d'un vertex array à partir duquel nous allons créer notre boîte englobante
sf::VertexArray triangle(sf::Triangles, 3);
triangle[0].position = m_position;
triangle[1].position = m_position + pt1;
triangle[2].position = m_position + pt2;
 
// Création de notre boîte englobante
sf::FloatRect boundingBox = triangle.getBounds();
 
// Création d'un tableau de 4 points qui seront égaux aux quatre points de chaque tile
sf::Vector2f l[4];
 
// Nos variables compteurs pour nos boucles for
int x, y;
 
// On crée notre boucle for sur y puis sur x de manière à tester toutes les tiles de la boîte englobante
 
for (y = (int)(boundingBox.top / TILE_SIZE) - 2 + (map.getStartY() / TILE_SIZE);
y < ((int)(boundingBox.top + boundingBox.height) / TILE_SIZE) + 2 + (map.getStartY() / TILE_SIZE); y++)
{
for (x = (int)(boundingBox.left / TILE_SIZE) - 2 + (map.getStartX() / TILE_SIZE);
x < ((int)(boundingBox.left + boundingBox.width) / TILE_SIZE) + 2 + (map.getStartX() / TILE_SIZE); x++)
{
// Ici nous testons nos tiles
 
// Si ce sont des tiles solides
if (map.getTile(y, x) > BLANK_TILE)
{
//l0, l1, l2, l3 sont les coordonnée de chaque point du bloc relative au centre de la lumière
l[0] = sf::Vector2f(x*TILE_SIZE - map.getStartX(),
y*TILE_SIZE - map.getStartY());
 
l[1] = sf::Vector2f((x + 1)*TILE_SIZE - map.getStartX(),
y*TILE_SIZE - map.getStartY());
 
l[2] = sf::Vector2f((x + 1)*TILE_SIZE - map.getStartX(),
(y + 1)*TILE_SIZE - map.getStartY());
 
l[3] = sf::Vector2f(x*TILE_SIZE - map.getStartX(),
(y + 1)*TILE_SIZE - map.getStartY());
 
l[0] -= m_position;
l[1] -= m_position;
l[2] -= m_position;
l[3] -= m_position;
 
// Création de murs pour gérer les collisions à la place des tiles,
// ça permet de les fusionner pour moins subdiviser les triangles.
mur.push_back(Wall());
mur.back().Pt1 = l[0];
mur.back().Pt2 = l[1];
 
mur.push_back(Wall());
mur.back().Pt1 = l[1];
mur.back().Pt2 = l[2];
 
mur.push_back(Wall());
mur.back().Pt1 = l[2];
mur.back().Pt2 = l[3];
 
mur.push_back(Wall());
mur.back().Pt1 = l[3];
mur.back().Pt2 = l[0];
 
}
}
}
 
 
// On calcule notre triangle
AddShape(pt1, pt2, 0, mur);
}
 
   Mais il nous manque encore notre fonction pour fusionner deux segments ! surprise
   Remédions tout de suite à cela wink.
   Nous allons créer deux fonctions pour tester respectivement si deux points sont communs, et si deux segment sont alignés : 
 

Fichier : lightEntity.cpp : Rajouter à la fin du fichier :

/******************************************************************************************/
/******************************* Collisions de la lumière ********************************/
/******************************************************************************************/
 
bool lightEntity::pointCommun(sf::Vector2f pt1, sf::Vector2f pt2)
{
//Cette fonction permet de déterminer si deux points sont considérés comme confondus malgré un tout petit décalage
 
return (pt1.x >= pt2.x - 2 && pt1.x <= pt2.x + 2 &&
pt1.y >= pt2.y - 2 && pt1.y <= pt2.y + 2);
 
}
 
 
 
bool lightEntity::coteAcote(Wall &mur1, Wall &mur2, sf::Vector2f &ptCommun)
{
// Renvoie true si les deux murs peuvent être fusionnés
 
 
//Trois points nécessaires aux calculs
sf::Vector2f A, B, C;
 
//Vecteurs nécessaires aux calculs
sf::Vector2f V1, V2;
 
 
// On teste s'il y a deux points en commun
 
if (pointCommun(mur1.Pt1, mur2.Pt1))
// Cas de figure 1 : le point n°1 du mur 1 est le même que le point n°1 du mur 2
{
A = ptCommun = mur1.Pt1;
B = mur1.Pt2;
C = mur2.Pt2;
}
else if (pointCommun(mur1.Pt1, mur2.Pt2))
// Cas de figure 2 : le point n°1 du mur 1 est le même que le point n°2 du mur 2
{
A = ptCommun = mur1.Pt1;
B = mur1.Pt2;
C = mur2.Pt1;
}
else if (pointCommun(mur1.Pt2, mur2.Pt1))
// Cas de figure 3 : le point n°2 du mur 1 est le même que le point n°1 du mur 2
{
A = ptCommun = mur1.Pt2;
B = mur1.Pt1;
C = mur2.Pt2;
}
else if (pointCommun(mur1.Pt2, mur2.Pt2))
// Cas de figure 4 : le point n°2 du mur 1 est le même que le point n°2 du mur 2
{
A = ptCommun = mur1.Pt2;
B = mur1.Pt1;
C = mur2.Pt1;
}
else return false; // Sinon on arrête
 
// Teste si les trois points sont alignés (V1 = vecteur AC, V2 = vecteur AB)
V1.x = C.x - A.x;
V1.y = C.y - A.y;
 
V2.x = B.x - A.x;
V2.y = B.y - A.y;
 
 
//Petit rappel, trois points A, B et C sont alignés, quand les vecteurs AB et AC sont colinéaires
if (V1.y*V2.x - V1.x*V2.y == 0)
return true; // Si les points sont alignés on retourne "vrai" et on pourra fusionner les murs
 
return false; // Sinon on retourne "faux"
}
 
  Et bam ! Du vecteur dans tes dents ! laugh
  C'est du calcul pur et dur, mais l'important c'est que ça marche ! wink
   Et si vous comprenez : félicitations, vous avez suivi vos cours correctement en seconde (note de Jay : et vous ne les avez pas oubliés depuis! indecision) ! laugh
   Notre fonction pour fusionner nos murs :
 

Fichier : lightEntity.cpp, Rajouter à la suite :

bool lightEntity::fusionneMurs(Wall &mur1, Wall &mur2)
{
sf::Vector2f ptCommun;
if (coteAcote(mur1, mur2, ptCommun))
{
// On remplace le point commun du coté du mur1 par l'extrémité du mur2
 
if (pointCommun(ptCommun, mur1.Pt1) && pointCommun(ptCommun, mur2.Pt1))
mur1.Pt1 = mur2.Pt2;
else if (pointCommun(ptCommun, mur1.Pt1) && pointCommun(ptCommun, mur2.Pt2))
mur1.Pt1 = mur2.Pt1;
else if (pointCommun(ptCommun, mur1.Pt2) && pointCommun(ptCommun, mur2.Pt1))
mur1.Pt2 = mur2.Pt2;
else if (pointCommun(ptCommun, mur1.Pt2) && pointCommun(ptCommun, mur2.Pt2))
mur1.Pt2 = mur2.Pt1;
else return false;
 
return true;
}
 
return false;
}
 
   En fait, on attribue à notre premier mur le point qu'il n'a pas en commun sur le second mur à la place du point en commun. indecision
   Il ne nous restera plus qu'à supprimer le mur 2, ce que l'on fera dans AddTriangle(). D'où le fait que l'on renvoie une valeur "true" ou "false", selon si on a pu fusionner le mur ou non. wink
   Avant cela, mettons à jour les prototypes dans le fichier lightEntity.h 
 

Fichier : lightEntity.h, Rajouter :

//Modification de la position de chacun des points de chaque triangle de la lumière
void move(sf::Vector2f mouvement);
 
//Rajouter :
//Collisions de la lumière
bool pointCommun(sf::Vector2f pt1, sf::Vector2f pt2);
bool coteAcote(Wall &mur1, Wall &mur2, sf::Vector2f &ptCommun);
bool fusionneMurs(Wall &mur1, Wall &mur2); 

   Puis revenons modifier AddTriangle() dans lightEntity.cpp :

Fichier : lightEntity.cpp, Modifier la fin de la fonction :

// On fusionne les murs communs
for (int C = 0; C<(int)mur.size(); C++)
{
for (int D = 0; D<(int)mur.size(); D++)
{
 
if (C != D)
{
if (fusionneMurs(mur[C], mur[D]))
mur.erase(mur.begin() + D);
}
 
}
}
 
// On calcule notre triangle
AddShape(pt1, pt2, 0, mur);
 
} 
 
   C'est bien beau tout ça, mais mes collisions, on les calcule ou pas ? angry
   On y arrive, on y arrive ! Du calme !...indecision
   Mais avant, nous allons ajouter des fonctions pour tester les collisions dans des cas précis wink :
 

Fichier : lightEntity.cpp, Rajouter à la fin du fichier :

sf::Vector2f lightEntity::pointdIntersection(sf::Vector2f p1, sf::Vector2f p2, sf::Vector2f q1, sf::Vector2f q2)
{
//Cette fonction renvoie le point d'intersection des droites (p1 p2) et (q1 q2)
 
sf::Vector2f i;
 
if ((p2.x - p1.x) == 0 && (q2.x - q1.x) == 0) // On vérifie que les deux ne sont pas égaux à 0 (division par 0 = le mal !)
{
i.x = i.y = 0;
}
 
else if ((p2.x - p1.x) == 0) // On vérifie que p1 - p2 n'est pas égal à 0 (idem qu'au-dessus)
{
i.x = p1.x;
 
float c = (q2.y - q1.y) / (q2.x - q1.x);
float d = q1.y - q1.x * c;
 
i.y = c * i.x + d;
}
 
else if ((q2.x - q1.x) == 0) // Pareil avec q1 - q2
{
i.x = q1.x;
 
float a = (p2.y - p1.y) / (p2.x - p1.x);
float b = p1.y - p1.x * a;
 
i.y = a * i.x + b;
}
else // Sinon on peut faire les calculs sans contraintes
{
float a = (p2.y - p1.y) / (p2.x - p1.x);
float b = p1.y - p1.x * a;
 
float c = (q2.y - q1.y) / (q2.x - q1.x);
float d = q1.y - q1.x * c;
 
i.x = (d - b) / (a - c);
i.y = a * i.x + b;
}
 
 
return i;
}
 
 
sf::Vector2f lightEntity::segmentCollision(sf::Vector2f p1, sf::Vector2f p2, sf::Vector2f q1, sf::Vector2f q2)
{
// Retourne le point d'intersection entre deux segments [p1 p2] et [q1 q2], s'ils se croisent
 
sf::Vector2f i;
i = pointdIntersection(p1, p2, q1, q2);
 
if (((i.x >= p1.x - 0.1 && i.x <= p2.x + 0.1)
|| (i.x >= p2.x - 0.1 && i.x <= p1.x + 0.1))
&& ((i.x >= q1.x - 0.1 && i.x <= q2.x + 0.1)
|| (i.x >= q2.x - 0.1 && i.x <= q1.x + 0.1))
&& ((i.y >= p1.y - 0.1 && i.y <= p2.y + 0.1)
|| (i.y >= p2.y - 0.1 && i.y <= p1.y + 0.1))
&& ((i.y >= q1.y - 0.1 && i.y <= q2.y + 0.1)
|| (i.y >= q2.y - 0.1 && i.y <= q1.y + 0.1)))
return i;
else
return sf::Vector2f(0, 0); // Sinon, on revoie une valeur par défaut
}
 
 
bool lightEntity::estDansLeTriangle(sf::Vector2f L, sf::Vector2f pt1, sf::Vector2f pt2, float radius, sf::Vector2f &i)
{
// Cette fonction retourne true si un point se trouve dans le triangle
 
if (L.x * L.x + L.y * L.y < radius * radius)
{
i = pointdIntersection(pt1, pt2, sf::Vector2f(0, 0), L);
 
if (pt1 != i && pt2 != i)
if ((pt1.x >= i.x && pt2.x <= i.x) || (pt1.x <= i.x && pt2.x >= i.x))
if ((pt1.y >= i.y && pt2.y <= i.y) || (pt1.y <= i.y && pt2.y >= i.y))
if ((L.y >= 0 && i.y >= 0) || (L.y <= 0 && i.y <= 0))
if ((L.x >= 0 && i.x >= 0) || (L.x <= 0 && i.x <= 0))
return true;
}
return false;
} 

   Tout en n'oubliant pas de rajouter leurs prototypes dans lightEntity.h :

Fichier : lightEntity.h, Rajouter les prototypes :

//Collisions de la lumière
bool pointCommun(sf::Vector2f pt1, sf::Vector2f pt2);
bool coteAcote(Wall &mur1, Wall &mur2, sf::Vector2f &ptCommun);
bool fusionneMurs(Wall &mur1, Wall &mur2);
 
//Ajouter :
sf::Vector2f pointdIntersection(sf::Vector2f p1, sf::Vector2f p2,
sf::Vector2f q1, sf::Vector2f q2);
sf::Vector2f segmentCollision(sf::Vector2f p1, sf::Vector2f p2,
sf::Vector2f q1, sf::Vector2f q2);
bool estDansLeTriangle(sf::Vector2f L, sf::Vector2f pt1,
sf::Vector2f pt2, float radius, sf::Vector2f &i); 
 
   Chaque fonction a un but :
- pointdIntersection() retourne le point d'intersection de deux droites,
- segmentCollision() retourne le point d'intersection de deux segments (0,0 s'il n'y en a pas),
- estDansLeTriangle() retourne true si un point se trouve dans un triangle.
 
   Nous avons là tous nos outils pour notre collision avec la map. wink
   Créons (enfin ! surprise) notre fonction resizeLight() ! (oui je sais, le nom est pas terrible, j'en suis désolé, j'avais une panne d'inspiration voyez vous, et...
 

Fichier : lightEntity.h, Rajouter à nouveau :

//Collisions de la lumière
bool pointCommun(sf::Vector2f pt1, sf::Vector2f pt2);
bool coteAcote(Wall &mur1, Wall &mur2, sf::Vector2f &ptCommun);
bool fusionneMurs(Wall &mur1, Wall &mur2);
 
sf::Vector2f pointdIntersection(sf::Vector2f p1, sf::Vector2f p2,
sf::Vector2f q1, sf::Vector2f q2);
sf::Vector2f segmentCollision(sf::Vector2f p1, sf::Vector2f p2,
sf::Vector2f q1, sf::Vector2f q2);
bool estDansLeTriangle(sf::Vector2f L, sf::Vector2f pt1,
sf::Vector2f pt2, float radius, sf::Vector2f &i);
 
//Rajouter à la suite :
//Fonction pour gérer la collision de la lumière
void resizeLightEntity(sf::Vector2f &pt1, sf::Vector2f &pt2, sf::Vector2f l1,
sf::Vector2f l2, float radius, int minimumWall, std::vector <Wall> &mur); 

 

Fichier : lightEntity.cpp, Rajouter la nouvelle fonction :

// Collision avec un mur
void lightEntity::resizeLight(sf::Vector2f &pt1, sf::Vector2f &pt2, sf::Vector2f l1, sf::Vector2f l2, float radius, int minimumWall, std::vector <Wall> &mur)
{
sf::Vector2f i;
 
/** Test du cas 3 **/
 
if (estDansLeTriangle(l1, pt1, pt2, radius, i)) // Test du cas 3 pour la première extrémité
{
AddShape(i, pt2, minimumWall, mur), pt2 = i;
}
 
if (estDansLeTriangle(l2, pt1, pt2, radius, i)) // Test du cas 3 pour la seconde extrémité
{
AddShape(i, pt1, minimumWall, mur), pt1 = i;
}
 
/** Tests des cas 1 et 2 **/
 
// Point d'intersection du premier côté adjacent au mur
sf::Vector2f m = segmentCollision(l1, l2, sf::Vector2f(0, 0), pt1);
// Point d'intersection du deuxième côté adjacent au mur
sf::Vector2f n = segmentCollision(l1, l2, sf::Vector2f(0, 0), pt2);
// Point d'intersection du côté opposé au mur
sf::Vector2f o = segmentCollision(l1, l2, pt1, pt2);
 
 
// Si les deux côtés sont coupés ( m et n != 0,0 ), alors on est dans le cas 1 :
if ((m.x != 0 || m.y != 0) && (n.x != 0 || n.y != 0))
{
// On place les points au niveau des intersections de chaque côté
pt1 = m;
pt2 = n;
}
 
// Plus qu'un cas à tester (le cas 2, si vous êtes perdu)
else
{
// Si un côté adjacent est coupé et un côté opposé est coupé,
// on crée une subdivision en plaçant le point correspondant à la place de o
if ((m.x != 0 || m.y != 0) && (o.x != 0 || o.y != 0))
{
AddShape(m, o, minimumWall, mur);
pt1 = o;
}
 
else if ((n.x != 0 || n.y != 0) && (o.x != 0 || o.y != 0))
{
AddShape(o, n, minimumWall, mur);
pt2 = o;
}
}
 
// Et voilà, plus qu'à appliquer le reste des modifications !
} 

   Et on appelle désormais la fonction dans AddShape() :

Fichier : lightEntity.cpp, Mettre à jour :

//Ajout d'un sous triangle
void lightEntity::AddShape(sf::Vector2f pt1, sf::Vector2f pt2, int minimumWall, std::vector <Wall> &mur)
{
// Variable qui contiendra l'intensité calculée, pour le dégradé
float intensity;
 
// On "coupe" la lumière par rapport à chacun des murs
// Pas besoin de tester les murs déjà testés par les triangles précédents, ça fait gagner beaucoup de temps ;)
for (int i = minimumWall; i<mur.size(); i++, minimumWall++)
{
resizeLight(pt1, pt2, mur[i].Pt1, mur[i].Pt2, m_radius, minimumWall, mur);
}
 
// On ajoute un shape
m_triangle.push_back(sf::VertexArray());
m_triangle.back().setPrimitiveType(sf::Triangles);
 
 
// On lui donne comme point de départ (0,0), le centre de la lumière, avec la couleur et l'intensité maximales
m_triangle.back().append(sf::Vertex(m_position,
sf::Color((int)(m_intensity*m_color.r / 255),
(int)(m_intensity*m_color.g / 255),
(int)(m_intensity*m_color.b / 255))));
 
// On calcule où l'on se trouve par rapport au centre, pour savoir à quelle intensité on est
intensity = m_intensity - sqrt(pt1.x*pt1.x + pt1.y*pt1.y)*m_intensity / m_radius;
// Et on ajoute un point au shape
m_triangle.back().append(sf::Vertex(m_position + pt1,
sf::Color((int)(intensity*m_color.r / 255),
(int)(intensity*m_color.g / 255),
(int)(intensity*m_color.b / 255))));
 
// Idem
intensity = m_intensity - sqrt(pt2.x*pt2.x + pt2.y*pt2.y)*m_intensity / m_radius;
m_triangle.back().append(sf::Vertex(m_position + pt2,
sf::Color((int)(intensity*m_color.r / 255),
(int)(intensity*m_color.g / 255),
(int)(intensity*m_color.b / 255))));
 
} 

   On compile et !!!!... TADAAAAAAAA !!!!! angel
   Nos lumières gèrent les collisions avec les murs ! wink
   Mais ils nous restent encore quelques ajustements à faire... blush

        Ajustements

   Nos pentes sont gérées comme des blocs vides, nos plateformes traversables comme des blocs pleins, et nos blocs de plateformes aussi... surprise Réglons vite ça ! indecision

Fichier : lightEntity.cpp, Modifier la fonction AddTriangle() :

// Ajout d'un triangle
void lightEntity::AddTriangle(sf::Vector2f pt1, sf::Vector2f pt2, Map &map)
{
// Création de notre tableau de murs
std::vector <Wall> mur;
 
//Mur temporaire
Wall tempMur;
 
// Création d'un vertex array à partir duquel nous allons créer notre boîte englobante
sf::VertexArray triangle(sf::Triangles, 3);
triangle[0].position = m_position;
triangle[1].position = m_position + pt1;
triangle[2].position = m_position + pt2;
 
// Création de notre boîte englobante
sf::FloatRect boundingBox = triangle.getBounds();
 
// Création d'un tableau de 4 points qui seront égaux aux quatre points de chaque tile
sf::Vector2f l[4];
 
// Nos variables compteurs pour nos boucles for
int x, y;
 
// On crée notre boucle for sur y puis sur x de manière à tester toutes les tiles de la boite englobante
 
for (y = (int)(boundingBox.top / 32) - 2 + (map.getStartY() / 32);
y < ((int)(boundingBox.top + boundingBox.height) / 32) + 2 + (map.getStartY() / 32); y++)
{
for (x = (int)(boundingBox.left / 32) - 2 + (map.getStartX() / 32);
x < ((int)(boundingBox.left + boundingBox.width) / 32) + 2 + (map.getStartX() / 32); x++)
{
// Ici nous testons nos tiles
 
// Si ce sont des tiles solides
if (map.getTile(y, x) > 80)
{
// On teste si ce ne sont pas des tiles tronquées (bord de plateforme) ou des tiles d'ombre
 
if (map.getTile(y, x) != 116 && map.getTile(y, x) != 117
&& map.getTile(y, x) != 120 && map.getTile(y, x) != 121
&& (map.getTile(y, x) < 96 || map.getTile(y, x) > 99))
{
//l0, l1, l2, l3 sont les coordonnée de chaque point du bloc relative au centre de la lumière
l[0] = sf::Vector2f(x * 32 - map.getStartX(),
y * 32 - map.getStartY());
 
l[1] = sf::Vector2f((x + 1) * 32 - map.getStartX(),
y * 32 - map.getStartY());
 
l[2] = sf::Vector2f((x + 1) * 32 - map.getStartX(),
(y + 1) * 32 - map.getStartY());
 
l[3] = sf::Vector2f(x * 32 - map.getStartX(),
(y + 1) * 32 - map.getStartY());
 
l[0] -= m_position;
l[1] -= m_position;
l[2] -= m_position;
l[3] -= m_position;
 
// Création de murs pour gérer les collisions à la place des tiles,
// ça permet de les fusionner pour moins subdiviser les triangles
//VS 2013 refuse : mur.push_back(Wall(l[0], l[1]));
//car il n'y a pas de constructeur, mais pas Xcode... :(
//Donc, la solution complexe est :
tempMur.Pt1 = l[0];
tempMur.Pt2 = l[1];
mur.push_back(tempMur);
 
 
// Si la tile est une plateforme traversable, on ne bloque que la lumière au premier segment
if (map.getTile(y, x) > 99)
{
tempMur.Pt1 = l[1];
tempMur.Pt2 = l[2];
mur.push_back(tempMur);
 
tempMur.Pt1 = l[2];
tempMur.Pt2 = l[3];
mur.push_back(tempMur);
 
tempMur.Pt1 = l[3];
tempMur.Pt2 = l[0];
mur.push_back(tempMur);
 
//Idem au lieu de :
//mur.push_back(Wall(l[1], l[2]));
//mur.push_back(Wall(l[2], l[3]));
//mur.push_back(Wall(l[3], l[0]));
}
 
}
 
// On teste si c'est une tile tronquée à gauche
else if (map.getTile(y, x) == 116 || map.getTile(y, x) == 120)
{
// Les tiles designé par Jay ne sont pas vraiment triangulaires, mais plutot un polygone à 5 cotés
// Nous allons donc créer 5 murs
 
l[0] = sf::Vector2f(x * 32 - map.getStartX(),
y * 32 - map.getStartY());
 
l[1] = sf::Vector2f((x + 1) * 32 - map.getStartX(),
y * 32 - map.getStartY());
 
l[2] = sf::Vector2f((x + 1) * 32 - map.getStartX(),
(y + 1) * 32 - map.getStartY());
 
l[3] = sf::Vector2f((x + 1) * 32 - map.getStartX() - 8,
(y + 1) * 32 - map.getStartY());
 
l[0] -= m_position;
l[1] -= m_position;
l[2] -= m_position;
l[3] -= m_position;
 
tempMur.Pt1 = l[0];
tempMur.Pt2 = l[1];
mur.push_back(tempMur);
 
tempMur.Pt1 = l[1];
tempMur.Pt2 = l[2];
mur.push_back(tempMur);
 
tempMur.Pt1 = l[2];
tempMur.Pt2 = l[3];
mur.push_back(tempMur);
 
tempMur.Pt1 = l[3];
tempMur.Pt2 = sf::Vector2f(l[0].x, l[0].y + 8);
mur.push_back(tempMur);
 
tempMur.Pt1 = sf::Vector2f(l[0].x, l[0].y + 8);
tempMur.Pt2 = l[0];
mur.push_back(tempMur);
 
/* Idem au lieu de :
mur.push_back(Wall(l[0], l[1]));
mur.push_back(Wall(l[1], l[2]));
mur.push_back(Wall(l[2], l[3]));
mur.push_back(Wall(l[3], sf::Vector2f(l[0].x, l[0].y + 8)));
mur.push_back(Wall(sf::Vector2f(l[0].x, l[0].y + 8), l[0]));*/
 
}
 
// On teste si c'est une tile tronquée à droite
else if (map.getTile(y, x) == 117 || map.getTile(y, x) == 121)
{
// Les tiles designé par Jay ne sont pas vraiment triangulaires, mais plutot un polygone à 5 cotés
// Nous allons donc créer 5 murs
 
l[0] = sf::Vector2f(x * 32 - map.getStartX(),
y * 32 - map.getStartY());
 
l[1] = sf::Vector2f((x + 1) * 32 - map.getStartX(),
y * 32 - map.getStartY());
 
l[2] = sf::Vector2f((x + 1) * 32 - map.getStartX(),
y * 32 - map.getStartY() + 8);
 
l[3] = sf::Vector2f(x * 32 - map.getStartX() + 8,
(y + 1) * 32 - map.getStartY());
 
l[0] -= m_position;
l[1] -= m_position;
l[2] -= m_position;
l[3] -= m_position;
 
tempMur.Pt1 = l[0];
tempMur.Pt2 = l[1];
mur.push_back(tempMur);
 
tempMur.Pt1 = l[1];
tempMur.Pt2 = l[2];
mur.push_back(tempMur);
 
tempMur.Pt1 = l[2];
tempMur.Pt2 = l[3];
mur.push_back(tempMur);
 
tempMur.Pt1 = l[3];
tempMur.Pt2 = sf::Vector2f(l[3].x - 8, l[3].y);
mur.push_back(tempMur);
 
tempMur.Pt1 = sf::Vector2f(l[3].x - 8, l[0].y);
tempMur.Pt2 = l[3];
mur.push_back(tempMur);
 
/* Idem au lieu de :
mur.push_back(Wall(l[0], l[1]));
mur.push_back(Wall(l[1], l[2]));
mur.push_back(Wall(l[2], l[3]));
mur.push_back(Wall(l[3], sf::Vector2f(l[3].x - 8, l[3].y)));
mur.push_back(Wall(sf::Vector2f(l[3].x - 8, l[0].y), l[3])); */
 
}
}
else if (map.getTile(y, x) > 68 && map.getTile(y, x) < 73) // Si c'est une pente
{
switch (map.getTile(y, x))
{
// On place les points correctement selon la pente
case 69:
l[0] = sf::Vector2f((x + 1) * 32 - map.getStartX(),
(y + 1) * 32 - map.getStartY());
 
l[1] = sf::Vector2f((x + 1) * 32 - map.getStartX() - 1,
y * 32 - map.getStartY() + 16);
 
l[2] = sf::Vector2f(x * 32 - map.getStartX(),
(y + 1) * 32 - map.getStartY());
 
l[3] = l[0];
break;
 
case 70:
l[0] = sf::Vector2f(x * 32 - map.getStartX(),
y * 32 - map.getStartY() + 16);
 
l[1] = sf::Vector2f((x + 1) * 32 - map.getStartX(),
y * 32 - map.getStartY());
 
l[2] = sf::Vector2f((x + 1) * 32 - map.getStartX(),
(y + 1) * 32 - map.getStartY());
 
l[3] = sf::Vector2f(x * 32 - map.getStartX() - 1,
(y + 1) * 32 - map.getStartY());
break;
 
case 71:
l[0] = sf::Vector2f(x * 32 - map.getStartX(),
(y + 1) * 32 - map.getStartY());
 
l[1] = sf::Vector2f((x + 1) * 32 - map.getStartX(),
y * 32 - map.getStartY() + 16);
 
l[2] = sf::Vector2f((x + 1) * 32 - map.getStartX(),
(y + 1) * 32 - map.getStartY());
 
l[3] = l[0];
break;
 
case 72:
l[0] = sf::Vector2f(x * 32 - map.getStartX(),
y * 32 - map.getStartY());
 
l[1] = sf::Vector2f((x + 1) * 32 - map.getStartX(),
y * 32 - map.getStartY() + 16);
 
l[2] = sf::Vector2f((x + 1) * 32 - map.getStartX(),
(y + 1) * 32 - map.getStartY());
 
l[3] = sf::Vector2f(x * 32 - map.getStartX(),
(y + 1) * 32 - map.getStartY());
break;
 
}
 
l[0] -= m_position;
l[1] -= m_position;
l[2] -= m_position;
l[3] -= m_position;
 
// Création de murs pour gérer les collisions à la place des tiles,
// ça permet de les fusionner pour moins subdiviser les triangles
tempMur.Pt1 = l[0];
tempMur.Pt2 = l[1];
mur.push_back(tempMur);
 
tempMur.Pt1 = l[1];
tempMur.Pt2 = l[2];
mur.push_back(tempMur);
 
tempMur.Pt1 = l[2];
tempMur.Pt2 = l[3];
mur.push_back(tempMur);
 
/* Idem au lieu de :
mur.push_back(Wall(l[0], l[1]));
mur.push_back(Wall(l[1], l[2]));
mur.push_back(Wall(l[2], l[3])); */
 
// Si c'est une partie inférieure de pente, on n'a besoin de gérer les collision qu'avec un triangle
if (map.getTile(y, x) != 69 && map.getTile(y, x) != 71)
{
tempMur.Pt1 = l[3];
tempMur.Pt2 = l[0];
mur.push_back(tempMur);
 
/* Idem au lieu de :
mur.push_back(Wall(l[3], l[0])); */
}
 
}
}
}
 
// On fusionne les murs communs
for (int C = 0; C<(int)mur.size(); C++)
{
for (int D = 0; D<(int)mur.size(); D++)
{
 
if (C != D)
{
if (fusionneMurs(mur[C], mur[D]))
mur.erase(mur.begin() + D);
}
 
}
}
 
// On calcule notre triangle
AddShape(pt1, pt2, 0, mur);
 
} 

   Et voilàààà !!!!! angel

   Nous avons notre système de lumières entièrement fonctionnel ! Et le moins que l'on puisse dire, c'est que ça en jette !! laugh

   Maintenant, à vous de créer vos propres niveaux, vos propres lumières, votre propre jeu ! wink

        Pour aller encore plus loin

   Ces lumières ont la particularité d'être customisables sur plein de points différents. Vous pouvez donc en faire plein de choses différentes. Et si vous essayiez de déchaîner votre imagination ? Voici quelques idées de ce que vous pouvez faire avec vos lumières :
- Créer le Rabidja Night Club ! cheeky
- Créer un niveau où il faut suivre une luciole qui se déplace dans tout le niveau et qui est la seule source de lumière du niveau !
- Créer des monstres qui n'attaquent le joueur que s’il est dans une lumière !
- Créer des monstres qui attaquent notre lapin si celui-ci ne se réfugie pas dans la clarté d'une petite lanterne !
- Créer des niveaux d'infiltration où notre lapin se fera repérer s’il traverse une lumière !
- Créer des monstres invisibles dans l'obscurité !
- Créer des fantômes qui meurent à la lumière !

   Il y a encore plein d'autres idées qui ne demandent qu'à être exploitées ! wink
   C'est en exploitant chaque fonctionnalité jusqu'au bout que l'on arrive à faire un jeu original. Je compte sur vous pour trouver des idées plus géniales les unes que les autres ! Et n'hésitez pas à les partager via les commentaires et les forums ! smiley

 

        Remerciements

   Je souhaiterais remercier Jay pour tout son travail, que ce soit pour m'avoir aidé à écrire ces chapitres, montré et corrigé certains bugs, corrigé mes erreurs (et il y en avait beaucoup :lol: ), réécrit des parties entières de ces tutos, avoir accepté d'héberger ces chapitres, mais aussi pour avoir créé et s'être occupé si bien de Meruvia.fr, ce qui a permis à une super communauté de grandir. Merci beaucoup Jay ! 

   Réponse de Jay : Je tiens moi aussi à remercier Skrool pour tout le temps qu'il a passé à écrire ces super tutoriels et pour nous avoir fait part de ses lumières (oui, je sais, le jeu de mots est pourri !… indecision).

   Sinon, effectivement, comme le dit Skrool, la communauté meruvienne ne cesse de s'agrandir, et c'est avec une grande joie que j'accueille tout un chacun, qui peut à son tour écrire un tutoriel, même basique, sur un point qu'il domine particulièrement. cool On en a de plus en plus, et c'est vraiment super ! 

   Et pour finir, un grand merci à Skrool, une fois de plus. angel

 

   @ bientôt sur Meruvia ! angel

                                          Skrool 

 

This site uses cookies to enable you to log in. We do not store or sell any personal data. By continuing to use this website, you agree to their use. Thanks!