Un jeu en 2D, c'est avant tout des sprites.
Je me propose ici de vous fournir ma méthode pour gérer ces sprites.
J'utiliserais 2 classes :
- Une classe Sprite
- Une classe Vecteur

La classe vecteur
Un vecteur est un objet simple pour se repérer dans le plan. Je trouve avantageux d'utiliser des vecteurs plutôt qu'un simple système de coordonnées pour nos sprites. En effet, on peut ici ajouter des opérateurs entre nos vecteurs, ce qui va simplifier grandement les calculs (mais peut-être pas le code...).

Nos vecteurs auront pour seul caractéristique une composante sur l'axe des ordonnées, et une sur l'axe des abscisses. De plus, cette composante sera du type déclaré avec le vecteur (grâce aux templates), de façon à avoir là un type de base souple.

Voici le code du vecteur :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
 
#define cX	0
#define cY	1
 
/*
	Classe représentant un vecteur
*/
template <typename T>
class Vecteur
{
	private:
		// Composantes
		T m_X;
		T m_Y;
 
	public:
		Vecteur ();
		Vecteur (T x, T y);
 
		// Accesseurs
		T	GetX ()	{return m_X;}
		T	GetY ()	{return m_Y;}
 
		// Mutateurs
		void SetX (T x)	{m_X = x;}
		void SetY (T y)	{m_Y = y;}
 
		// Méthodes
		T	 GetNorme ();
 
		// Opérateurs
		Vecteur operator + (Vecteur vec); // Somme
		Vecteur operator - (Vecteur vec); // Différence
		Vecteur operator * (T v); 		  // Produit
		T 		operator * (Vecteur vec); // Produit scalaire
		T&		operator [] (int index);  // Retourne la composante index (X, Y ou Z)
};
 
template <typename T>
Vecteur <T>::Vecteur ()
{
	SetX (0);
	SetY (0);
}
 
template <typename T>
Vecteur <T>::Vecteur (T x, T y)
{
	SetX (x);
	SetY (y);
}
 
template <typename T>
T Vecteur <T>::GetNorme ()
{
	return sqrt (pow (GetX (), 2) + pow (GetY (), 2));
} 
 
template <typename T>
Vecteur <T> Vecteur<T>::operator + (Vecteur vec)
{
	Vecteur <T> new_vec (GetX () + vec.GetX (), GetY () + vec.GetY ());
	return new_vec;
}
 
template <typename T>
Vecteur <T> Vecteur<T>::operator - (Vecteur vec)
{
	Vecteur <T> new_vec (GetX () - vec.GetX (), GetY () - vec.GetY ());
	return new_vec;
}
 
template <typename T>
Vecteur <T> Vecteur<T>::operator * (T v)
{
	Vecteur <T> new_vec (GetX () * v, GetY () * v);
	return new_vec;
}
 
template <typename T>
T Vecteur<T>::operator * (Vecteur vec)
{
	return GetX () * vec.GetX () + GetY () * vec.GetY ();
}
 
template <typename T>
T& Vecteur<T>::operator [] (int index)
{
	if (index == cX)
		return m_X;
	else if (index == cY)
		return m_Y;
	else
		return m_X;
}
La classe Sprite

La classe Sprite contiendra :
- Un vecteur Position
- Un vecteur Vitesse
- Un vecteur accélération
- Une vitesse maximum
- Un angle
- Un "zoom" (qui servira à modifier la taille de l'image affichée)
- Un tableau de bitmaps
- Un entier indiquant quelle image du tableau d'images afficher.

Comme méthodes :
-Une fonction Move, qui déplacera le sprite en fonction des différents vecteurs.
- Une fonction Clear qui délestera le sprite de ses images
- Une fonction Draw (surchargée) qui affichera le sprite à l'écran.

De plus, l'on ajouteras 3 fonctions manipulant le sprite :
- Une fonction regardant si le point de coordonnée spécifié est inclue dans le sprite
- Une fonction qui regarde si le pixel spécifié est transparent
Ces deux fonctions étant là pour le traitement des collisions, effectué grâce à la fonction "AreCollide", qui teste les collisions entre deux sprite spécifiés en argument.

Voici la classe :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
 
/*
	Le sprite est un objet graphique, animé ou non.
*/
class Sprite
{
	protected:
		// Position, vitesse et accélération
		Vecteur <float> m_vPos;
		Vecteur <float> m_vSpeed;
		Vecteur <float> m_vAccel;
		// Vitesse max (Norme vecteur vitesse maximum)
		float	m_fVMax;
		// Angle, radians
		float	m_fAngle;
		// Echelle
		float	m_fScale;
 
		// Image
		// Tableau d'images
		ALLEGRO_BITMAP	**m_ppImages;
		int				m_iCurrentImage;
		int				m_iNImages;
 
	public:
		Sprite ();
		Sprite (float posX, float posY, 
				float speedX, float speedY,
				float accelX, float accelY,
				float VMax, float angle,
				float scale, ...);
		~Sprite ();
 
		// Position, vitesse et accélération
		// Mutateurs
		void SetPosX (float value)		{ m_vPos [cX] = value;}
		void SetPosY (float value)		{ m_vPos [cY] = value;}
		void SetSpeedX (float value)	{ if (sqrt (pow (value, 2) + pow (GetSpeed ().GetY (), 2)) < GetVMax ()) m_vSpeed [cX] = value;}
		void SetSpeedY (float value)	{ if (sqrt (pow (GetSpeed (). GetX (), 2) + pow (value, 2)) < GetVMax ()) m_vSpeed [cY] = value;}
		void SetAccelX (float value)	{ m_vAccel [cX] = value;}
		void SetAccelY (float value)	{ m_vAccel [cY] = value;}
		void SetVMax (float value)		{ m_fVMax = value;}
		void SetAngle (float angle)		{ m_fAngle = angle;}
		void SetScale (float scale)		{ m_fScale = scale;}
		// Accesseurs
		Vecteur <float> GetPos ()	{ return m_vPos;}
		Vecteur <float> GetSpeed ()	{ return m_vSpeed;}
		Vecteur <float> GetAccel ()	{ return m_vAccel;}
		float GetVMax ()			{ return m_fVMax;}
		float GetAngle ()			{ return m_fAngle;}
		float GetScale ()			{ return m_fScale;}
		// Méthodes générales
		void Move ();
		void Clear ();
		void AddEffect ();
		void Draw ();
		void Draw (int posX, int posY);
 
		// Image
		void SetNumberOfImages (int n) 					{ m_iNImages = n; m_ppImages = (ALLEGRO_BITMAP **) malloc (n * sizeof (ALLEGRO_BITMAP *));}
		void SetImage (int n, const char *image);
		void SetCurrentImage (int value) 				{ m_iCurrentImage = value;}
		int  GetCurrentImage () 						{return m_iCurrentImage;}
		int	 GetNumberOfImages ()						{return m_iNImages;}
		ALLEGRO_BITMAP* GetCurrentBitmap ()  			{return m_ppImages [GetCurrentImage ()];}
 
 
};
 
/*
	Fonctions génériques
*/
bool IsInclude (int, int, Sprite);
bool IsTransparent (ALLEGRO_COLOR);
bool AreCollide (Sprite *, Sprite *);
Voilà les définitions des différentes méthodes, constructeurs, etc :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
 
#include "Vecteur.h"
#include "Sprite.h"
 
Sprite::Sprite ()
{
 
}
 
Sprite::Sprite (float posX, float posY, 
				float speedX, float speedY,
				float accelX, float accelY,
				float VMax, float angle,
				float scale, ...)
{
	SetPosX (posX);
	SetPosY (posY);
 
	SetVMax (VMax);
 
	SetSpeedX (speedX);
	SetSpeedY (speedY);
 
	SetAccelX (accelX);
	SetAccelY (accelY);
 
	SetAngle (angle);
	SetScale (scale);
 
	va_list lptr;
	// On compte le nombre d'images
	int NImages = 0;
	va_start (lptr, scale);
	for (NImages = 0; va_arg (lptr, const char *) != NULL; NImages++) {}
	// On alloue le tableau d'images du sprite;
	SetNumberOfImages (NImages);
	// On charge les images
	va_start (lptr, scale);
	for (int i = 0; i < NImages; i++)
		SetImage (i, va_arg (lptr, const char *));
	SetCurrentImage (0);
}
 
Sprite::~Sprite ()
{
 
}
 
/*
	La méthode Move permet de modifier les coordonnées du sprite en fonction de sa vitesse et de son accélération
*/
void
Sprite::Move ()
{
	/* Accélération */
	if (m_vSpeed.GetNorme () < GetVMax ())
	  {
		m_vSpeed [cX] = m_vSpeed.GetX () * m_vAccel.GetX ();
		m_vSpeed [cY] = m_vSpeed.GetY () * m_vAccel.GetY ();
	  }
 
	/* Déplacement */
	m_vPos = m_vPos + m_vSpeed;
}
 
/*
	Clear libère la mémoire allouée au sprite
*/
void
Sprite::Clear ()
{
	for (int i = 0; i < m_iNImages; i++)
	  {
		al_destroy_bitmap (m_ppImages [i]);
	  }
	free (m_ppImages);
}
 
/*
	SetImage permet de charger l'image ayant pour chemin "image" dans le tableau d'images du sprite à l'emplacement 'n'.
*/
void 
Sprite::SetImage (int n, const char *image)		
{ 
	try
	  {
		m_ppImages [n] = al_load_bitmap (image);
		if (m_ppImages [n] == NULL)
			throw 0;
		else
		  {
			al_convert_mask_to_alpha (m_ppImages [n], al_get_pixel (m_ppImages [n], 0, 0));
		  }
	  }
 
	catch (int e)
	  {
		if (e == 0)
			std::cerr << "Cannot load image " << image << std::endl;
		system ("pause");
	  }
}
 
/*
	La méthode Draw dessine le sprite sur le bitmap de destination, aux coordonnées du sprite
*/
void
Sprite::Draw ()
{
	if (m_ppImages [GetCurrentImage ()] != NULL)
	  {
		al_draw_rotated_scaled_bitmap (m_ppImages [GetCurrentImage ()], 
								al_get_bitmap_width (m_ppImages [GetCurrentImage ()]) / 2, 
								al_get_bitmap_height (m_ppImages [GetCurrentImage ()]) / 2,
								GetPos ().GetX (), GetPos ().GetY (), 
								GetScale (), GetScale (), 
								GetAngle (), 0);
	  }
}
 
void 
Sprite::Draw (int posX, int posY)
{
	if (m_ppImages [GetCurrentImage ()] != NULL)
	  {
		al_draw_rotated_scaled_bitmap (m_ppImages [GetCurrentImage ()], 
								al_get_bitmap_width (m_ppImages [GetCurrentImage ()]) / 2, 
								al_get_bitmap_height (m_ppImages [GetCurrentImage ()]) / 2,
								posX, posY, 
								GetScale (), GetScale (), 
								GetAngle (), 0);
	  }
}
Et la fonction de collision (et ses acolytes)
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
 
/*
	Retourne vrai si le point de coordonné spécifié appartient au sprite spécifié
*/
bool
IsInclude (int posX, int posY, Sprite *sprite)
{
	int limX = sprite->GetScale () * al_get_bitmap_width (sprite->GetCurrentBitmap ());
	int limY = sprite->GetScale () * al_get_bitmap_height (sprite->GetCurrentBitmap ());
 
	if (posX >= sprite->GetPos ().GetX () 
		&& posX <= sprite->GetPos ().GetX () + limX
		&& posY >= sprite->GetPos ().GetY () 
		&& posY <= sprite->GetPos ().GetY () + limY)
		return true;
 
	return false;
}
 
/*
	Retourne vrai si le point a une composante alpha = 255
*/
bool 
IsTransparent (ALLEGRO_COLOR color)
{
	unsigned char r, g, b, a;
 
	al_unmap_rgba (color, &r, &g, &b, &a);
 
	if (a != 255)
		return true;
	else
		return false;
}
 
/*
	Regarde si les deux sprites sont en collision
*/
#define PAS	4
bool	
AreCollide (Sprite *sprite1, Sprite *sprite2)
{
	// On regarde si la distance entre les deux sprites nécessite un test plus poussé
	Vecteur <float> s1s2;
	s1s2 = sprite1->GetPos ();
	s1s2 = s1s2 * -1;
	s1s2 = s1s2 + sprite2->GetPos ();
	// Diamètre sprite 1
	Vecteur <float> D1 (sprite1->GetScale () * al_get_bitmap_width (sprite1->GetCurrentBitmap ()), 
						sprite1->GetScale () * al_get_bitmap_height (sprite1->GetCurrentBitmap ()));
	// Diamètre sprite 2
	Vecteur <float> D2 (sprite2->GetScale () * al_get_bitmap_width (sprite2->GetCurrentBitmap ()), 
						sprite2->GetScale () * al_get_bitmap_height (sprite2->GetCurrentBitmap ()));
	// Si D1 + D2 < distance s1s2
	if (D1.GetNorme () + D2.GetNorme () < s1s2.GetNorme ())
		return false;
 
	// On recréé deux bitmaps correspondants aux nouvelles images
	ALLEGRO_BITMAP *bmp1 = NULL;
	ALLEGRO_BITMAP *bmp2 = NULL;
	ALLEGRO_BITMAP *target = al_get_target_bitmap ();
 
	// On reagrde les dimensions...
	// ...du sprite 1
	// 1.5 = marge d'erreur
	bmp1 = al_create_bitmap (1.5 * sprite1->GetScale () * al_get_bitmap_width (sprite1->GetCurrentBitmap ()), 
							 1.5 * sprite1->GetScale () * al_get_bitmap_height (sprite1->GetCurrentBitmap ()));
	// ...du sprite 2
	bmp2 = al_create_bitmap (1.5 * sprite2->GetScale () * al_get_bitmap_width (sprite2->GetCurrentBitmap ()), 
							1.5 * sprite2->GetScale () * al_get_bitmap_height (sprite2->GetCurrentBitmap ()));
 
	// On dessine les deux bitmaps
	// Sprite 1
	al_set_target_bitmap (bmp1);
	al_clear_to_color (al_map_rgb (0, 0, 0));
	sprite1->Draw (0, 0);
	// Sprite 2
	al_set_target_bitmap (bmp2);
	al_clear_to_color (al_map_rgb (0, 0, 0));
	sprite2->Draw (0, 0);
	// Remise par défaut du bitmap de destination
	al_set_target_bitmap (target);
 
	al_draw_bitmap (bmp1, 0, 0, 0);
 
	al_convert_mask_to_alpha (bmp1, al_map_rgb (255, 0, 255));
	al_convert_mask_to_alpha (bmp2, al_map_rgb (255, 0, 255));
 
	al_lock_bitmap (bmp1, al_get_bitmap_format(bmp1), ALLEGRO_LOCK_READONLY);
	al_lock_bitmap (bmp2, al_get_bitmap_format(bmp2), ALLEGRO_LOCK_READONLY);
	for (int i = 0; i < al_get_bitmap_width (bmp1); i+=PAS)
	  {
		for (int j = 0; j < al_get_bitmap_height (bmp1); j+=PAS)
		  {
			// Si le point donné appartient aux deux sprites simultanément
			if (IsInclude (sprite1->GetPos ().GetX () + i, sprite1->GetPos ().GetY () + j, sprite2))
			  {
				// Si le point donné n'est pas magic pink sur le premier sprite
				if (!IsTransparent (al_get_pixel (bmp1, i, j)))
				  {
					// Si le point donné n'est pas de la couleur magic pink sur le second sprite
					if (!IsTransparent (al_get_pixel (bmp2, sprite2->GetPos ().GetX () + i - sprite1->GetPos ().GetX (), 
															sprite2->GetPos ().GetY () + j - sprite1->GetPos ().GetY ())))
					  {
						al_unlock_bitmap (bmp1);
						al_unlock_bitmap (bmp2);
						al_destroy_bitmap (bmp1);
						al_destroy_bitmap (bmp2);
						return true;
					  }
				  }
			  }
		  }
	  }
 
	al_unlock_bitmap (bmp1);
	al_unlock_bitmap (bmp2);
	al_destroy_bitmap (bmp1);
	al_destroy_bitmap (bmp2);
	return false;
}
La fonction de collision teste si deux pixels des sprites spécifiés sont superposés. Ce traitement étant gourmand, j'ai préféré ne tester qu'un pixel sur 4 (constante PAS), mais l'on peut affiner la précision si nécessaire.






Notez bien que tout cela est loin d'être parfait, je vous donne juste ma méthode dont vous pouvez vous inspirer si cela vous plait.

Je remerci d'avance ceux qui voudront bien me dire ce qu'ils pensent de tout cela.
(J'ai conscience que c'est pas explicatif, mais je pense qu'on peut se débrouiller avec les commentaires).