Game Develop 2.0.10498 SDK

Le SDK est maintenant accessible depuis la page de téléchargement de Game Develop sur le site : compilgames.net/index.php?file=kop18.php

Pour ceux qui ont utilisés les versions précédentes, il vous faut mettre à jour toutes les bibliothèques et le compilateur.

Changements importants par rapport aux versions précédentes au niveau du code :
-Les bibliothèques sont maintenant à placer dans le dossier ExtLibs du SDK.

-La déclaration des extensions a un peu changé, notamment les paramètres. Prenez exemple sur les extensions existantes, j’ai remplacé certaines macro ( DECLARE_PARAMETER ) par des fonctions ( instrInfo.AddParameter ), afin d’avoir quelque chose d’un peu plus flexible ( Possibilité de déclarer une valeur par défaut en “chainant” les appels ).

-Les macros DECLARE_ACTION et compagnie sont toujours là, mais ne prenne plus de pointeurs de fonctions vers la fonction à appeler. A la place, utilisez instrInfo.cppCallingInformation.SetFunctionName(“NomDeMaFonction”) ( A placer au même niveau que les paramètres ).
Game Develop peut maintenant utiliser beaucoup plus facilement et directement les fonctions que vous définissez dans vos objets/automatismes et les fonctions libres : Game Develop va générer un appel à la fonction ayant le nom indiqué dans SetFunctionName, avec pour arguments les paramètres de l’action/condition/expression. La correspondance entre arguments C++ et paramètres de Game Develop est disponible dans la documentation à EventsCodeGenerator::GenerateParametersCodes.
Enfin, là aussi, le mieux est de regarder en pratique comment ça marche avec une extension simple comme AES ou Text Object.

-Toujours dans la déclaration des extensions, il faut exclure de la compilation “Runtime” tout ce qui touche aux actions/conditions/expressions. En gros, tous les blocs de déclaration d’actions/conditions/expressions doivent être entourés de #if defined(GD_IDE_ONLY) et #endif. La page “About Extension.cpp” de la documentation montre un exemple à la fin.

-Tout ce qui concerne la classe ObjectsConcerned a disparu. Si vous avez besoin de truc spéciaux genre une liste d’objets dans une condition/action, ajoutez un paramètre invisible dans l’éditeur à l’aide de instrInfo.AddCodeOnlyParameter.

-Les évènements n’ont plus de méthode Execute, mais une méthode GenerateEventCode pour générer le code associé à l’évènement. Vu que les extensions TimedEvent et FunctionEvent risquent de faire un peu peur à ce niveau, la documentation de BaseEvent::GenerateEventCode donne un exemple de ce qu’il faut faire pour générer le code d’un évènement standard.

Pour les fichiers de projets Code::Blocks :
J’ai changé toutes les chemins des bibliothèques externes pour utiliser celles qui sont dans le répertoire ExtLibs plutôt que dans un vulgaire et malpropre C:\Libs.
Changez de même dans vos projets tous les répertoire C:\Libs\ par …\ExtLibs
Les binaires de SFML ne sont plus dans SFML/Libs mais dans SFML/build-mingw-release/lib ( ou SFML/build-mingw-debug/lib ).

Ca fait pas mal de changement, mais je pense que l’écriture d’extension n’est pas plus complexe qu’avant, il va essentiellement s’agir de mettre à jour les fichiers projets avec les nouveaux chemins des bibliothèques et d’adapter la déclaration des extensions.
En cas de problème, dites moi, je peux vous donner un coup de main pour adapter un projet ou si vous butez quelque part.

Merci 4ian. :slight_smile:

Merci, et au niveau de la distribution de l’extension, il faut fournir le xgdwe et wgdw en plus des headers dans un dossier séparé ?

Ah oui, j’ai oublié de parler de ça. Les fichiers d’entête dont votre extension a besoin sont à placer dans Extensions/include/NomDeVotreExtension.
Deux remarques là dessus :
-Vous pouvez utiliser un petit script/fichier batch que vous pourrez lancer pour copier automatiquement les entêtes de votre extension dans le répertoire voulu. C’est ce que j’utilise personnellement, ce qu fait que quand j’apporte des changements à des entêtes utilisés par le code C++ des évènements.
-Limitez au maximum les dépendances de vos fichiers d’entête. Afin d’avoir une compilation des plus rapide possible, soyez sur que les entêtes utilisés de vos extensions soit les plus légers possibles :
Utilisez autant que possible les “forward declaration” ( c’est à dire, plutôt que de faire #include “MaClasse.h”, mettez class MaClasse; pour indiquer au compilateur l’existence de la classe sans l’inclure. Ca ne marche pas contre que si votre fichier d’entête ne contient que des références/pointeurs vers la classe.).
De même, limitez au maximum l’utilisation de bibliothèque externe. Il ne doit y avoir aucune référence à un header de wxwidgets par exemple ( Mais vous remarquerez dans Object.h par exemple que j’utilise des déclarations anticipées pour pouvoir quand même définir des fonctions qui utilisent des classes de wxWidgets ). Pour SFML, n’incluez pas “SFML/Graphics.hpp” ou tout autre entête dans la mesure du possible. Je me suis quand même autorisé l’utilisation de #include <SFML/System/Clock.hpp>, #include <SFML/System/Vector2.hpp> et #include <SFML/Graphics/Color.hpp> qui sont des entêtes utiles et pas trop lourds.

Si vous adaptez votre extension, c’est pas le point essentiel au départ mais les forward declaration d’ailleurs sont de bonne technique à prendre pour limiter le temps de compilation, et pas que dans GD : Utilisez les au maximum dans les fichiers d’entête, et n’incluez le fichier de la classe voulue que quand c’est vraiment nécessaire ( dans un .cpp typiquement ).

Pour la distribution, seule fichier .xgdw est nécessaire, à placer dans Extensions/ donc, à coté du .xgdwe.

Aussi, on doit mettre GD_EXTENSION_API devant les actions/conditions… ?
Comment sont générés les libMachin ? Est-ce en réalité le fichier libMachin.xgdwe.a ?

Exact oui, devant les fonctions à exporter ( c’est à dire, qui peuvent être utilisées par GD ) et devant les noms de classe qui doivent aussi être exportées.
Il faut aussi définir GD_EXTENSION_API=__declspec(dllexport) dans Code::Blocks : Project > Build Options > Compiler Settings > #defines ( pour les projets Windows. Pour les projets linux, GD_EXTENSION_API="" suffit ).

Oui, mais ils ne servent à rien pour le moment.

Et c’est quoi les fichiers AES, Light sans extension ?
Si j’ai une singleton qui gère mes tableaux mais qui n’est pas utilisée directement par les action (des fonctions font l’intermédiaire), dois-je l’exporter ?

EDIT : [size=85]Tu pourrais me dire comment récupérer un pointeur sur le jeu actuel ( Game* ) dans les conditions, les actions, les expressions et les event car j’en ai besoin pour mes tableaux ?[/size]
(Non, j’ai trouvé :smiley:)

EDIT 2 : Tu peux m’expliquer exactement le fonctionnement de SetManipulatedType() et de SetAssociatedGetter ?
Ca sert pour les operator non (signe de modif) ? Si c’est la cas, le getter est appelé avec les mêmes arguments que le setter ?
Pour finir, cette méthode est obligatoire ou peut-on simplement récupérer le signe et faire le traitement soit-même dans le codage de l’action ?

Des reste de “.a” avec un mauvais nom.

Si les fonctions font l’intermédiaire, pas besoin de l’exporter.

Le principe est que si tu indique à Game Develop que ta fonction manipule une std::string ou un nombre ( avec [i]SetManipulatedType/i ou [i]SetManipulatedType/i donc ), Game Develop va appeler ta fonction comme si c’était un “setter”, en lui passant en paramètre les paramètres que tu as entré sauf le paramètre “operator”, et en utilisant la fonction indiquée avec SetAssociatedGetter comme “getter”.
Par exemple, ce code :

[code] DECLARE_OBJECT_ACTION(“String”,
_(“Change the string”),
_(“Change the string of a text”),
_(“Do PARAM2__PARAM1 to the string of PARAM0”),
_(“Text”),
“Extensions/text24.png”,
“Extensions/text.png”);

  instrInfo.AddParameter("object", _("Object"), "Text", false);
  instrInfo.AddParameter("string", _("String"), "", false);
  instrInfo.AddParameter("operator", _("Modification operator"), "", false);

  instrInfo.cppCallingInformation.SetFunctionName("SetString").SetManipulatedType("string").SetAssociatedGetter("GetString").SetIncludeFile("MyExtension/TextObject.h");

DECLARE_END_OBJECT_ACTION()[/code]

va permettre à Game Develop de générer :

TonObjet->SetString("lachainevoulue") //Dans le cas où operator est "=" TonObjet->SetString( TonObjet->GetString() + "chainequejajoute" ) //Dans le cas où j'utilise l'action avec l'opérateur "+"

Voilà. Si Game Develop repère des paramètres en plus que l’opérateur et la valeur associée ( qui doit toujours être placée juste avec l’opérateur ), alors Game Develop les passe comme arguments au setter et au getter.

Tu peux toujours ne pas ajouter le SetManipulatedType à la déclaration de ta fonction, auquel cas ta fonction sera appelée simplement, l’opérateur étant passé sous la forme d’une std::string.
Question performance, mieux vaut quand même utiliser SetManipulatedType. On élimine ainsi entièrement le surcoût lié au test de l’opérateur.

Dernière chose, si tu ne spécifie pas de “getter” avec SetAssociatedGetter, Game Develop considèrera que ta fonction renvoie une (référence) vers une chaine ou un nombre, et utiliser directement =, +=, -=, /= ou *= dessus.
Exemple d’une condition ( ça marche aussi avec les conditions évidemment ) qui utilise pas de “getter” et qui a des argument en plus :

[code] DECLARE_CONDITION(“VarGlobalTxt”,
_(“Texte d’une variable globale”),
_(“Teste si le texte de la variable globale correspond au test effectué.”),
_(“Le texte de la variable globale PARAM1 est PARAM3 à PARAM2”),
_(“Variables”),
“res/conditions/var24.png”,
“res/conditions/var.png”);

    instrInfo.AddCodeOnlyParameter("currentScene", "");
    instrInfo.AddParameter("globalvar", _("Nom de la variable"), "", false);
    instrInfo.AddParameter("string", _("Texte à tester"), "", false);
    instrInfo.AddParameter("relationalOperator", _("Signe du test"), "", false);

    instrInfo.cppCallingInformation.SetFunctionName("GetGlobalVariable").SetManipulatedType("string").SetIncludeFile("GDL/RuntimeSceneTools.h");

DECLARE_END_CONDITION()[/code]

va générer :

GetGlobalVariable(runtimeScene, "nomdelavariable") == 0 //En imaginant que je teste la variable "nomdelavariable", avec l'opérateur "=" et la valeur 0. GetGlobalVariable(runtimeScene, "nomdelavariable2") < 5 //En imaginant que je teste la variable "nomdelavariable2", avec l'opérateur "<" et la valeur 5.

Est-ce que c’est histoire de ManipulatedType et AssociatedGetter marche sur des actions traditionnelles ?

EDIT : J’ai été quand même obliger d’exporter la classe car sinon, GD me dit que la référence est indéfinie.
EDIT 2 : Je peux faire en sorte que mon getter renvoit une référence ? Pour que j’utilise ce getter en tant qu’expression et condition à la fois ?

Oui, la plupart des actions/conditions qui utilisent des opérateurs peuvent ( et doivent ) en bénéficier.

Une fonction qui sert de “getter” peut tout à fait être utilisé en tant qu’expression et condition sans problème. ( Même pas besoin forcément de référence ? )

Pas besoin de référence, ça marche très bien pour les conditions. J’imagine que celui peut entraîner un gain de performances, non ?
C’est cool, car ça va plus vite pour créer les mêmes choses qu’avec le GDSDK 1.5. Bref, ce SDK a l’esprit “programmeur”, c’est-à-dire, il est conçus pour les fainéants. :laughing:

Sinon, niveau perf, passer des string par réf constante peut améliorer grandement les perfs ? Aussi, limiter les tailles des headers en supprimant les noms des paramètres des prototype a un impact sur le perf (minime ou important) ?

Le fait de supprimer le besoin ( dans beaucoup de cas ) d’une fonction annexe peut améliorer les performances oui. En tout cas, dans l’absolu, c’est plus rapide.

Oui, plus besoin quand on veut ajouter une simple propriétés à un objet de se taper l’écriture des 3 fonctions de la condition/action/expression, il n’y a plus qu’à écrire un getter et un setter pour l’objet et à déclarer ça à Game Develop correctement.

Oui, c’est une bonne habitude à prendre que de passer les std::string par référence constante, que ce soit dans GD ou dans les autres codes. Peut être que la lib standard est déjà optimisée pour éviter de copier les string si on les passe par valeur, mais mieux vaut quand même les passer par référence constante, on est sur d’avoir des performances optimales.

Non, c’est minime. Des fois je fais ça pour signaler que ce paramètre n’est pas utilisé par la fonction ( Ça peut arriver si on est forcé d’utiliser un paramètre à cause de la déclaration dans GD ).
Le plus important est vraiment de limiter au maximum les dépendances. Pas exemple, ne surtout pas inclure “GDL/RuntimeScene.h”. La classe et ses dépendances est lourde, trop en tout cas pour avoir un temps raisonnable de compilation dans GD. Mais comme on l’utilise tout le temps par référence, il suffit de faire class RuntimeScene; et le header fonctionne sans problème ( Par contre, il faut bien l’inclure dans le .cpp qui l’utilise évidemment, mais là ça pose pas de soucis ).

Ok, sinon dans mon extension, dans toutes les actions/conditions/expressions, je récupère mon tableau (un std::vector<std::string) via une fonction (de la singleton qui gère les tableaux) qui renvoit une référence vers ce tableau. Est-ce que l’utilisation d’une référence dans ce cas de figure est plus lourd qu’un pointeur.

Car j’ai fait des tests GD1 vs GD2 vs GD2 optimisation et les résultat sont très mauvais.
(test effectués avec un jeu compilé)

  • GD1 réussi à insérer 83906 nombres aléatoire dans le tableau en 30 sec (j’utilise la fonction d’insertion (équivalent de insert pour std::vector)) (Le jeu insère un élément à chaque images)
  • GD2 insère 26041 nombres aléatoire dans le tableau en 30 sec.
  • GD2 avec optimisations insère 27436 nombres aléatoire dans le tableau en 30 sec.

Dans ce cas de figure, GD2 est 3 fois moins rapide que sont prédécesseur, ce qui m’inquiète un peu…
D’ailleurs, le nombre de fps semble plafonner à 1000 fps (pile, ce qui est étonnant) maximum pour GD2 alors que GD1 dépase allégrement les 4000.

Il y aurait possibilité de voir le code source de l’action appelée par GD1 et par GD2 ?

Car de mon coté, une fois que les évènements sont compilés entièrement, la scène ne fait qu’appeler la fonction qui contient les évènements compilés, grâce à un pointeur de fonction : Tout ce qui touche à la génération de code est fait avant, une fois que la scène est lancée, elle execute les évènements à chaque rafraichissement en appelant simplement un pointeur vers une fonction.

Si tes évènements se résume à appeler une action, ça fait pour GD2 deux appels à des fonctions : Le premier de la part de la scène pour appeler la fonction des évènements compilés, le deuxième de la part de cette fonction pour appeler ton action. Là où GD1 faisait au minimum un appel à la fonction Execute() de l’évènement, puis ensuite à ExecuteActions(), puis enfin à ton action, le tout en passant par quelques boucles et la gestion des objets. Peut être cette dernière qui va moins vite dans GD2, même si pourtant avec GD1 j’utilise une classe spécialisée normalement plus lourde que la façon dont GD2 procède… Peut être qu’en activant les optimisations à fond dans GD2 à la compilation on verrait un changement plus important. J’essayerai de compiler une version où les optimisations sont maximum ( O3 ), actuellement même en les activant on se cantonne à du O1, pour éviter une compilation vraiment longue.

Les compilateurs implémente les références en terme de pointeur je crois, donc il y a de grandes chances que ça change rien.

Ok, je crois avoir trouvé d’où venait le ralentissement. MEs tableaux sont des std::vectorstd::string, mais ils peuvent aussi stocker des nombres. Et donc, je fais appel à ToString (de CommonTools.h) pour convertir en std::string. Le ralentissement doit venir de là. Je vais aussi en profiter pour inliner ma fonction qui retourne le tableau. :wink:

J’ai un problème avec le SDK : Mon extension possède plusieurs .h (un header pour les tableaux 1 dim, un autre pour les tableaux 2 dim)
J’ai donc dit pour chaque conditions/actions/expression que leur IncludeFile est soit l’header pour les tableaux 1 dim, soit le header pour les 2 dim.
Or, GD à l’air d’ignorer le deuxième header quand j’utilise l’action qui en a besoin et il me sort ça :

[code]C:\Users\Victor\AppData\Local\Temp/GDTemporaries/0x7085168events.cpp:46:8: error: no member named ‘TwoDimension’ in namespace ‘Array’; did you mean ‘OneDimension’?
Array::TwoDimension::setNumberValue(*runtimeContext->scene, std::string(“test2D”), 2, 10, 1024);

       OneDimension
In file included from C:\Users\Victor\AppData\Local\Temp/GDTemporaries/0x7085168events.cpp:1:
Extensions/include/Array/ArrayOneDimensionalTools.h:12:15: note: 'OneDimension' declared here
    namespace OneDimension
              ^
C:\Users\Victor\AppData\Local\Temp/GDTemporaries/0x7085168events.cpp:46:91: error: too many arguments to function call, expected 4, have 5
Array::TwoDimension::setNumberValue(*runtimeContext->scene, std::string("test2D"), 2, 10, 1024);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                                                       ^~~~
In file included from C:\Users\Victor\AppData\Local\Temp/GDTemporaries/0x7085168events.cpp:1:
Extensions/include/Array/ArrayOneDimensionalTools.h:15:9: note: 'setNumberValue' declared here
        void GD_EXTENSION_API setNumberValue(RuntimeScene &, const std::string &, const double, const double);
        ^
[/code]
Pourtant TwoDimension est un namespace déclaré dans le deuxième header.

EDIT : La fonction setNumberValue est déclarée dans OneDimension (un namespace) et dans TwoDimension (un autre namespace). C'est pour ça que LLVM tente d'utiliser la fonction contenue dans OneDimension vu qu'il ne trouve pas TwoDimension.

Et tes headers ? On ne peut pas t’aider sans la source du problème. :unamused:

EDIT : GDL2 est sorti, mais ça ne t’empêche pas de créer un nouveau topic.

voici les headers :

ArrayOneDimensionalTools.h

[code]#ifndef ONEDIMTOOLS_H_INCLUDED
#define ONEDIMTOOLS_H_INCLUDED

#include

class RuntimeScene;

namespace Array
{
void GD_EXTENSION_API initializeArray();

namespace OneDimension
{
    double GD_EXTENSION_API getNumberValue(RuntimeScene &, const std::string &, const double);
    void GD_EXTENSION_API setNumberValue(RuntimeScene &, const std::string &, const double, const double);
    void GD_EXTENSION_API addNumberValue(RuntimeScene &, const std::string &, const double, const double);

    std::string GD_EXTENSION_API getStringValue(RuntimeScene &, const std::string &, const double);
    void GD_EXTENSION_API setStringValue(RuntimeScene &, const std::string &, const double, const std::string &);
    void GD_EXTENSION_API addStringValue(RuntimeScene &, const std::string &, const double, const std::string &);

    void GD_EXTENSION_API assignFromOtherArray(RuntimeScene &, const std::string &, const std::string &);

    void GD_EXTENSION_API eraseAnIndex(RuntimeScene &, const std::string &, const double);
    void GD_EXTENSION_API eraseAllIndex(RuntimeScene &, const std::string &);

    double GD_EXTENSION_API getCount(RuntimeScene &, const std::string &);

    double GD_EXTENSION_API searchNumberValue(RuntimeScene &, const std::string &, const double, const double);
    double GD_EXTENSION_API searchStringValue(RuntimeScene &, const std::string &, const std::string &, const double);
}

}

//#include “ArrayTwoDimensionalTools.h” juste pour inclure le header le temps que le bug soit corrigé

#endif[/code]

ArrayTwoDimensionalTools.h

[code]#ifndef ONEDIMTOOLS_H_INCLUDED
#define ONEDIMTOOLS_H_INCLUDED

#include

class RuntimeScene;

namespace Array
{
namespace TwoDimension
{
double GD_EXTENSION_API getNumberValue(RuntimeScene &, const std::string &, const double, const double);
void GD_EXTENSION_API setNumberValue(RuntimeScene &, const std::string &, const double, const double, const double);
}
}

#endif
[/code]

Voici la partie d’Extension.cpp

[code]/**
Valeurs d’un tableau à 2 dimensions
**/

            DECLARE_ACTION("2d_SetNumberOnArray",
                           _("Affecter une valeur"),
                           _("Affecter une valeur à un tableau à 2 dimensions"),
                           _("Faire _PARAM5__PARAM4_ à l'index _PARAM2_->_PARAM3_ du tableau à 1 dimension _PARAM1_"),
                           _("Tableaux 2 dim"),
                           "res/Array/setValue24.png",
                           "res/Array/setValue.png")

                            instrInfo.AddCodeOnlyParameter("currentScene", "0");
                            instrInfo.AddParameter("string", _("Tableau"), "", false);
                            instrInfo.AddParameter("expression", _("Index"), "", false);
                            instrInfo.AddParameter("expression", _("Sous-Index"), "", false);
                            instrInfo.AddParameter("expression", _("Valeur"), "", false);
                            instrInfo.AddParameter("operator", _("Signe de modification"), "", false);

                           instrInfo.cppCallingInformation.SetFunctionName("Array::TwoDimension::setNumberValue")
                                                            .SetIncludeFile("Array/ArrayTwoDimensionalTools.h")
                                                            .SetManipulatedType("number")
                                                            .SetAssociatedGetter("Array::TwoDimension::getNumberValue");

            DECLARE_END_ACTION()

            DECLARE_CONDITION("2d_VerifyNumber",
                           _("Valeur d'un index"),
                           _("Teste la valeur d'un index d'un tableau."),
                           _("La valeur de l'index _PARAM2_->_PARAM3 du tableau _PARAM1_ est _PARAM5__PARAM4_"),
                           _("Tableaux 2 dim"),
                           "res/Array/NbrStrAt24.png",
                           "res/Array/NbrStrAt.png")

                            instrInfo.AddCodeOnlyParameter("currentScene", "0");
                            instrInfo.AddParameter("string", _("Tableau"), "", false);
                            instrInfo.AddParameter("expression", _("Index"), "", false);
                            instrInfo.AddParameter("expression", _("Sous-index"), "", false);
                            instrInfo.AddParameter("expression", _("Valeur à comparer"), "", false);
                            instrInfo.AddParameter("relationalOperator", _("Signe de comparaison"), "", false);

                            instrInfo.cppCallingInformation.SetFunctionName("Array::TwoDimension::getNumberValue")
                                                            .SetIncludeFile("Array/ArrayTwoDimensionalTools.h")
                                                            .SetManipulatedType("number");

            DECLARE_END_CONDITION()

            DECLARE_EXPRESSION("2d::NumberAt", _("Valeur d'un index"), _("Valeur d'un index"), _("Tableau 2 dim"), "res/Array/NbrStrAt.png")

                            instrInfo.AddCodeOnlyParameter("currentScene", "0");
                            instrInfo.AddParameter("string", _("Tableau"), "", false);
                            instrInfo.AddParameter("expression", _("Index"), "", false);
                            instrInfo.AddParameter("expression", _("Sous-index"), "", false);

                            instrInfo.cppCallingInformation.SetFunctionName("Array::TwoDimension::getNumberValue").SetIncludeFile("Array/ArrayTwoDimensionalTools.h");
            DECLARE_END_EXPRESSION()[/code]

Tu ne peux pas inclure ArrayTwoDimensionalTools.h car tu as déjà défini ONEDIMTOOLS_H_INCLUDED, ce qui empêche son inclusion.

Oui… :unamused:
Quel idiot je fais… :laughing: