Posté le 27/11/2019 15:12
Planète Casio v4.3 © créé par Neuronix et Muelsaco 2004 - 2024 | Il y a 160 connectés | Nous contacter | Qui sommes-nous ? | Licences et remerciements
Planète Casio est un site communautaire non affilié à Casio. Toute reproduction de Planète Casio, même partielle, est interdite.
Les programmes et autres publications présentes sur Planète Casio restent la propriété de leurs auteurs et peuvent être soumis à des licences ou copyrights.
CASIO est une marque déposée par CASIO Computer Co., Ltd
Citer : Posté le 07/01/2021 21:25 | #
Tu peux toujours remplacer le dynamic_cast par un cast C sur le pointeur :
Ta liste contient des pointeurs dont tu ne pouvais pas obtenir un Cat même dans un environnement C++ complet, c'est bien un Cat * que tu aurais eu.
Citer : Posté le 07/01/2021 21:41 | #
Ha oui, ça fonctionne. Merci
Ajouté le 08/01/2021 à 15:05 :
Je suis face à un autre problème qui est que deux class s'appellent entre elle et que vu que l'une est initialisé avant l'autre ça n'arrive pas à compiler
Exemple:
class SceneManager
{
public:
SceneManager(int* LstSceneInBuild);
GameObject* AllElem;#ici GameObject n'est pas encore Initialisé
int AllElemLength;
};
class GameObject:
{
public:
GameObject(SceneManager* scene);
SceneManager* Scene;
};
Albert Einstein
Citer : Posté le 08/01/2021 15:06 | #
Tu peux écrire class GameObject; tout en haut du fichier pour annoncer que la classe existe mais est définie plus tard.
Une classe qui est annoncée de cette façon ne peut pas être utilisée n'importe comment, mais en gros tant que tu n'as que des pointeurs de son type c'est safe.
Citer : Posté le 08/01/2021 15:08 | #
D'accord, merci
Ajouté le 10/01/2021 à 17:15 :
Bonjour, je voulais savoir s'il y avait moyen d'override une fonction d'une class avec un header ?
Parce que ça me met une erreur de type ' Expected a ";" '
Albert Einstein
Citer : Posté le 10/01/2021 17:23 | #
Je soupçonne que ce que tu veux faire n'a aucun sens... mais pour en juger il faut voir le code. Ça ne sert à rien de rapporter une erreur de syntaxe sans montrer la syntaxe !
Citer : Posté le 10/01/2021 17:53 | #
Ce je jeux veut faire est de regrouper les "Components" dans une liste donc je créer une class component :
class Component {
public:
void OnStart() {};
void OnUpdate() {};
GameObject* gameObject;
int classType;
};
et du coup chaque component est représenté par une autre class qui hérite de Component
class CharacterController : public Component
{
public:
CharacterController();
void OnStart();
void OnUpdate();
};
et j'aimerai que OnStart et OnUpdate de CharacterController remplace ceux de Component pour pouvoir faire ceci
CharacterController c0_14_CharacterController1;
Component* BoxCompo0_14[] ={ &c0_14_CharacterController1,};
GameObject Box_0_14(this,Box1,123,-8,28,24,"Box","Default",BoxCompo0_14,2);
GameObject* AllElem[1] = {&Box_0_14};
int AllElemLength =1;
for (int i(0); i < AllElemLength; ++i)
{
for (int j(0); j < AllElem[i].AllCompoLength; ++j)
{
AllElem[i].AllCompo[j].OnUpdate();
}
}
Ajouté le 10/01/2021 à 17:55 :
Au lieux de devoir faire un cast avec plein de condition
Albert Einstein
Citer : Posté le 10/01/2021 18:02 | #
Ce dont tu as besoin s'appelle des méthodes virtuelles. Je ne sais pas si le SDK les supporte, mais si oui ça se passe comme ça.
Le principe d'une méthode virtuelle est qu'elle est résolue à l'exécution. C'est-à-dire que tu peux appeler OnUpdate() sur un Component * sans savoir si c'est un Component ou un CharacterController. L'objet lui-même ira chercher sa propre méthode OnUpdate(), qui sera CharacterController::OnUpdate() si l'objet est un CharacterController, et Component::OnUpdate() sinon.
Ça se passe comme ça. D'abord tu déclares les méthodes de base comme virtuelles (pas besoin de point-virgule après les accolades définissant la méthode) :
public:
virtual void OnStart() {}
virtual void OnUpdate() {}
GameObject* gameObject;
int classType;
};
Ensuite dans la classe héritée tu remets virtual :
public:
CharacterController();
virtual void OnStart();
virtual void OnUpdate();
};
En C++ moderne on écrirait override à la fin au lieu de virtual au début... pour diverses raisons qui font que c'est plus pratique. Mais le SDK ne connaîtra pas j'en suis sûr, donc on va rester avec la façon à l'ancienne.
Ensuite quand tu codes le truc tu mets aussi virtual.
/* ... */
}
Et enfin, et c'est très important, tu dois utiliser un pointeur ou une référence pour accéder à l'objet. Donc un Component * ou un Component &, et surtout pas un Component. La raison c'est que si tu assignes un CharacterController à un Component la copie va supprimer les informations spécifiques au CharacterController et ne te laisser que la partie de base, ce qui n'est vraiment pas ce que tu veux.
/* Appelle Component::OnUpdate */
c1->OnUpdate();
Component *c2 = new CharacterController();
/* Appelle CharacterController::OnUpdate */
c2->OnUpdate();
Note que ton code d'origine n'utilise pas des pointeurs :
Tu dois absolument faire un tableau de pointeurs de composants (du genre Component **AllCompo) et écrire à la fin :
Citer : Posté le 10/01/2021 18:28 | #
D'accord je viens de comprendre donc du coup, supposons qu'on souhaite faire une liste de GameObject qui Contient une liste de Component il faudrait ça du coup :
ClassParticule.h :
class GameObject;
class Component {
public:
virtual void OnStart() {}
virtual void OnUpdate() {}
GameObject* gameObject;
int classType;
};
class GameObject
{
public:
GameObject(const unsigned char* init_Img, int init_x, int init_y, int init_w, int init_h, const char* name, const char* type, Component* Compo[], int CompoLength);
unsigned char* Img;
char* Type;
char* Name;
int Static;
int TrfmImgScale;
int rotation;
int x;
int y;
int w;
int h;
SceneManager* Scene;
int AllCompoLength;
Component* AllCompo;
};
class CharacterController : public Component {
public:
CharacterController();
virtual void OnStart();
virtual void OnUpdate();
};
ClassParticule.cpp :
GameObject::GameObject(const unsigned char* init_Img, int init_x, int init_y, int init_w, int init_h, const char* name, const char* type, Component* Compo[], int CompoLength)
{
Img = (unsigned char*)init_Img;
Name = (char*)name;
Type = (char*)type;
Static = 1;
TrfmImgScale = 100;
rotation = 90;
x = init_x;
y = init_y;
w = init_w;
h = init_h;
AllCompoLength = CompoLength;
AllCompo = (Component*)Compo;
}
virtual void CharacterController::OnUpdate() {
/* ... */
}
Code principale :
void CreateScene(){
Component *compoCharacterController0 = new CharacterController();
Component* Default11Compo[] ={ &compoCharacterController0};
GameObject Obj_Default11(Image,152,49,16,16,"Default11","Default",Default11Compo,1);
return Obj_Default11;
}
int main()
{
GameObject* AllElem = CreateScene();
for (int j(0); j < scene.AllElem[i]->AllCompoLength; ++j)
{
AllElem[0]->AllCompo[j]->OnUpdate();
}
}
est-ce que le code est bon ou j'ai fait des fautes avec les pointeurs ?
Albert Einstein
Citer : Posté le 10/01/2021 18:37 | #
Dans GameObject, tu ne peux pas avoir un tableau (pointeur) de Component :
Il te faut un tableau (pointeur) de pointeurs :
Et d'ailleurs c'est ce que tu prends en paramètre (ici Component *[] est identique à Component **) :
Et donc tu ne castes pas.
CharacterController * compoCharacterController0;
Component* Default11Compo[] ={ &compoCharacterController0};
GameObject Obj_Default11(Image,152,49,16,16,"Default11","Default",Default11Compo,1);
return Obj_Default11;
}
Ça c'est faux pour une raison subtile. Tu vois compoCharacterController0, c'est un pointeur. Il fait 4 octets. Et ces 4 octets sont stockés sur la pile. Ce qui est stocké sur la pile disparaît lorsque tu quittes la fonction, donc il est hors de question de mettre &compoCharacterController0 (qui pointe donc vers la pile) dans ta classe, sachant que les données qui sont dans la pile vont changer dès que tu vas faire le return.
Tu es obligé de faire une vraie allocation :
Default11Compo[0] = compoCharacterController0;
Comme ça, les 4 octets de compoCharacterController0 sont copiés de la pile (où ils n'existeront bientôt plus) vers le tas (puisque c'est là que new alloue). BIen sûr faut delete après.
Et ensuite divers problèmes de types :
Tu renvoies un GameObject, ça ne peut donc pas être void.
Tu renvoies un GameObject, ça ne peut donc pas être GameObject *.
De façon générale c'est bien plus naturel de tout manipuler via pointeur :
/* ... */
GameObject *ObjDefault11 = new GameObject(Image, ..., 1);
return ObjDefault11;
Mais bien sûr faut delete après.
Citer : Posté le 10/01/2021 18:49 | #
D'accord merci pour tout ces explications, je ferai toute le modifications.
Je veux juste revenir sur GameObject *CreateScene(), si je voulais faire plusieurs component de plusieurs GameObject je fais ça par conséquent :
GameObject *CreateScene() {
Component *c1 = new CharacterController();
Component *c2 = new CharacterController();
Component **Default11Compo = new Component *[2];
Default11Compo[0] = c1;
Default11Compo[1] = c2;
GameObject *ObjDefault11 = new GameObject(Image, ..., 1)
Component *c3 = new CharacterController();
Component *c4 = new CharacterController();
Component **Default12Compo = new Component *[2];
Default12Compo[0] = c3;
Default12Compo[1] = c4;
GameObject *ObjDefault12 = new GameObject(Image, ..., 1)
GameObject * AllElem[2] ={&ObjDefault11,&ObjDefault12}
return AllElem;
}
int main {
GameObject* AllElem = CreateScene();
/*....*/
}
Du coup la on est d'accord que ça fait bien une liste de GameObject de liste de Component ?
Albert Einstein
Citer : Posté le 10/01/2021 21:10 | #
Tu y es presque ! Tu t'en sors ben sur toute la ligne et ensuite tu chokes à la toute dernière étape !
return AllElem;
AllElem est un tableau de 2 pointeurs, il occupe 8 octets dans la pile. Quand tu fais return AllElem, implicitement ça retourne l'adresse du tableau... dans la pile. Ces 8 octets sont pourtant sur le point de disparaître !
Tu peux soit allouer AllElem avec new, soit renvoyer une structure ou un objet avec deux champs... à toi de voir comment tu peux les grouper de façon raisonnable.
Par ailleurs AllElem est de type GameObject *[2] ce qui est similaire à GameObject ** (un tableau c'est sensiblement équivalent à un niveau de pointeur). Donc si tu fais ça la valeur de retour de CreateScene() et le type de AllElem dans main() doivent être GameObject **.
Et enfin, tu as un peu bâclé la définition de main(). xD
Citer : Posté le 11/01/2021 00:00 | #
Désolé c'est un peu nouveau pour moi les pointeur
Donc si j'ai bien compris je dois mettre autant "*" qu'il y a d'élément dans la liste ?
mais ça veut dire que si j'ai 500 éléments par exemple je dois faire 500 étoiles ?
ou je peux juste faire GameObject * liste[500] = {&elem0,...,&elem499}
Albert Einstein
Citer : Posté le 11/01/2021 00:16 | #
Non, surtout pas. Il faut compter ici les « indirections », c'est-à-dire les étapes intermédiaires entre ta variable et la donnée finale.
Si tu crées un objet directement, il sera de type GameObject. C'est le cas normal.
Si tu alloues un objet dans le tas avec new GameObject(), ce que tu obtiens en retour ce n'est pas l'objet. C'est simplement l'adresse dans le tas où l'objet a été créé (on dira : un pointeur vers l'objet créé). Il y a donc une étape de trajet, en partant de la valeur que tu as reçue, pour atteindre le nouvel objet. La valeur retournée par new est donc de type GameObject * (un « pointeur vers un GameObject »).
De même, si tu crées un tableau de GameObject, pour diverses raisons le tableau peut être vu comme un pointeur vers le premier élément (et comme les éléments se suivent dans la mémoire ça te suffit pour tous les trouver). Pour accéder à un élément à partir de l'adresse du groupe, il faut là aussi faire une étape de trajet. Donc un tableau de GameObject (dont le type est GameObject[10] s'il a 10 éléments par exemple) se comporte d'une façon proche d'un GameObject *.
En assimilant ainsi les tableaux à des pointeurs, en partant de ta variable, tu comptes le nombre d'indirections nécessaires pour atteindre la donnée, et ça c'est le niveau de pointeur (ie. le nombre d'étoiles).
AllElem est un tableau, c'est donc un premier niveau. Lorsque tu accèdes à un élément de AllElem, tu trouves un pointeur (par exemple &ObjDefault11). Ce pointeur est un second niveau. Lorsque tu voyages vers la cible du pointeur (on dit « déréférencer » le pointeur), tu atteins ton objet final qui est un GameObject. AllElem a donc deux indirections, ce qui correspond à GameObject **.
Tu peux le lire dans l'autre sens. Ton objet final est un GameObject. &ObjDefault11 est un pointeur qui y mène, c'est donc un GameObject *. AllElem est un tableau de variables comme &ObjDefault11, c'est donc un tableau de GameObject *. La notation rigoureuse est GameObject *[2], mais comme un tableau dont les éléments sont de type T (T[]) se comporte d'une façon proche d'un pointeur vers un objet de type T (T*), tu peux changer ce [2] en étoile et dire que AllElem est un GameObject **.
Dans les deux cas tu arrives au même résultat.
Ça c'est correct aussi, puisque tu as une première indirection dans le tableau (le [500]) puis une autre indirection une fois que tu as été lire l'élément du tableau, qui est pointeur (le *). Là encore le tableau de 500 GameObject * se comporte comme un pointeur vers un GameObject *, c'est-à-dire un GameObject **.
Citer : Posté le 11/01/2021 00:34 | #
Haaaaaaaaaaaaaaaaaa, ok je viens de comprendre, enfin je crois.
Donc si j'ai bien compris je dois mettre autant d'étoile qu'il a de chemin vers des pointer.
Genre par exemple j'ai ça : pointer->pointer->pointer je dois donc mettre 3 étoile vu qu'il y a 3 pointer donc si je reprend l'exemple d'avant ça donne ça :
GameObject ** CreateScene() {
Component *c_0_0 = new CharacterController();
Component *c_0_1 = new CharacterController();
Component **Default0Compo = new Component *[2];
Default0Compo[0] = c_0_0;
Default0Compo[1] = c_0_1;
GameObject *ObjDefault0 = new GameObject(Image, ..., 1)
/*.....*/
Component *c_499_0 = new CharacterController();
Component *c_499_1 = new CharacterController();
Component **Default499Compo = new Component *[2];
Default499Compo[0] = c_499_0;
Default499Compo[1] = c_499_1;
GameObject *ObjDefault12 = new GameObject(Image, ..., 1)
GameObject * AllElem[500] ={&ObjDefault0,...,&ObjDefault499};
return AllElem;
}
int main {
GameObject ** AllElem = CreateScene();
//exemple d'utilisation :
AllElem[0].AllCompo[0]->OnUpdate();
}
Et là normalement c'est bon vu que j'ai mis les 2 étoiles dans main
Albert Einstein
Citer : Posté le 11/01/2021 07:47 | #
Voilà là les types sont bons. Par contre tu as toujours le même problème d'allocation :
return AllElem;
AllElem est un tableau de 500 pointeurs tous stockés dans la pile (2000 octets dans la pile), et ces 2000 octets n'existeront plus quand tu quitteras CreateScene(). Lorsque tu fais return AllElem, tu ne copies pas les 500 pointeurs, tu renvoies simplement leur adresse dans la pile. Le programme ne marchera pas parce que tes 500 pointeurs (&ObjDefaultX) seront écrasés par la première fonction que tu vas appeler après CreateScene(), qui stockera ses variables locales dessus.
Citer : Posté le 11/01/2021 11:53 | #
Du coup pour palier à ce problème il faudrait remplacer ces deux lignes par quoi ?
Je pense avoir la réponse que serait :
GameObject **AllElem = new GameObject *[500];
AllElem[0] = ObjDefault0;
....
AllElem[499] = ObjDefault499;
return AllElem;
Mais ça me parait assez long ça faire comme ça
Albert Einstein
Citer : Posté le 11/01/2021 12:20 | #
Voilà allouer dans le tas est la méthode simple. Tu peux aussi écrire
mais c'est plus moderne donc je sais pas si le SDK en voudra.
Tu peux aussi renvoyer une structure ou une classe.
Btw j'ai aucune idée de ce que tu fais ici vis-à-vis du moteur de jeu, je vois vraiment pas pourquoi tu créerais 500 objets comme ça, donc pense à valider que c'est la bonne approche. ^^"
Citer : Posté le 11/01/2021 13:16 | #
Btw j'ai aucune idée de ce que tu fais ici vis-à-vis du moteur de jeu, je vois vraiment pas pourquoi tu créerais 500 objets comme ça, donc pense à valider que c'est la bonne approche. ^^"
Bah je détaillerai quand j'aurai publié mon script, mais je pense que ce je compte faire est adapter pour ce que je veux.
Mais maintenant supposons que je veux supprimer tout les éléments c'est à dire Component et GameObject pour libérer de la mémoire il faut que je fasse ça ? :
for (int i(0); i < AllElemLength; ++i)
{
for (int j(0); j < AllElem[i].AllCompoLength; ++j)
{
delete AllElem[i]->AllCompo[j];
}
delete AllElem[i];
}
Albert Einstein
Citer : Posté le 11/01/2021 13:22 | #
You can make a destructor for GameObject that frees the AllCompo, then simply do delete[] AllElem.
Citer : Posté le 11/01/2021 13:24 | #
Oui mais est-ce que ça efface aussi la liste de Component le fait de détruire las liste de GameObject ?
Albert Einstein
Citer : Posté le 11/01/2021 13:27 | #
Nevermind, you have an array of pointers - delete[] only works for arrays of objects.