IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Allegro Discussion :

[C++]Une classe Sprite


Sujet :

Allegro

  1. #1
    Membre averti Avatar de yetimothee
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2007
    Messages
    260
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Maine et Loire (Pays de la Loire)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2007
    Messages : 260
    Points : 364
    Points
    364
    Par défaut [C++]Une classe Sprite
    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).

  2. #2
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 928
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 928
    Points : 220 601
    Points
    220 601
    Billets dans le blog
    128
    Par défaut
    Bonjour,

    Pour moi, votre Vecteur (que, je suis d'accord avec l'idée) devrait être une structure (élimination des get / set ), de plus il devrait avoir un operateur << pour des qestions de debugguage (affichage simple dans une console)

    Pour le 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.
    La vitesse ; accelération ; vitesse maximum, n'a pas à être ici ... si je veux un Sprite fixe, je m'encombre avec des données ... (on pourra par contre, les ajouter avec un héritage (MobileSprite) ou alors ... en faisant une classe Mobile , qui elle utilise un sprite pour s'afficher.
    Un tableau de bitmaps / un entier indiquant l'image ... donc c'est une animation.
    Et là, pour moi, un sprite et une animation sont deux choses différentes (habituellement, je fais en sorte que l'animation hérite de Sprite (voir le code d'OpenAWars).
    Maintenant c'est surement des choix personnelles, et vous pouvez faire comme vous voulez.
    (L'animation peut aussi être, simplement, un tableau de Sprite (sans avoir nécéssairement un héritage) ...

    (Je vous conseille de lire ceci: http://blog.emmanueldeloget.com/inde...abilite-unique ; pour moi, actuellement, votre classe est trop grosse )

    Et puis, votre tableau d'image ... qui est un tableau digne du C ... c'est moche ! (nous sommes en C++ bon sang ... utilisez donc std::list ou std::vector ... cela vous fera un pointeur en moins à géré )
    (En C++, on tente d'utiliser le moins possible les pointeurs :p)

    Est ce que la fonction GetCurrentBitmap est vraiment nécéssaire (du moins, vraiment nécessaire en public ?) Normalement la fonction draw est la seule à devoir connaitre le bitmap actuel.

    Dans votre constructeur de Sprite, utilisez la liste d'initialisation du constructeur au lieu d'utiliser les setters ... ( entre autre -> http://cpp.developpez.com/faq/cpp/in...EUR_liste_init )

    Ah ... le destructeur ne fait rien O_o ... pourtant il y a gestion de ressources ... je pensais qu'il allait peut être libéré les ressources (notamment, depuis qu'elles me semblent chargés dans le constructeur ). Je crois que vous le faites dans clear() ... et si clear n'est pas appelé ? (clear me semble superflu ... nous avons un destructeur )

    J'aurais mis setImage en private ... mais là ... c'est un choix (un choix qui peut faire crasher votre classe si l'utilisateur est stupide ou simplement innatentif (depuis que vous n'utilisez pas ce std::vector )

    La fonction isInclude pouvait être dans la classe Sprite.
    AreCollide aussi mais ça c'est un choix qui se défend

    Voilà ce que j'en pense
    Vous pouvez donc voir le code d'OpenAWars, qui fait les choses différement ( http://code.google.com/p/openawars/s.../Sources/?r=53 )
    Je ne dis pas que je détient la vérité absolue j'ai juste dit ce que j'en pense

  3. #3
    Membre éclairé Avatar de seeme
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    430
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 430
    Points : 791
    Points
    791
    Par défaut
    J'ajouterais à la réponse précédente une manière plus propre de gérer les collisions que ce système de pas (qui selon la forme du sprite peut donner des faux négatifs).

    D'abord regarder si les deux sprites se rencontrent du point de vue de leur hitbox (ici ce serait le rectangle du sprite), ensuite, s'il y a hit entre les hitboxs, regarder si la collision est pixel perfect.

    On réduit de beaucoup les traitements

  4. #4
    Membre averti Avatar de yetimothee
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2007
    Messages
    260
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Maine et Loire (Pays de la Loire)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2007
    Messages : 260
    Points : 364
    Points
    364
    Par défaut
    Merci pour vos suggestions !

    Alors, je prends note.

    En quoi un tableau style C est moche ? Moi j'aime bien le C, et franchement je ne vois pas la différence entre ça et un vector, surtout quand ce tableau ne sera alloué et désinitialisé qu'une seule fois.


    La fonction "GetCurrentBitmap" n'est fondamentalement pas utile. Je verrais à l'enlever.

    Alors, vous m'apprenez un truc sur le constructeur et la liste d'initialisation.

    Le destructeur, oui tiens, pourquoi j'ai utilisé une fonction clear ? Ah oui, j'avais un doute : Lorsque je détruit un objet héritant de la classe sprite, est-ce que le destructeur de sprite sera appelé ?

    La fonction SetImage, une ptite condition en plus et ça se stabilise tout seul.

    Merci encore pour vos renseignement, je vais en tenir compte (surtout pour la classe Sprite, MobileSprite, etc, c'est pas pour le temps que ça va me prendre ^^).

    Sinon, pour ce qui est d e la collision, je répugne à utiliser des boites parce que ça n'est pas précis. Ma méthode reste à mon avis davantage précise. (par contre, additionner les deux... pourquoi pas ?)

    Cela étant dit, j'ai pensé, au chargement du sprite, créer une boite plus fidèle à la réalité, avec pas forcément seulement 4 cotés. Là ça m'amuserait. Mais pour l'instant j'en suis pas là.

    Aller, au boulot !

  5. #5
    Membre éclairé Avatar de seeme
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    430
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 430
    Points : 791
    Points
    791
    Par défaut
    Pour ce qui concerne le destructeur, oui, il sera appellé (CF FAQ).

    Je n'ai jamais dit qu'il fallait se limiter aux hit box... relis mon post.

    Ok, ta fonction est précise.. Je met 10000 sprites dans ta fenêtre, tu vas voir comment ça va ramer..

    L'exemple typique: tu as un sprite en (0, 0) et un en (1024, 1024). LEs sprites font 32x32.
    A chaque frame, tu vas te manger une comparaison sur TOUS les pixels des sprites alors qu'il est impossible qu'il y ai collision...

    C'est pour ça qu'on utilise les hitbox. Si les hitbox se chevauchent, ça ne veut pas dire qu'il y a collision. Ca veut dire qu'il y a probablement collision, et qu'il est nécessaire de tester en pixel perfect.

    C'est aussi pour ça qu'on peut partitionner l'espace. Tu optiens un graph ce qui va te permettre d'éllaguer carrément.. mais bon, ça dépasse ta classe de sprite.

  6. #6
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 928
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 928
    Points : 220 601
    Points
    220 601
    Billets dans le blog
    128
    Par défaut
    Citation Envoyé par yetimothee Voir le message
    En quoi un tableau style C est moche ? Moi j'aime bien le C, et franchement je ne vois pas la différence entre ça et un vector, surtout quand ce tableau ne sera alloué et désinitialisé qu'une seule fois.
    Bah, c'est que gere des pointeurs a la main ... a la place de laisser faire une classe standardise ... je trouve cela du gachis. En C on est un peu plus force, en C++ autant laisse faire les choses (on a deja assez de problemes a gere le reste)

    Citation Envoyé par yetimothee Voir le message
    Alors, vous m'apprenez un truc sur le constructeur et la liste d'initialisation.


    Citation Envoyé par yetimothee Voir le message
    Le destructeur, oui tiens, pourquoi j'ai utilisé une fonction clear ? Ah oui, j'avais un doute : Lorsque je détruit un objet héritant de la classe sprite, est-ce que le destructeur de sprite sera appelé ?
    Que si le destructeur est declare virtuel (voir la FAQ C++)

    Citation Envoyé par yetimothee Voir le message
    La fonction SetImage, une ptite condition en plus et ça se stabilise tout seul.
    Et dans le cas ou l'on ajoute une image et que le tableau est trop petit ... un std::vector, c'est plus simple
    Mais je critiquais aussi le fait que la fonction n'est pas totalement la plus utile (et que l'on peut l'enlever)

    Citation Envoyé par yetimothee Voir le message
    Merci encore pour vos renseignement, je vais en tenir compte (surtout pour la classe Sprite, MobileSprite, etc, c'est pas pour le temps que ça va me prendre ^^).
    De rien

    Citation Envoyé par yetimothee Voir le message
    Sinon, pour ce qui est d e la collision, je répugne à utiliser des boites parce que ça n'est pas précis. Ma méthode reste à mon avis davantage précise. (par contre, additionner les deux... pourquoi pas ?)

    Cela étant dit, j'ai pensé, au chargement du sprite, créer une boite plus fidèle à la réalité, avec pas forcément seulement 4 cotés. Là ça m'amuserait. Mais pour l'instant j'en suis pas là.
    Le mix des deux, cela permet de ne pas perdre trop de temps sur les collisions (choses qui fait perdre enormement de temps)

  7. #7
    Membre averti Avatar de yetimothee
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2007
    Messages
    260
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Maine et Loire (Pays de la Loire)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2007
    Messages : 260
    Points : 364
    Points
    364
    Par défaut
    Je n'ai jamais dit qu'il fallait se limiter aux hit box... relis mon post.
    C'est bien ce que j'avais compris. Le hitbox ne correspondrait pas à cette partie de ma fonction de collision :

    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
     
    // 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;
    Bon, c'est très à l'arrache, mais ça fait déjà une différence.

    Sinon, ce à quoi j'avais songé, c'était de modéliser le sprite dans un tableau de booléen, ou une case correspondrait à un pixel. Je ne sais pas vraiment si c'est plus rapide de lire dans un tableau de booléen plutôt que dans un tableau de pixels. Probablement.

  8. #8
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 928
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 928
    Points : 220 601
    Points
    220 601
    Billets dans le blog
    128
    Par défaut
    Citation Envoyé par yetimothee Voir le message
    Sinon, ce à quoi j'avais songé, c'était de modéliser le sprite dans un tableau de booléen, ou une case correspondrait à un pixel. Je ne sais pas vraiment si c'est plus rapide de lire dans un tableau de booléen plutôt que dans un tableau de pixels. Probablement.
    Vous ne gagnerai absolument rien ...

  9. #9
    Membre averti Avatar de yetimothee
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2007
    Messages
    260
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Maine et Loire (Pays de la Loire)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2007
    Messages : 260
    Points : 364
    Points
    364
    Par défaut
    Ok. Je vais utiliser la méthode des rectangles de collision.

  10. #10
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 928
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 928
    Points : 220 601
    Points
    220 601
    Billets dans le blog
    128
    Par défaut
    Pardon, je precise ma reponse.

    Il n'y a pas de difference, entre une comparaison 32 bits, et une comparaison 8bits.
    De plus, le boolean, il est peut etre code sur 32bits ... donc.

    Apres, on ne gagne pas en memoire non plus, car cela fait utilise un tableau de plus.

  11. #11
    Membre éclairé Avatar de seeme
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    430
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 430
    Points : 791
    Points
    791
    Par défaut
    Citation Envoyé par LittleWhite Voir le message
    Pardon, je precise ma reponse.

    Il n'y a pas de difference, entre une comparaison 32 bits, et une comparaison 8bits.
    De plus, le boolean, il est peut etre code sur 32bits ... donc.

    Apres, on ne gagne pas en memoire non plus, car cela fait utilise un tableau de plus.
    Disons que ça irait un peu plus vite vu que le picking de la couleur (isAlpha ou pas) ne se ferait qu'une fois...

    Mais c'est négligeable face à un système de partitionnement (si vraiment beaucoup d'éléments) et hitbox + pixel perfect..

  12. #12
    Membre averti Avatar de yetimothee
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2007
    Messages
    260
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Maine et Loire (Pays de la Loire)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2007
    Messages : 260
    Points : 364
    Points
    364
    Par défaut
    J'ai fini ma fonction AreCollide en tenant compte de vos suggestions (enfin pas toutes, ça prends du temps et ça chamboule un peu tout de modifier de la sorte ; Je comprends en quoi ce concept de responsabilité est important).

    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
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
     
    bool	
    AreCollide (Sprite *sprite1, Sprite *sprite2)
    {
    	// On teste les bounding boxes des sprites
    	Vecteur <float> p1Rect; // Point supérieur gauche du rectangle de collision
    	Vecteur <float> p2Rect; // Point inférieur droit du rectangle de collision
    	// Boite
    	// Comme le sprite peut subir une rotation, nous devons prendre en compte celle ci :
    	int m, n, l, k;
    	float alpha;
    	// Box sprite 1
    	alpha = sprite1->GetAngle ();
    	l = sqrt (pow (sprite1->GetScale () * al_get_bitmap_height (sprite1->GetCurrentBitmap ()) * sin (alpha), 2));
    	k = sqrt (pow (sprite1->GetScale () * al_get_bitmap_height (sprite1->GetCurrentBitmap ()) * cos (alpha), 2));
    	m = sqrt (pow (sprite1->GetScale () * al_get_bitmap_width (sprite1->GetCurrentBitmap ()) * sin (alpha), 2));
    	n = sqrt (pow (sprite1->GetScale () * al_get_bitmap_width (sprite1->GetCurrentBitmap ()) * cos (alpha), 2));
    	Vecteur <float> box1 (n + l, m + k);
     
    	// Box sprite 2
    	alpha = sprite2->GetAngle ();
    	l = sqrt (pow (sprite2->GetScale () * al_get_bitmap_height (sprite2->GetCurrentBitmap ()) * sin (alpha), 2));
    	k = sqrt (pow (sprite2->GetScale () * al_get_bitmap_height (sprite2->GetCurrentBitmap ()) * cos (alpha), 2));
    	m = sqrt (pow (sprite2->GetScale () * al_get_bitmap_width (sprite2->GetCurrentBitmap ()) * sin (alpha), 2));
    	n = sqrt (pow (sprite2->GetScale () * al_get_bitmap_width (sprite2->GetCurrentBitmap ()) * cos (alpha), 2));
    	Vecteur <float> box2 (n + l, m + k);
    	// Il s'agit de déterminer les point du rectangle de collision
    	// Sur l'axe des abscisses
    	if (sprite1->GetPos () [cX] > sprite2->GetPos () [cX])
    	  {
    		if (sprite1->GetPos () [cX] > sprite2->GetPos () [cX] + box2 [cX])
    		  {
    			return false;
    		  }
    		else
    		  {
    			// p1x > p3x & p1x < p4x
    			p1Rect [cX] = sprite1->GetPos () [cX];
    			if (sprite1->GetPos () [cX] + box1 [cX] > sprite2->GetPos () [cX] + box2 [cX])
    			  {
    				// p1x > p3x & p2x < p4x
    				p2Rect [cX] = sprite1->GetPos () [cX] + box1 [cX];
    			  }
    			else
    			  {
    				// p1x > p3x & p2x > p4x
    				p2Rect [cX] = sprite2->GetPos () [cX] + box2 [cX];
    			  }
    		  }
    	  }
    	else
    	  {
    		if (sprite1->GetPos () [cX] + box1 [cX] > sprite2->GetPos () [cX])
    		  {
    			// p1x < p3x && p2x > p3x
    			p1Rect [cX] = sprite2->GetPos () [cX];
    			if (sprite1->GetPos () [cX] + box1 [cX] > sprite2->GetPos () [cX] + box2 [cX])
    			  {
    				// p1x < p3x & p2x > p3x & p2x > p4x
    				p2Rect [cX] = sprite2->GetPos () [cX] + box2 [cX];
    			  }
    			else
    			  {
    				// p1x < p3x & p2x > p3x & p2x < p4x
    				p2Rect [cX] = sprite1->GetPos () [cX] + box1 [cX];
    			  }
    		  }
    		else
    		  {
    			// p1x < p3x & p2x < p3x
    			return false;
    		  }
    	  }
     
    	// sur l'axe des ordonnées
    	if (sprite1->GetPos () [cY] > sprite2->GetPos () [cY])
    	  {
    		if (sprite1->GetPos () [cY] > sprite2->GetPos () [cY] + box2 [cY])
    		  {
    			return false;
    		  }
    		else
    		  {
    			// p1y > p3x & p1y < p4y
    			p1Rect [cY] = sprite1->GetPos () [cY];
    			if (sprite1->GetPos () [cY] + box1 [cY] > sprite2->GetPos () [cY] + box2 [cY])
    			  {
    				// p1y > p3y & p2y < p4y
    				p2Rect [cY] = sprite1->GetPos () [cY] + box1 [cY];
    			  }
    			else
    			  {
    				// p1y > p3y & p2y > p4y
    				p2Rect [cY] = sprite2->GetPos () [cY] + box2 [cY];
    			  }
    		  }
    	  }
    	else
    	  {
    		if (sprite1->GetPos () [cY] + box1 [cY] > sprite2->GetPos () [cY])
    		  {
    			// p1y < p3y && p2y > p3y
    			p1Rect [cY] = sprite2->GetPos () [cY];
    			if (sprite1->GetPos () [cY] + box1 [cY] > sprite2->GetPos () [cY] + box2 [cY])
    			  {
    				// p1y < p3y & p2y > p3y & p2y > p4y
    				p2Rect [cY] = sprite2->GetPos () [cY] + box2 [cY];
    			  }
    			else
    			  {
    				// p1y < p3y & p2y > p3y & p2y < p4y
    				p2Rect [cY] = sprite1->GetPos () [cY] + box1 [cY];
    			  }
    		  }
    		else
    		  {
    			// p1y < p3y & p2y < p3y
    			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 (255, 0, 255));
    	sprite1->Draw (0, 0);
    	// Sprite 2
    	al_set_target_bitmap (bmp2);
    	al_clear_to_color (al_map_rgb (255, 0, 255));
    	sprite2->Draw (0, 0);
    	// Remise par défaut du bitmap de destination
    	al_set_target_bitmap (target);
     
    	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 = p1Rect [cX]; i < p2Rect [cX]; i+=2)
    	  {
    		for (int j = p1Rect [cY]; j < p2Rect [cY]; j+=2)
    		  {
    			// Si le point donné n'est pas magic pink sur le premier sprite
    			if (!IsTransparent (al_get_pixel (bmp1, i - sprite1->GetPos () [cX], j - sprite1->GetPos () [cY])))
    			  {
    				// Si le point donné n'est pas de la couleur magic pink sur le second sprite
    				if (!IsTransparent (al_get_pixel (bmp2, i - sprite2->GetPos () [cX], j - sprite2->GetPos () [cY])))
    				  {
    					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;
    }
    Alors, je ne suis pas sûr de la véracité mathématique de tout cela, mais je dois dire que le rendu est très satisfaisant, pour l'instant.
    Cela étant dit, l'ordi (avec 25% de mon phenom II qui est assigné au programme) rame un peu lorsque deux sprites se chevauchent, parfois.
    Je me demande si ça ne serait pas dû à mes collisions des boites...
    Enfin voilà, j'vais prendre un peu de repos et re-regarder cela avec un peu plus de recul.

    Encore merci à vous deux, dans tout les cas

Discussions similaires

  1. Classe Sprite, une par programme ou une par image?
    Par anto38fr dans le forum XNA/Monogame
    Réponses: 7
    Dernier message: 04/10/2014, 17h02
  2. collision entre un sprite d'une classe et un vector2d liste
    Par kate59 dans le forum Développement 2D, 3D et Jeux
    Réponses: 7
    Dernier message: 22/04/2008, 00h10
  3. Une class Sprite
    Par FabaCoeur dans le forum SDL
    Réponses: 11
    Dernier message: 28/04/2007, 19h06
  4. Variable d'une Classe Ancêtre
    Par Génie dans le forum Langage
    Réponses: 3
    Dernier message: 18/09/2002, 20h24
  5. Sortir un typedef d'une classe
    Par Theophil dans le forum C++Builder
    Réponses: 13
    Dernier message: 03/07/2002, 18h21

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo