cible(LaraCroft)

Transcription

cible(LaraCroft)
Camera, C++
un peu de Structure avec le polymorphisme et un coup d’héritage
par Kitone
02/06/02
Le but de ce papier et de d’écrire une méthode pour gérer les Cameras ou tout autre objet,
surtout du point de vue code C++, parce que j’ai reçu des mails ou dans le forum des questions
sur ce sujet. Beaucoup on de mal a structurer leur prog., s’en sortir avec les class de bases, et
finissent souvent par tenir plusieurs list (ou tableau) de chaque type de Camera (libre,
cible..), de chaque type de Light (spot, fixe,…), donc j’ espère que ce papier va les éclaire.
Ici je donne une certaine méthode, elle convient dans bien des cas, mais il existe beaucoup de
façon d’atteindre un même but. Bien sur, j’utilise le C++ et je prône l’OpenGL :) .
Comment structuré sont moteur3D ?
Je vais prendre comme exemple une classe Camera, comment la représenté.
Avant il faut la penser un peu, que voulons-nous ?
Plusieurs types de Camera :
• Libre (doom, quake,…)
• Cible (LaraCroft, MétalGearSolid, Zelda,…)
• Fixe (Résidant Evil :)
• etc. …
Alors on va parler un peu de ces Camera.
Camera Libre
Ce sont les cameras utilisé dans les jeux de types quake, doom, les jeux a la première
personne, vous voyez directement dans les yeux du personnage.
Camera Cible
C’est une Camera qui suit le personnage, un objet, une voiture, …le jeux LaraCroft (Mario,…)
l’utilise, elle ne fait que suivre une entité.
Camera Fixe
En faite ce sont des Camera…que vous placer dans certain endroit, elle ne bouge plus. Le jeu
ResidentEvil l’utilise. Elle permet d’avoir des angle de vue défini.
Bien sur vous pouvez en crée d’autre, selon le besoin.
Alors quel est le point comment de toutes ces Camera ?
Bein.. Ce sont des Camera :)
Mais encore, du point de vue code :
Elle on d’autre point commun, une position, cible,…donc on peu dire une Matrice. Il est
possible de les modifiées (Rotation, Translation, Angle de vue, frustum…), elle peut être
identifier par un id (unsigned int) , un nom (ex : « Camera Chambre » )
Alors le but est de ne pas réécrire plusieurs fois les mêmes choses, utilise l’héritage et la
puissance du C++ pour nous facilitez la vie.
On a dit qu’ une Camera c’est un identifiant, une matrice et quelques fonctions que je ne vais
pas d’écrire ici.
Mais aussi, un mesh peu être représenter comme ça, c une matrice, un identifient unique, un
nom,…et des truc qui lui sont propre. Donc on peu faire ça d’un coup.
/* ici on initi avec SetType(int type */
enum TypeObjet
{
OBJET,
/* class de base */
CAMERA,
CAMERA_CIBLE,
CAMERA_LIBRE,
CAMERA_FIXE,
LIGHT,
LIGTH_SPOT,
LIGTH_FIXE,
//LIGHT…
MESH,
//...
};
class Objet
{
public :
/* constructeur & destructeur */
Objet();
virtual
~Objet();
/* dessine l’objet, fonction virtuel pure */
virtual
void Draw() { /* vide */ };
/* load la matrice */
virtual
void Put();
/* gestion de l’id et du nom et du type */
void
SetName(char *name);
char*
GetName();
void
UINT
void
int
//protected :
Matrix
Char
UINT
int
};
SetID(unsigned int id);
GetID();
SetType(int type);
GetType();
matrix;
Name[MAX_NAME];
id;
Type;
Bon voilà globalement ce qu’il faut, on a le constructeur et destructeur, je revient sur
ce dernier car il est virtual, pourquoi ? quand on va faire hériter les autres objets de cette class
et si on tien les objets dans un tableau de type Objet, alors le fait de détruire l’objet ne va pas
obligatoirement détruire les class qui en hérite (Camera,…) donc le fait de le mettre virtual va
appeler les destructeur des class supérieur et les détruire, mais nous en reparlerons plus bas,
avec les pointeurs sur les class de bases :o)
Ensuite on peu voir des fonctions pour la gestion du nom et de l’id, rien de bien
méchant, après la ou ça devient intéressant c’est la fonction SetType() et GetType(), a quoi
peuvent t’elle bien servir ? Prenons le cas de la list (ou tableau) sur les pointeurs de class de
base, comme on ne gère que des pointeurs de type Objet, comment savoir qu’elle est
réellement la class, une Camera ou une Light ,… ? Simplement en interrogent le la valeur
Type.
Je rappelle un p’tit truc, comment avoir un pointeur de class de base ?
Objet *ptrbase = new Camera ;
Ici ptrbase pointe sur la class de base Objet (de Camera puisqu’elle en hérite)
Bon la suite, la class Camera
Alors on ne va pas crée 3 type de Camera héritant de Objet, non on va faire un type de base
Camera qui hérite de Objet (pour la gestion des noms, id,… et pour la matrice,…) puis on va
faire 3 types (Libre, Cible, Fixe) qui vont dériver de Camera.
class Camera : public Objet
{
public :
Camera();
virtual
~Camera();
/* on peu dessiner en mode débug, sinon place la Cam */
virtual
void
Draw() ;
/* place la camera, donc ça matrice */
virtual
void
Put ();
/* permet de donner une cible, c abstrait */
virtual
void
SetCible(Objet *o);
/* modifie la place de la camera */
virtual
void
Translate(Vector v);
virtual
void
Rotate(int axe, float v);
protected:
Objet *cible;
/* la cible */
};
Bon ici, la fonctions Draw() n’a rien de spécial, elle dessine simplement une camera, ça peu
être utile en mode débug, ensuite Put() fait un glLoadMatrix(matrix.Get()) (pour OpenGL)
Les fonctions Translate et Rotate sont simple, mais c’est pour une camera classique, parce
que une camera cible ne tourne pas de la même manière qu’une camera Libre.
Quel est la différence entre les 2 ?
L’axe de la camera, l’un est situe au niveau
de la camera, donc quand on tourne, ça
fait…comme une camera « normal » (style
quake) on peu voir l’objet en rouge est
l’axe de la camera a ça base.
Maintenant pour la Camera Cible l’axe est
celui de l’objet, donc les rotations ce font
autour de cette axe, un peu comme une
planète tourne autour du soleil. Donc
quand l’objet bouge, la camera aussi, mais
elle a ça propre matrice ce qui permet de la
faire tourner sans avoir besoin de bouger
l’objet. (regarder la fonction gluLookAt, un
peu du même type)
Le SetCible, permet de définir une cible (on aurai pu le placer dans Objet mais je le mais ici
pour que ce soit plus clair )
void
{
Camera::SetCible(Objet *o)
cible=o ;
}
donc a quoi ça peu bien servir ?
pour qu’une camera suive un objet, elle doit avoir la position de l’objet, donc ça matrice, alors
on met un pointeur sur l’objet cible.
Pourquoi un Pointeur de type Objet ?
Simplement parce qu’avec un pointeur sur la class de base on peu suivre n’importe quel objet,
une lumière, un mesh, un autre camera,…tout ce qui hérite de la class Objet.
Pourquoi mettre cible ici et pas dans Camera Cible ?
Il est vrai que pour la Camera Libre, on n'a pas besoin Cible ni pour Camera Fixe (quoiqu’on
peu définir une cible avec un objet invisible, c’est plus facile a ciblé).
(avant une remarque, il est mieux de mettre *Cible dans Objet, parce que un objet peu
suivrent un autre, imaginer un Spot qui suit une cible (comme en prisions quand on s’évade),
mais je veut rester clair ) Simplement pour ne pas être obliger de transformer mon pointeur
de class de base en class CameraCible, pour pouvoir le modifier directement de Camera.
Donc je ne vous fait pas la class CameraCible, ni CameraFixe et CameraLibre mais je vais
juste dire que les fonctions Rotate() et Translate() de CameraCible ne ce calcul pas de la
même manière que pour une CameraCible, il faut prendre en compte la matrice de l’objet
ciblé, mais ce n’est pas le but de cette article donc pas de code.
Comment géré tous ça ?
Certain aurai fait une list CameraFixe, CameraCible,…ça devient très vite dur a géré et un
peu…bête :) Donc on ne va pas crée 3 list (ou tableau)mais juste une qui va pointer sur les
class la Base Camera. C’est ici que je veux en venir, le but étant d’avoir une seul list qui
contient tous les types de Camera.
On crée une list (j’utilise la STL)
list<*Camera>
list_Camera;
typedef list<*Camera>::iterator
iterCam;
Ensuite il suffit de donner à cette list le pointeur de la class de base.
/* crée une camera Libre */
Camera *cameralibre = new CameraLibre() ;
List_Camera.push_back(cameralibre) ;
/* crée une camera Cible */
Camera *cameracible = new CameraCible() ;
List_Camera.push_back(cameracible) ;
/* crée une camera Fixe */
Camera *camerafixe = new CameraFixe() ;
List_Camera.push_back(camerafixe) ;
Ensuite pour dessiner toutes les Camera, il suffit de faire une boucle dans la fonction Display.
for(iterCam cam = list_Camera.begin() ; cam !=
{
(*cam)->Draw();
}
list_Camera.end(); ++cam)
Avec ça, ça dessine automatiquement le bon type de Camera (pour peu que vous ayez fait des
dessin différent pour chaque type de Camera).
Maintenant le mieux c’est d’avoir une class qui gère toutes les cameras, et qui ne fait que ça,
une CameraSystem. Donc elle fera quoi ?
Elle aura la List des Camera, s’occupera de gère les param de retourner un pointeur d’une
Camera par son Nom ou son Id…Aussi elle devra avoir un pointeur sur la Camera en cour
(current). Parce que dans tous les Cas, il n’y a qu’une camera qui peu être actuellement la
camera par rapport a laquel vous voyer la scène. Aussi si vous fait un system de portal,
octrée,…il faut avoir une camera pour calculer le frustum, clipping,…donc on le fait avec la
camera Active. (on peu aussi faire pur que la class CameraSystem crée une camera par défaut)
Ainsi en début de votre fonction Display il faut placer la Camera.
void Display()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(0,0,0,1);
/* put fait un glLoadMatrix() */
CameraSystem->GetCurrent()->Put();
/* Ensuite vous pouvez dessiner toutes les cameras */
iterCam cam = list_Camera.begin() ;
itercam end = list_Camera.end();
for( ; cam !=end; ++cam)
{
(*cam)->Draw();
}
/* dessiner vos objet, la scène,...
* imaginons un system de portal, vous avez besoin de la camera
* pour calculer le clipping
*/
PortalSystem->SetFrustm(CameraSystem->GetCurrent());
PortalSystem->Draw();
SwapBuffer() ;
}
Comment récupérer un objet et pouvoir le traiter avec ça vrai class et pas ça class de base ?
Il suffit de le convertire. Prenons le cas d’une class CameraLibre.
Ici, on utilise une fonction qui retourne une Camera selon l’id de la camera, ensuite il faut
convertire le pointeur en CameraLibre, comme vous le faite pour les types prédéfini (int*)0.1 .
CameraLibre *cam = (CameraLibre*)CameraSystem->GetCamera(12);
Le mieux ce de vérifier si la conversion est valide. Parce que cette inst ruction fonctionne
toujours ! donc si 12 est en réalité une class CameraFixe, bein…c’est pas bon. On peu utilisé
l’opérateur dynamic_cast qui vérifie si la conversion est valide.
CameraLibre *cam =dynamic_cast<CameraLibre*>(CameraSystem->getCamera(12)) ;
Bon moi je l’utilise pas trop, il suffit simplement de suivre certain règle pour éviter les erreurs.
On peu aussi faire des p’tit fonctions qui teste si le type est valide a l’aide de la fonction
Objet ::GetType() .
CameraLibre* CAMERE_LIBRE_TYPE(Camera *cam)
{
if(cam->GetType()==CAMERA_LIBRE)
return (CameraLibre*) cam;
//printf(“mauvais type\n”);
return NULL ;
}
On peu faire pareil pour tous les autres types. Ensuite on utilise cette fonction. L’avantage c
qu’on peu faire des testes, écrire un truc, dire quel et le type de la cam 12,…tout dépend des
cas !
CameraLibre *cam=CAMERA_LIBRE_TYPE(CameraSystem->GetCamera(12)) ;
if(cam==NULL) return;
conclusion
hum…rien, juste de quoi rendre vos class plus clair…enfin faut espérer :)
[email protected] www.glInFrench.fr.st

Documents pareils