:: PlayerAdvance.org ::  

Précédent   :: PlayerAdvance.org :: > :: Développement Amateur :: > Tutoriels

Publicité

Réponse
 
Outils de la discussion Modes d'affichage
Vieux 12/09/2006, 17h32   #1
Dr.Vince
Administrateur
 
Date d'inscription: 10/11/2005
Messages: 4 962
Voir les codes amis Nintendo DS Voir les codes amis Wii
Par défaut LUA in my game

Intégrer le langage d'extension interprète LUA dans son application GBA

Auteur : moechofe

1. c'est quoi ?
C’est un langage de scripts que l'on intègre dans ses sources C. Ou encore, cela permet d'interprétés des scripts à partir du programme hôte.

2. pourquoi faire ?
Imaginé un jeu d'avenue ou un RPG, dans lequel le héros doit interagirent avec des PNJ. Ces PNJ doivent être capable de réagir différemment en fonction des actions du héros tout au long de la partie, ces PNJ doivent aussi avoir une vie a eux, bref il leur faut une intelligence artificielle. ne partez pas tout de suite. l'intelligence artificielle n'est pas difficile en elle-même, ce qui est dur c'est de la rendre parfaite. Alice ou le Prolog sont des exemples de simplicité qui permettent d'effectuer des choses remarquables. On trouve LUA dans des produits commerciaux comme Homeworld 2, World of Warcraft ou encore Far Cry.

3. comment ça marche ?
Il faut intégrer la librairie ou simplement compiler les sources avec votre application, déclarer toute une série de fonctions utilisables à partir des scripts LUA (scroller la map selon des trajectoires, déplacer les protagonistes, faire fondre les couleures de la palette, écrire du texte dans une console), intégrer les scripts LUA dans votre application (en chargent des fichiers, en lisant directement dans la rom), et exécuter ces derniers.

4. quoi d'autre ?
LUA est un langage procédural offrant une syntaxe simple, proche de celle du pascal. il utilise un garbage collector qui s'occupe de s'occuper de votre mémoire et de vos variables. LUA est le plus rapide des langages d'extension interprète grâce au fait qu'il pre-compile le script en un bytecode indépendant de la plateforme (comme java). LUA est compatible ansi c. il existe d'autres distributions qui rajoutent des fonctionnalités, comme CaLUA et ToLua, a vous de voir.

5. comment on fait, alors ?

5.1. Récupérer les sources de LUA sur le site http://www.lua.org/. Pour ma pars c'était lua-5.0.2.tar.gz. Il existe des versions déjà compilées qui offre la possibilité de tester vos scripts dans une console, et aussi de pré-compile les scripts avant de les intégrer dans votre application (c'est très pratique, on gagne un temps fou, mais je n'ai pas encore teste) pour l'instant j'en suis resté aux sources directement intégrées dans mon projet (c'est parce que j'ai pas réussi a compiler la librairie en faite)

5.2. commencer un nouveau projet. (Et puis pour faire plus simple, je vais parler a la première personne). donc je créer un dossier lua_in_my_game/ pour démarrer un nouveau projet, et puis comme je suis sympa, je vais le faire avec HAM. j'estime que ceux qui compilent avec DEVKITADV n'ont pas de souci avec leur makefile.

5.3. Je décompresse l'archive dans le dossier, j'obtiens un lua-5.0.2/, dedans il y a un dossier src/, je copie tous les fichiers *.h et *.c qui s'y trouvent dans un dossier a la racine de mon projet que j'ai nomme lua/, je retourne dans lua-5.0.2/ il y a un dossier include/ dans lequel je prends les fichiers lua.h, lauxlib.h et lualib.h, (d'après ce que j'ai pu lire, lauxlib.h est très bien si vous souhaiter créer votre propre librairie base sur LUA, car c'est aussi quelques choses de très intéressantes, question performance), je les copie dans le dossier lua/ aussi.

5.4. Je vais compiler tout ça. Pour ça, je ne vais pas m'embêter a intégrer tous les fichiers un par un dans la variable OFILES de mon makefile, je vais le faire automatiquement :
Code:
#
# Set a list of files you want to compile
#
OFILES += main.o

LUASRC = $(wildcard lua/*.c)
OFILES += $(LUASRC:.c=.o)
Un petit coup de compilateur, et hop, lua_in_my_game.gba fait deja 162ko, il faut dire que j'ai tout compilé, alors qu'on peut n'integrer que ce qui nous interesse. Voici donc la les fichiers de la verion core de la librairie LUA :
Code:
 lib:lapi.c lcode.c ldebug.c ldo.c ldump.c lfunc.c lgc.c llex.c lmem.c lobject.c lopcodes.c lparser.c lstate.c lstring.c ltable.c ltests.c ltm.c lundump.c lvm.c lzio.c
Et ici la liste des fichiers qui constitue la librarie standard :
Code:
 lauxlib.c lbaselib.c ldblib.c liolib.c lmathlib.c ltablib.c lstrlib.c loadlib.c
Ces informations sont disponible dans le fichier lua-5.0.2/INSTALL.

interpreter:core lib, standard lib, lua.c

compiler:core lib, standard lib, luac.c print.c
and also lopcodes.c (with LUA_OPNAMES defined) from core.

5.5. On va affiche une connerie a l'écran :
Code:
int main(void)
{
  ham_Init();
  ham_InitText(0);
  ham_DrawText( 0, 0, "%s", "un connerie" );
  while(TRUE);
  return 0;
}
Je compile et je test, j'ai effectivement une connerie d'affiche au haut a gauge.

5.6. On va voir si LUA fonctionne un peu, je rajoute l'inclusion suivante après <mygba.h> :
Code:
#include "lua/lua.h"
Je rajoute avant le while infini :
CODE
  // créer un nouvel état lua
  lua_State *L = lua_open();
  // ferme l'etat
  void lua_close( lua_State *L );
Ça compile encore dit donc. Les états LUA sont comme des contextes, on peut en créer plusieurs, ils auront chacun leurs variables. On peut switcher d'un état a un autre, mais pas e, avoir deux en même temps.

5.7. Je créer une fonction qui sera utilisable par LUA :
Code:
static void script_texte( lua_State *L )
{

 // récupère le nombre de paramètres dans la pile
 unsigned short n = lua_gettop(L);

 // scanne tous les paramètres
 unsigned short i;
 for( i=1; i<=n; i++ )
 {

   // si le paramètre est une chaîne de caractères
   if( lua_isstring(L,i) )
     ham_DrawText( 0, 1, "%s", lua_tostring(L,i) );

   // si le paramètre est nul
   else if( lua_isnil(L,i) )
     ham_DrawText( 0, 1, "nil" );

   // si le paramètre est un booléen
   else if( lua_isboolean(L,i) )
     ham_DrawText( 0, 1, lua_toboolean(L,i) ? "true" : "false" );

 }

}
J'ai choisi script_ comme préfixe, pour ne pas interférer avec les fonctions internes de LUA qui commencent toute pas lua_. Si vous compiler avec une fonction qui ne renvoie rien, ça ne compile pas. Si vous avez une autre fonction d'affichage a la place de ham_DrawText, ne vous gênez pas. Si vous déclarer la fonction après le main, il faudra ajouter un prototype, ça compile toujours avec deux warnings, lua_State et script_texte non utilisés. J'expliquerai un peu plus tard ce que fait ma fonction script_texte.

5.8. J'informe LUA que j'ai créé une fonction pour lui, j'ajoute pendant que mon état existe :
Code:
  // enregistre une fonction à destination de LUA
  lua_register(L,"texte",script_texte);
Ça veut dire, entre les fonctions lua_open et lua_close, pour ceux qui ne suivent pas. Et la, ça compile plus. Ba vi, il faut que la fonction retourne quelque chose, toujours :
Code:
static int script_texte( lua_State *L )
N'oubliez pas votre prototype s’il y en a un. Maintenant ça compile mieux.

5.9. Interlude. Pour tester la fonction. Il y a deux manières de procéder, soit en utilisant un script LUA, soit directement via le code C. cette dernière méthode est une opération plutôt délicate, mais obligatoire pour bien comprendre le fonctionnement. LUA utilise un systeme de pile pour transvaser les variables, les donnes, mais aussi : tout. exemple : lua_register n'est pas une fonction, mais une macro, et voila a quoi elle ressemble :
Code:
#define lua_register(L,n,f) \
       (lua_pushstring(L, n), \
       lua_pushcfunction(L, f), \
       lua_settable(L, LUA_GLOBALSINDEX))
// lua_State *L;
// const char *n;
// lua_CFunction f;
Tout passe par la pile, ici, on push le nom de la fonction dans la pile, on push l'adresse-mémoire de la fonction dans la pile, et on fait un lua_settable. lua_settable sert a plein de choses, c'est une fonction magique, voir la doc dans le dossier lua-5.0.2/doc/manual
.html. Elle va dépiler les deux paramètres que l'on vient d'empiler, et en lui passant le paramètre LUA_GLOBALSINDEX, elle va créer une fonction dans le contexte global. Schématiquement : le contexte global est un gros tableau a clefs associatives. lua_settable ajoute dans ce tableau une clef qui est le nom de la fonction et, la valeur associe a cette clef qui est l'adresse de la fonction dans la mémoire. Le truc c'est que le contexte global se trouve lui aussi dans la pile. Cette fonction mérite beaucoup d'attention, mais pas tout de suite.
Tout est histoire d'empilage et de dépilage. La pile est de type FIFO. CQP (comprendront qui pourront) (first in, first out) (premier entre, premier sortie) Je peux donc maintenant vous expliquer ce que fait la fonction script_texte que j'ai créer.
lua_gettop récupère le nombre de paramètres ajouter dans la pile. (On ne sait pas encore comment de paramètre a été ajoute dans la pile : en faite c'est la fonction lua_call qui la fait, mais ça on ne la pas encore vu.)
lua_isstring, lua_isnil, lua_isboolean (y'en a d'autre) vérifie que le paramètre i dans la pile est bien du type voulu. lua_tostring, lua_toboolean (y' en a d'autre) retourne le paramètre i dans le type voulu. (J’ai pas encore testé, mais d'âpres ce que j'ai lu, LUA est très peu typé, on peut donc facilement convertir n'importe quoi en n'importe quoi.)

5.10. Interlude. la liste des types LUA !
Code:
 LUA_TNONE, LUA_TNIL, LUA_TNUMBER, LUA_TBOOLEAN, LUA_TSTRING, LUA_TTABLE, LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, LUA_TLIGHTUSERDATA
La liste des fonctions qui teste le type des paramètres stoker dans la pile :
Code:
 int lua_type            (lua_State *L, int index);
 int lua_isnil           (lua_State *L, int index);
 int lua_isboolean       (lua_State *L, int index);
 int lua_isnumber        (lua_State *L, int index);
 int lua_isstring        (lua_State *L, int index);
 int lua_istable         (lua_State *L, int index);
 int lua_isfunction      (lua_State *L, int index);
 int lua_iscfunction     (lua_State *L, int index);
 int lua_isuserdata      (lua_State *L, int index);
 int lua_islightuserdata (lua_State *L, int index);
La liste des fonctions qui renvoie un paramètre stoker dans la pile :
Code:
 int            lua_toboolean   (lua_State *L, int index);
 lua_Number     lua_tonumber    (lua_State *L, int index);
 const char    *lua_tostring    (lua_State *L, int index);
 size_t         lua_strlen      (lua_State *L, int index);
 lua_CFunction  lua_tocfunction (lua_State *L, int index);
 void          *lua_touserdata  (lua_State *L, int index);
 lua_State     *lua_tothread    (lua_State *L, int index);
 void          *lua_topointer   (lua_State *L, int index);
et puis dans la foulée, des fonctions qui empile un paramètre d'un type défini :
Code:
 void lua_pushboolean       (lua_State *L, int b);
 void lua_pushnumber        (lua_State *L, lua_Number n);
 void lua_pushlstring       (lua_State *L, const char *s, size_t len);
 void lua_pushstring        (lua_State *L, const char *s);
 void lua_pushnil           (lua_State *L);
 void lua_pushcfunction     (lua_State *L, lua_CFunction f);
 void lua_pushlightuserdata (lua_State *L, void *p);
Le tout avec comme information supplémentaire :
Code:
 @param lua_State *L : est un État LUA
 @param int index : est l'index du paramètre dans la pile
5.11. Donc pour tester ma fonction script_texte, je rajoute :
Code:
  // tente d'appeler directement une fonction LUA
  lua_pushstring(L,"returnvalue");
  lua_pushstring(L,"texte");
  lua_gettable(L, LUA_GLOBALSINDEX);
  lua_pushstring(L,"hello world");
  lua_call(L,1,1);
  lua_settable(L, LUA_GLOBALSINDEX);
Ça compile, et ça marche. Juste pour information, ces 6 lignes en LUA s'écrivent :
Code:
 returnvalue = texte("hello world")
Et pour expliquer :
lua_pushstring ajoute un paramètre de type chaîne dans la pile. lua_gettable qui fait l'inverse de lua_settable. (Et vous n'avez normalement toujours pas compris. Ça va venir.)
lua_call appelle une fonction avec un nombre de paramètre en envoye, et un nom de nombre paramètre en retour (eh oui, voici un des rares langages qui autorisent les fonctions a retourner plusieurs valeurs.)
Et on recommence les explications :
lua_pushstring(L,"returnvalue"); place dans la pile le nom de la variable LUA (pas une variable utilisable dans le code C. du moins pas encore). Cette variable recevra le résultat de l'appel a ma fonction script_texte (qui est 0 (zéro)). lua_pushstring(L,"texte"); place dans la pile le nom de la fonction. lua_gettable(L, LUA_GLOBALSINDEX); dépile le paramètre fraîchement envoyé texte et récupère dans le contexte global (le gros tableau associatif) la valeur pour laquelle la clef est texte. cette valeur c'est l'adresse-mémoire de la fonction LUA texte, qui n'est autre que l'adresse-mémoire de la fonction C script_texte. Et c'est pas tout, la fonction lua_gettable finis par ajouter dans la pile cette adresse. lua_pushstring(L,"hello world"); empile le premier et l'unique paramètre (si vous avez suivi, notre fonction script_texte peut prendre un infinité de paramètres). lua_call(L,1,1); indique qu'il y a un paramètre a envoyer et un parametre a attendre de la fonction dont le nom se trouve après tout ça (pfuuuu).
Un petit schéma :
Code:
o---------------------------------o---------o---------------o
|XXXXXje_suis_la_pileXXXXXXXXXXXXX| "texte" | "hello world" |
o---------------------------------o---------o---------------o
|                                  |         |
|                                  |         ^---------------> Le seul et unique paramètre (le pauvre).
|                                  |                           Vous pouvez en ajouter pour tester.
|                                  ^-------------------------> Le nom de la fonction ou plutôt,
|                                                              l'adresse-mémoire de la fonction.
^------------------------------------------------------------> Ça, c'est la pile
5.12. Conclusion. Pour un début, c'est un début, mais voir ce gros paquet de scripts C qui fonctionne sans broncher après quelques lignes, ça donne envie de continuer. Il ne vous reste plus qua virer tous les fichiers *.s qui se trouve a la racine du prohet qui ont pu être créé par les précédentes compilations, ils ne serviront plus maintenant. les sources du tutoriel lua_in_my_game.1.zip.

6. et our aller plus loin ?
Une documentation en français http://lyrian.obnix.com/lua/ qui vous permettera de comprendre le fonctionnement générale, et de découvrir les possiblités. C'est du LUA en version 3, donc ne vous fiez pas au code donnée.
La doc interne de LUA en anglais qui se trouve dans lua-5.0.2/doc/manual.html. Elle vous expliquera toutes les fonctions de l'API ainsi que la syntaxe de LUA.
Un exemple simple pour implementer, une fonction LUA et une méthode de récuperation des scripts LUA dans un source C se trouve dans le fichier lua-5.0.2/etc/min.c
Et puis ya google aussi.

Ajouter des scripts LUA externe dans son projet via GBFS.

6. Pourquoi faire ?
Parceque sinon xFlash, il aurait fait un tutoriel pour rien.

7. Par ou commencer ?
A lire le tutoriel de xFlash sur playeradvance et tenter de tout comprendre. Et bien sur, de reprendre les sources obtenues après le premier tutoriel sur LUA

7.1. Je récupère les fichiers gbfs.h et libgbfs.c et je les ajoute dans un dossier gbfs/ a la racine du projet. Puis j'ajoute dans le fichier main.c dans la déclaration des fichiers à inclures :
Code:
#include "gbfs/gbfs.h"
C'est pas tout, il faut aussi compiler le fichier libgbfs.c, pour cela j'édite le makefile et j'ajoute quelque pars :
Code:
OFILES += gbfs/libgbfs.o
Pour bien faire, (lu dans le tutoriel sur GBFS) il faudrait que cette librairie soit ajouter en dernier dans l'édition des liens, cela devrait douc ressembler a ceci :
Code:
#
# Set a list of files you want to compile
#
OFILES += main.o

LUASRC = $(wildcard lua/*.c)
OFILES += $(LUASRC:.c=.o)

OFILES += gbfs/libgbfs.o
Ca devrait compiler sans problème.

7.2. Interlude. Je rapelle qu'il existe deux façons d'éxecuter une fonction LUA : soit en utilisant l'API de LUA directement en code C, ou bien utiliser des fichiers scripts LUA externe, que l'on insère dans la ROM, et que l'on charge par la suite grace à l'API de LUA. Cette première solution est expliquer dans le premier tutoriel, elle est compliquée à mettre en oeuvre mais importante pour comprendre le système de pile qui est l'une des choses les plus présentes au fond de tout système bas niveau. La deuxième méthode est autrement plus efficasse et bien plus simple à mettre en oeuvre. Elle permet par exemple, d'éxecuter plusieurs instruction à la suite (di donc), chose que la première méthode ne fait pas.
Pour lire des scripts LUA à partir de votre code C, il y a là aussi deux manières : lire les scripts directement dans la ROM grace à leur adresse, ou utiliser le système de fichier que Damian Yerrick nous met a disposition. (et même que xFlash il a écrit un tutoriel pour ça). Je n'expliquerai que cette dernière méthode, qui reviens en faite au même que la première, a savoir : pointer sur l'adresse du fichier, et dire à LUA qu'il faut lire x données.

7.3. Interlude. L'implementation du systeme de fichier GBFS est relativement simple, il faut utiliser le programme gbfs.exe pour empiler les scripts ou les fichiers que l'on veux. Il faut ensuite effectuer une operation sur la ROM compiler pour que sa taille atteigne un multiple de 256 octes. Dernier étape, il faut joindre la ROM avec les fichiers empilés (retourné par gbfs.exe). Pour ça, on utilise la commande DOS copy (je n'est pas trouvé l'équivalent sur linux), qui permet de copier plusieurs fichiers dans un seul.

7.4. Mais je vais d'abord commencer par créer un script LUA qui va se charger d'honorer ma belle fonction C script_texte. (Je rappele au passage que la fonction C script_texte s'appele texte en LUA), (c'est fait exprès pour vous embrouiller), (non, c'est fait exprès pour ne pas confondre). Je créer un beau dossier script, dans le lequel je créer un fichier test.lua, dans lequel j'ajoute ceci :
Code:
texte("Bonjour le monde")
7.5. Je vais ensuite m'occuper de fabriquer ce fichier .gbfs/. j'ajoute donc quelque pars dans mon makefile.
Code:
GBFSFILES = $(wildcard script/*.lua)
Et ensuite je modifie la cilble all :
Code:
all : $(PROGNAME).$(EXT) $(PROGNAME).gbfs clean
Juste après, je créer une nouvelle cible :
Code:
# empile les scripts LUA
$(PROGNAME).gbfs:
tools/gbfs.exe $(PROGNAME).gbfs $(GBFSFILES)
J'ai choisis d'inclure directement les executables fournis par Damian Yerrick dans mon projet, dans un dossier nommé tools/.
Une fois le fichier de donnée obtenu, je le join au binaire compilé, pour cela, je remodifie la cible all comme ceci :
Code:
##########################################################################################
# Standard Makefile targets start here
##########################################################################################
all : $(PROGNAME).$(EXT) $(PROGNAME).gbfs clean
tools/padbin 256 $(PROGNAME).$(EXT)
copy /y $(PROGNAME).$(EXT) /b + $(PROGNAME).gbfs /b $(PROGNAME).$(EXT)
Ne pas oublier les tabulations[/code]Je test tout ça avant de continuer.

7.6. Maintenant je vais ecrire le bout de code qui me permettra de reperer mon script grace â GBFS. Comme j'ai decidé que je n'allais utiliser qu'un seul fichier à la fois, je déclare les quelques variables suivantes en globale :
Code:
// le nombre de fichiers present dans la ROM
unsigned long nombrefichiers;
// le nom du dernier fichier pointé
char nomfichier[24];
// la taille du dernier fichier pointé
unsigned long taillefichier;
// pointeur sur le systeme de fichier
const GBFS_FILE * systemefichier = NULL;
// pointeur sur le debut du fichier courant
const char * fichiercourant;
Puis j'ajoute quelque pars dans mon code les quelques lignes :
Code:
   // pointe sur la TOC
   systemefichier = find_first_gbfs_file(find_first_gbfs_file);
   // pour le fun, je ne m'en sert pas dans ce tutoriel
   nombrefichiers = gbfs_count_objs(systemefichier);
TOC = Table Of Content, Table des matières en français.
Je vais maintenant pointer sur le script test.lua :
Code:
   // pointeur sur le fichier choisi
   fichiercourant = gbfs_get_obj( systemefichier, "test.lua", &taillefichier );
Et tester son contenu :
Code:
   // j'affiche brut, le contenu
   ham_DrawText( 0, 2, "%s", fichiercourant );
A la compilation, éxecution, mon script s'affiche, mais il n'est pas encore executer par LUA. (Et il y a des warning

7.7. Interlude. Les possiblitées offerte par LUA pour récupérer du code de l'exterieur on beaucoup évolué depuis les premières versions. Toujours de plus en plus simple. La dernière méthode permet en une fonction de charger soit des scripts LUA, soit des scripts pre-compile LUA. Pour ma part je n'ai pas encore réussi a faire fonctionner des scripts pré-compilé.

7.8. Interlude. La fonction lu_load() permet de charger un script LUA en utilisant une fonction de callback (j'ai pas de traduction). en gros, on fourni à lua_load() un pointeur sur quelquechose qui peut être un fichier ou un buffer d'entrée comme stdin, et a chaque fois que lua_load() en a besoin, elle fera appel à une fonction de callback qu'il faudra écrire. Cette fonction doit renvoyer une portion du script que l'on veux charger ainsi que la taille de cette portion. Si vous desirer connaître la raison de ce fonctionnement étrange, j'ai deux bonnes raisons : plus de souplesse quand à la possiblité de recupérer des donnees depuis n'importe quelle type de source, et, des traitements possible à la volé de ces sources comme par exemple de la décompression. Pour cette derniere raison, les possiblites ne sont pas énorme puisque les accès aux fonctions LUA ne sont pas autorisé, du moins pas avec la version 5.0.2. Ce référer à la documentation pour plus d'informations lua_5.0.2/doc.manual.htm : 3.10 - Loading Lua Chunks.

7.9. Interlude. Cette fonction : callback est donc appelé plusieurs fois, minimum deux. Pour le dernier appel, la fonction de callback devra renvoyer NULL, cela indique à lua_load() qu'il faut arreter d'appeler la fonction callback. Pour mes tests, je n'ai pas eu besoin d'envoyer les scripts en plusieurs morceaux, donc deux appels on suffit. Un cas ou il serait nescessaire d'effectuer plusieurs passes serait dans le cas d'une utilisation d'un buffer. Dans mon cas, la ROM ne risque pas de ce barrer, je peux donc accélérer les choses.

7.10. Voici donc deux nouvelles fonctions a étudier :
Code:
/* recupere_fichier {{{
*
* fonction callback pour lua_load()
*
* @param lua_State *L un état LUA (toujours a null)
* @param void *data un pointeur sur les donnees
* @param size_t *size la taille des donnees (a retourner)
*/
static const char * recupere_fichier( lua_State *L,
                                     void *data,
                                     size_t *size )
{
 // permet de vérifier si le fichier a déjà été envoyés
 static const char * fichierprecedent = NULL;
 // si le script a déjà été envoyé
 if( fichierprecedent == fichiercourant )
 {
   // renvoie null
   *size = 0;
   return NULL;
 }
 // sinon
 else
 {
   // renvoie un pointeur sur le script
   *size = taillefichier;
   // envoie le fichier en une seule partie
   fichierprecedent = fichiercourant;
   // j'aurai pu mettre ici : return data;
   return fichiercourant;
 }
}
// recupere_fichier */

/* charge_script {{{
*
* charge un script LUA et l'éxecute
*
* @param lua_State *L un état LUA
* @param char * nomfichier le nom du fichier a charger
*/
static const char * charge_script( lua_State *L, char * nomfichier )
{
 // pointe sur le fichier
 fichiercourant = gbfs_get_obj( systemefichier, nomfichier, &taillefichier );
 // appel la fonction de chargement des script LUA
 if( lua_load( L, recupere_fichier, (void*) fichiercourant, nomfichier )
 // et dans la foulé, éxecute le script
 || lua_pcall( L, 0, 0, 0 ) )
   // gestion des erreurs
   ham_DrawText( 0, 4, "%s",lua_tostring(L,-1));
 // ca, c'est pour la suite
 return fichiercourant;
}
// charge_script
La fonction a appeler depuis main() est la fonction charge_script. Elle s'occupe de pointer sur le fichier dans GBFS, de faire appel à lua_load(), d'éxecuter le résultat, et d'afficher un message d'erreur si il y besoin.
lua_pcall fait la même chose que lua_call a savoir : appeler une fonction, mais elle permet en plus de récupérer les erreurs.
L'appel a la fonction lua_load() nescessite l'adresse de la fonction de callback que j'ai appelé recupere_fichier. Cette dernière recoie trois paramêtres : un état LUA toujours a NULL (du moins pour la version 5), un pointeur sur mon script LUA, et une variable qu'il faudra renseigner. C'est un exemple grandement simplifié. Pour plus d'information vous pouvez ouvrir le fichier lua_5.0.2/etc/min.c qui est exemple d'utilisation de lua_load, et aussi la source de ce tutoriel.
La fonction lua_tostring() dont je vous ai déjà parler prend pour la première fois un paramêtre numérique négatif. La documentation en anglais explique très bien tout ceci lua_5.0.2/doc/manual.htm[/i] : [u]3.2 - The Stack and Indices. Lors de l'appel d'une fonction, les paramêtre sont envoyer a cette fonction dans un ordre. Pour récuperer un paramêter dans le même ordre, il faut utiliser un index positif. Pour récuperer un paramtre depuis le haut de la pile, il faut utiliser un index négatif.
Code:
o-------------------------o---o----o----o----o---o
|XXXje_suis_la_pileXXXXXXX|   | p1 | p2 | p3 | 3 |
o-------------------------o---o----o----o----o---o
|                          |   |    |    |    |
|                          |   |    |    |    ^-> le nombre de paramêtres
|                          |   |    |    |
|                          |   |    |    ^------> le dernier paramêtre
|                          |   |    |             accessible depuis :
|                          |   |    |             l'index 3 ou
|                          |   |    |             l'index -2
|                          |   |    |
|                          |   |    ^-----------> index 2 ou -3
|                          |   ^----------------> index 1 ou -4
|                          |
|                          ^--------------------> l'appel à la fonction
|
^-----------------------------------------------> c'est la pile
Ne vous fiéer pas trops aux valeures. C'est un schema simplifié.

7.11. Interlude. Petit précision pour ceux qui ont repéré la confusion. lua_call() permet d'appeler une fonction, or dans mon exemple, je n'appel pas une fonction mais directement un script. En fait on appele quelquechose qui se trouve à une adresse, quelque soit sont type, fonction ou pas fonction. Cela touche à une des souplesses agréable qu'ajoute les languages de scripts par rapport aux languages compiler. L'un des languages de scripts que je trouve le plus impressionant dans le domaine c'est le REBOL : il n'y a pas de notion de variable ou de fonction, tout est : mot (word). Et un mot peut aussi bien contenir des données numérique ou alphabetique que du code. Résultat, on peut donc avec des tableaux imbriquer, avoir des entiers dans du code dans un tableau dans du code dans une chaine de caractere. Le lisp est aussi un très bon exemple puisque 90% du LISP est écrit en LISP. LUA est plus modeste mais aussi bien plus rapide. Il peut quand même simuler tout comme REBOL, la plupart des comportements de la POO : programmation orientée objets. Et ça, c'est vachement bien.

7.12. Pour la suite je vais enlever de mon code mes précédents exemples et en ajouter un nouveau, avec un appel à la fonction fraichement écrite charge_script() :
Code:
int main(void)
{

   ham_Init();
   ham_InitText(0);

   // creer un nouveau etat lua
   lua_State *L = lua_open();

   // enregistre une fonction a destination de lua
   lua_register(L,"texte",script_texte);

   // pointe sur la TOC
   systemefichier = find_first_gbfs_file(find_first_gbfs_file);
   // pour le fun, je ne m'en sert pas dans ce tutoriel
   nombrefichiers = gbfs_count_objs(systemefichier);

   // charge et execute un script LUA
   charge_script( L, "test.lua" );

   // ferme l'etat
   void lua_close( lua_State *L );

   while(TRUE);
   return 0;
}
Puis je compile, je teste, et ça marche, je suis content, je vais me coucher.

8. Et maintenant ?
Tout le nescessaire minimum est présent, à pars peut-être la possiblité d'intégrer des scripts pré-compilé, et puis aussi de connaitre le language même LUA. (Les habitués des languages de script seront avantagé). les sources du tutoriel lua_in_my_game.2.zip
Pour ce qui est d'insérer des script pré-compilé, vous avez le droit de m'aider.
Pour ce qui est d'apprendre LUA la documentation officiel n'est pas ce qu'il y a de mieux, mais il reste les examples. Bon code, ou bon deboggage.
Dr.Vince est déconnecté   Réponse avec citation

Publicité

Vieux 12/09/2006, 17h36   #2
Mollusk
Membre confirmé
 
Date d'inscription: 10/11/2005
Messages: 1 037
Par défaut

Et bien, c'est long ! Beau boulot.
Mollusk est déconnecté   Réponse avec citation
Vieux 12/09/2006, 20h28   #3
Dr.Vince
Administrateur
 
Date d'inscription: 10/11/2005
Messages: 4 962
Voir les codes amis Nintendo DS Voir les codes amis Wii
Par défaut

bah c'est moechofe qu'il faut remercier, s'il nous lit encore
__________________
Projets Abandonnés: [Arcomage Advance] [Puzznic] [PA Card Games] [Blob Runner]
Projet en cours: [Ne plus abandonner de projet...]
Dr.Vince est déconnecté   Réponse avec citation
Vieux 12/09/2006, 20h53   #4
DJP
Bitchy Little Girl...
 
Date d'inscription: 29/10/2005
Messages: 3 199
Par défaut

Encore !
__________________

>> On garde :: On améliore :: On kill <<
DJP est déconnecté   Réponse avec citation
Vieux 12/09/2006, 22h40   #5
Dr.Vince
Administrateur
 
Date d'inscription: 10/11/2005
Messages: 4 962
Voir les codes amis Nintendo DS Voir les codes amis Wii
Par défaut

Citation:
Envoyé par DJP
Encore !
moechofe nous lit encore ou tu veux encore des tutos DJP ??
__________________
Projets Abandonnés: [Arcomage Advance] [Puzznic] [PA Card Games] [Blob Runner]
Projet en cours: [Ne plus abandonner de projet...]
Dr.Vince est déconnecté   Réponse avec citation
Vieux 12/09/2006, 22h52   #6
DJP
Bitchy Little Girl...
 
Date d'inscription: 29/10/2005
Messages: 3 199
Par défaut

Citation:
Envoyé par Dr.Vince
tu veux encore des tutos DJP ??
encore !
__________________

>> On garde :: On améliore :: On kill <<
DJP est déconnecté   Réponse avec citation
Vieux 13/09/2006, 15h03   #7
oldteen
 
Messages: n/a
Par défaut

Je testerais ce soir si ca fonctionne pour la PSP, mais il n'y a pas de raison que non vu que lua est codé avec du C très standard.

en tout cas bravo!
  Réponse avec citation
Vieux 13/09/2006, 20h22   #8
omg
Modérateur
 
Date d'inscription: 10/11/2005
Localisation: Mad Monster Mansion
Messages: 3 754
Par défaut

Bien sympa Dr Vince!
Faut que je me penche là-dessus!
__________________

News GBA/DS:
Portable DEV
Colorer un jeu GB? Débutant - Avancé - Expert
Projets : Banjo Advance - The Last Quest - Klungo's Brain School
Site des jeux : BanjoKazooie.Free.Fr Web Site

FAN FOREVER
"La Vie n'est pas aussi simple qu'un Jeu Vidéo..."
omg est déconnecté   Réponse avec citation
Réponse

Liens sociaux

Publicité



Utilisateurs regardant la discussion actuelle : 1 (0 membre(s) et 1 invité(s))
 
Outils de la discussion
Modes d'affichage

Règles de messages
Vous ne pouvez pas créer de nouvelles discussions
Vous ne pouvez pas envoyer des réponses
Vous ne pouvez pas envoyer des pièces jointes
Vous ne pouvez pas modifier vos messages

Les balises BB sont activées : oui
Les smileys sont activés : oui
La balise [IMG] est activée : oui
Le code HTML peut être employé : non
Navigation rapide


Fuseau horaire GMT +2. Il est actuellement 01h28.


Édité par : vBulletin® version 3.7.2
Copyright ©2000 - 2019, Jelsoft Enterprises Ltd. Tous droits réservés.
Version française #16 par l'association vBulletin francophone
Design par Ass-Itch, DJP et Dr.Vince