
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 !
Et on est reparti ! 
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();
|
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;
|
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))));
}
|
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);
}
|
Remédions tout de suite à cela
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"
}
|
C'est du calcul pur et dur, mais l'important c'est que ça marche !
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!
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;
}
|
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.
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);
}
|
On y arrive, on y arrive ! Du calme !...
Mais avant, nous allons ajouter des fonctions pour tester les collisions dans des cas précis
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);
|
- 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.
Créons (enfin !
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 !!!!! ![]()
Nos lumières gèrent les collisions avec les murs ! ![]()
Mais ils nous restent encore quelques ajustements à faire... ![]()
Ajustements
Nos pentes sont gérées comme des blocs vides, nos plateformes traversables comme des blocs pleins, et nos blocs de plateformes aussi...
Réglons vite ça ! ![]()
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àààà !!!!! ![]()
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 !! ![]()
Maintenant, à vous de créer vos propres niveaux, vos propres lumières, votre propre jeu ! ![]()
Pour aller encore plus loin
- 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 ! ![]()
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 ! ![]()
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 !…
).
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.
On en a de plus en plus, et c'est vraiment super !
Et pour finir, un grand merci à Skrool, une fois de plus. ![]()

@ bientôt sur Meruvia ! ![]()
Skrool

English
Français 