Startseite | Forum | Artikel/Tutorials | Downloads | Links
 
Navigation
Startseite
Forum
Artikel/Tutorials
Downloads
Bücher
Links
Kontakt
Galerie
Suche

Anzeige
FlashEcke.de

Shoutbox
Du musst Dich einloggen, um eine Nachricht zu schreiben.

GSlang
03.01.2010
WoW

Himmelweiss
29.12.2009
Denk dir nix, bei mir antwort doch auch keiner smiley Naja k kurdt hat mal seinen Senf dazugegeben, aber sonst.. smiley

Wam
26.12.2009
Könnte mir jemand darauf antworten: http://www.isogames.de/fo
rum/viewthread.php?forum_
id=30&thread_id=221


kurdt
23.12.2009
So, Klausuren vorbei und auch endlich mal Urlaub, wenn's auch mit Lernen verbunden ist, aber jetzt kann ich auch mal Tuts weitermachen smiley

Himmelweiss
02.12.2009
tja und ja, so schnell kanns gehn smiley

Shoutbox Archiv

Benutzer Online
Gäste Online: 2
Keine Mitglieder Online

Registrierte Mitglieder: 950
Neustes Mitglied: klausi1305

Eine Einführung in Lua

Lua-Tutorial:


Einbindung von Lua 5.0 anhand von Beispielen


Lua ist eine kleine, aber feine Script-Sprache, die frei erhältlich und frei verwendbar ist. Der Sourcecode ist unter http://www.lua.org erhältlich, dort kann man auch diverse Wrapper für Objekte und anderes finden. Das Manual, das dort erhältlich ist, lässt jedoch einiges zu wünschen übrig, und deshalb werden wir
hier jetzt ein paar dieser Schritte hier nachvollziehen.

Lua ist sehr einfach aufgebaut, verbraucht kaum Speicher (100 KB zusätzlich in der EXE-Datei) und ist doch recht mächtig.

Dieses Tutorial geht von Visual Studio .NET 2003 und Lua 5.0 aus. Das Tutorial sollte auf anderen Compilern bzw. IDEs ohne Änderungen laufen. Eventuell unterscheidet sich die Bedienung der IDE.


Einbinden von Lua in das Projekt:

Wir beginnen mit dem Erstellen eines neuen Projektes. Wir wählen unter Datei->Neu->Projekt links den Ordner „Visual C++-Projekt“ und rechts „Win32 Konsolenprojekt“. Bestätigen und den Wizard das Projekt erstellen lassen.

Danach in den Projekt-Eigenschaften die Option unter „C++“->„Vorkompilierte Header“ auf „Vorkompilierte Header nicht verwenden“ stellen.

Lua kann als .LIB eingebunden werden, oder aber auch die Source-Dateien direkt verwendet. Da das Erstellen der Libraries etwas aufwendiger ist, und wir uns im Tutorial auf die Einbindung konzentrieren, wählen wir hier die zweite Variante.

Im Projekt-Verzeichnis erstellen wir ein Unterverzeichnis „Lua“. Dorthin kopieren wir aus dem Lua-Paket sämtliche .c und .h-Dateien aus dem „src“-Verzeichnis. Ebenso alle .c-Dateien aus dem „src/lib“-Verzeichnis. Aus dem „include“-Verzeichnis kopieren wir alle .h-Dateien. Das ist insgesamt keine schöne Methode, wird im Lua-Manual jedoch so beschrieben und reicht fürs Erste.

Über Projekt->Neuer Ordner fügen wir dem „Projektmappen-Explorer“ (Solution-Explorer) einen Ordner „Lua“ hinzu. Auf den neuen Ordner rechtsklicken, Hinzufügen->Vorhandenes Element hinzufügen… und dann alle
Dateien im Lua-Unterordner auswählen.

Jetzt sollte man das Projekt ohne Fehler erstellen können. Sind die W64-Warnung angeschaltet, gibt es im Lua-Code ein paar Warnung, diese kann man aber getrost ignorieren (noch arbeiten wir mit 32bit).


Es wird ernst:

Um Lua überhaupt verwenden zu können, müssen wir „lua.h“ includen. Da Lua eine C-Library ist, und das Projekt selbst C++, muss man dem Compiler das mitteilen. Das sieht dann so aus:


extern "C"
{
#include "lua/lua.h"
}

Wie jede grössere Library will Lua initialisiert werden. Dazu gibt es den Befehl lua_State* lua_open(); Dieser liefert ein Handle zurück, das für alle weiteren Lua-Befehle nötig ist. Dieses Handle beinhaltet die komplette
Umgebungsstruktur für Lua. Es ist ohne Probleme möglich, mehrere dieser lua_States gleichzeitig geöffnet zu haben. Um dieses Handle wieder freizugeben, gibt es den Befehl void lua_close( lua_State* ); Dadurch werden sämtliche Variablen und Objekte, die diese Lua-Instanz angelegt hat, freigegeben.

Unser Beispiel-Projekt, erweitert um Lua-Initialisierung und –Freigabe sieht nun so aus:


//LuaTutorial.cpp: Definiert den Einstiegspunkt für die Konsolenanwendung //

#include "stdafx.h"
extern "C"
{
#include "lua/lua.h"
}

// eine globale Lua-Instanz
lua_State* g_LuaInstance = NULL;

void init()
{
// eine Lua-Instanz wird initialisiert
g_LuaInstance = lua_open();
}

void done()
{
// die Lua-Instanz wird wieder freigegeben
lua_close( g_LuaInstance );
}

int _tmain(int argc, _Tchar* argv[])
{
Init();
// auf einen Tastendruck warten
char cDummy;
std::cin >> cDummy;
done();
return 0;
}


Es passiert doch gar nichts?

Richtig, im Moment tut unser Beispiel nichts. Lua wird initialisiert, ein Tastendruck auf Enter abgewartet und Lua wieder freigegeben. Lua hat jetzt nur grundlegendste Funktionalität, es reicht aber immerhin zum parsen und Befehle abarbeiten.

Für unser Beispielprogramm benötigen wir noch einige Zusatzbibliotheken, die aber schon mitgeliefert
werden. Dazu müssen noch zwei weitere Includes hinzugefügt werden, "lua/lualib.h"
und "lua/lauxlib.h"
Zusätzlich muss die Zusatzbibliothek noch an der Lua-Instanz angemeldet werden, damit die
Befehle verwendet werden können. Dazu rufen wir nach lua_open ein luaopen_base( g_LuaInstance );auf.

Übrigens, wer in den Source-Code von Lua sieht, wird feststellen, dass dieser Befehl nichts anderes tut, als C-Funktionen an Lua zu binden. Das machen wir im übernächsten Schritt dann selbst.

In diesem Schritt begnügen wir uns damit, eine Text-Datei mit Lua-Befehlen abzuarbeiten. Wir erstellen eine neue Text-Datei, und nennen diese zum Beispiel FirstLua.lua. In diese Text-Datei schreiben wir nur eine Zeile:

print "Hallo Welt";
Nach dem init()-Aufruf in unserem Programm lassen wir Lua nun diese Datei parsen und abarbeiten. Dazu genügt eine Zeile, lua_dofile( g_LuaInstance, "firstlua.lua" );

Tadaa! Speichern. Erstellen, Starten.

Im Konsolen-Fenster sollte jetzt „Hallo Welt“ stehen und ein Druck auf Enter sollte das Programm wieder beenden.

Es gibt natürlich weitere Methoden, Lua-Befehle abzuarbeiten, es muss nicht aus einer Datei gelesen werden. Mit lua_dostring kann ein übergebener String als Programm abgearbeitet werden.


Der zweite Schritt:

Jetzt kommen wir zu dem eigentlich „Sinn“ von Scriptsprachen. Das Aufrufen von Programmfunktionen. Dazu muss man Lua die Funktionen und deren gewünschte Namen zur Verfügung stellen.

Lua erwartet C-Funktionen in einem bestimmten Format. Diese Funktionen erhalten einen Parameter (lua_State*) und geben ein int zurück. Wir fügen jetzt oberhalb unserer init()-Funktion folgende Zeilen ein:

int
VonLua( lua_State* L )
{
std::count << "Von Lua aufgerufen \n";
return 0;
}

Wir erhalten als Parameter in der Funktion einen Zeiger auf die Lua-Instanz, die die Funktion aufgerufen hat, und geben als Return-Wert die Anzahl der Parameter zurück, die wir auf den Lua-Stack (siehe später) geschoben haben; in unserem Fall 0.

Unterhalb des luaopen_base melden wir jetzt diese Funktion an unserer Lua-Instanz an:

lua_pushcfunction( g_LuaInstance, VonLua );
lua_setglobal( g_LuaInstance, "MeineFunktion" );

Wir schieben (pushen) einen Zeiger auf unsere Funktion auf den Lua-Stack
(dazu später mehr) und verpassen der Funktion den Namen „MeineFunktion“. Jetzt
fügen wir in unserer firstlua.lua-Datei noch einen Aufruf von MeineFunktion
ein:

MeineFunktion();
Speichern. Erstellen, Starten. In dem Konsolen-Fenster sollte jetzt ausser „Hallo Welt“ auch noch „Von
Lua aufgerufen“ stehen.


Damit lässt sich schon eine ganze Menge anfangen, aber wir sind noch
nicht am Ende. Wir würden ja auch gerne Variablen austauschen. Wie das geht?


Stacks, Variablen und Schiebung:

Lua arbeitet Stack-orientiert und hat auch sämtliche Funktionen darauf optimiert und getrimmt. Wenn man zum Beispiel Variablen übergibt, werden diese auf den Stack geschoben. Derselbe Vorgang mit Rückgabeparametern; diese werden ebenfalls auf den Stack geschoben. Ein riesiger Vorteil gegenüber normalen C/C++-Programmen: Im Gegensatz zu C/C++ ist man nicht auf einen Rückgabeparameter beschränkt, man kann beliebig viele Parameter auf den Stack packen. Ein weiterer Vorteil: Dadurch, dass die Anzahl der Parameter über den Stack festgelegt ist, sind sämtliche an Lua gebundenen C-Funktionen vom Aufbau her identisch. Man könnte diese alle zum Beispiel in eine gemeinsame Liste packen.


Der Nachteil des Ganzen: Die einzelnen Parameter muss man sich Stück für Stück vom Stack fischen. Lua stellt zur Stack-Bearbeitung eine ganze Reihe von Befehlen zur Verfügung. Die Anzahl der Parameter auf dem Stack erhält man über
lua_gettop( luaState* )
Man beachte, dass die Anzahl zur Laufzeit nicht fixiert ist, man erhält genau so viele Parameter über den Stack wie beim Aufruf der Funktion übergeben worden sind. Das kann auch variieren.

Um das Ganze sauber zu verarbeiten, sollte man auch den Stack von den Parametern befreien. Dazu verwenden wir den Befehl
lua_pop( luaState*, iAnzahlParameter );
Um sicher zu gehen, codieren wir die Anzahl nicht fest, sondern setzen lua_gettop ein. Das sieht so aus:
lua_pop( L, lua_gettop( L ) );
Für unseren Test gehen wir jetzt nicht genau auf die einzelnen Variablentypen der Parameter ein, sondern lassen uns alle Parameter als String (Text) ausgeben. Wir überprüfen, ob der Parameter überhaupt einen gültigen Wert hat, da der nil-Wert undefiniert ist. Dazu gibt es die Funktion
lua_isnil( luaState*, iStackIndex )
Um einen Parameter als String vom Stack zu holen (Zahlen und andere werden dabei automatisch in Text umgewandelt) benutzen wir die Funktion
lua_tostring( luaState*, -iStackIndex )
In unserem Beispiel bauen wir eine einfache Schleife, die alle Parameter prüft und, falls vorhanden, auf std::cout ausgibt. Das sieht dann so aus:

int VonLua( lua_State* L )
{
 int iParameterAnzahl = lua_gettop( L );
 std::cout << "Aufgerufen mit" << iParameterAnzahl << "Parametern. \n";
 while ( iParameterAnzahl )
{
std::string strParam = "";
 if ( !lua_isnil( L, -iParameterAnzahl ) )
 {
 strParam = lua_tostring( L, -iParameterAnzahl );
 std::cout << "Parameter:" << strParam.c_str() << "\n";
 } --iParameterAnzahl;
 }
 lua_pop( L, lua_gettop( L ) );
 std::cout << "Von Lua aufgerufen \n";
 return 0;
}

Für den Test erweitern wir unser FirstLua.Lua um folgende Zeile:
Meine Funktion( "Ich bin die erste Zeile", 17, "Ich bin das Ende." );
Speichern. Erstellen. Starten.

Unsere Funktion wird jetzt zweimal aufgerufen, einmal mit 0 Parametern,einmal mit dreien.

Unsere Möglichkeiten wachsen und wachsen. Aber noch sind wir nicht am Ende!

Jetzt wollen wir noch Werte zurückgeben! Das ist sogar noch einfacher, als die Werte vom Stack zu holen. Je nach Variablentyp verwenden wir

lua_pushstring
oder
lua_pushnumber

Es gibt noch einige Typen mehr (z.Bsp. boolean), aber für das Beispiel reichen diese beiden völlig aus.

Wir pushen zwei Parameter auf den Stack, und ändern den Return-Wert auf 2, um Lua die Parameteranzahl mitzuteilen. Unsere C-Funktion sieht jetzt so aus:

int VonLua( lua_State* L )
{
int
iParameterAnzahl = lua_gettop( L );
std::cout << "Aufgerufen mit" << iParameterAnzahl << "Parametern. \n";
 while ( iParameterAnzahl )
 {
 std::string strParam = "";
 if ( !lua_isnil( L, -iParameterAnzahl ) )
 {
 strParam = lua_tostring( L, -iParameterAnzahl );
 std::cout << "Parameter:" << strParam.c_str() << "\n";
 }
 --iParameterAnzahl;
 }
 lua_pop( L, lua_gettop( L ) );
 std::cout << "Von Lua aufgerufen \n";
 lua_pushstring( L, "Hurz" );
 lua_pushnumber( L, 42 );
 return 2;
}

Unser Script läuft zwar so, wie es im Moment aussieht, aber wir möchten ja unsere beiden Parameter auch sehen. Werden mehrere Parameter zurückgegeben, sieht der Aufruf im Script so aus:

ReturnWert1, ReturnWert2 = Funktion( Aufruf-Parameter );

Wir ändern unser Script ab und bauen auch eine Ausgabe ein:

print "Hallo Welt";
MeineFunktion();

Wert1, Wert2 = MeineFunktion( "Ich bin die erste Zeile", 17, "Ich bin das Ende." );
print( "Wert1: ", Wert1 );
print( "Wert2: ", Wert2 );

Auf die Gefahr der Wiederholung hin:
Speichern. Erstellen. Starten. Hurrrrra, Kasperl!


Damit lässt sich jetzt schon sehr viel machen. Weiterführende Schritte wären jetzt tatsächlich Objekte ansprechen bzw. fortgeschrittene Themen wie coroutines, eine Art Threads im Script.


Vielen Dank an Georg Rottensteiner für dieses Tutorial!







Kommentare
kurdt am 03. April 2008 22:00
Vllt eine kleine Korrektur, seit der neusten Version geht es nicht mehr:
lua_dostring(); sondern
luaL_dostring().

Dies aber nur so beim kurzen überfliegen.
androphinx am 14. Mai 2008 14:39
die restlichen tutorials gibts übrigens hier:
http://gamedev.ch/index.php?name=Search&action=search&active_stories=1&stories_author=&stories_cat%5B%5D=&bool=OR&q=lua&startnum=1&total=13
nemesis am 24. November 2008 19:48
ich habe ein problem und zwar 1:es gibt keine version 5 für windows sondern 5.1.4 und wenn ich das aufmache kommt son fenster mit copiright lua....was muss ich tun?
nemesis am 24. November 2008 19:49
ich habe ein problem und zwar 1:es gibt keine version 5 für windows sondern 5.1.4 und wenn ich das aufmache kommt son fenster mit copiright lua....was muss ich tun?
kurdt am 28. November 2008 14:27
Beschreib deinen "Fehler" doch mal genauer ... was machst du wo auf, mit was ... mi ma mu.
So kann dir wohl keiner mehr helfen.
Kommentar schreiben
Bitte einloggen, um einen Kommentar zu schreiben.
Bewertung
Die Bewertung ist nur für Mitglieder verfügbar.

Bitte loggen Sie sich ein oder registrieren Sie sich, um abzustimmen.

Keine Bewertung eingesandt.
Login
Username

Passwort



Noch kein Mitglied?
Klicke hier um dich zu registrieren.

Passwort vergessen?
Fordere Hier ein neues an

Letzer Artikel
01 - Creating a 2D G...
Quellcode-Dokumentat...
IsoGuide Teil 2
C++ Basics
Teil 3: Serie zur En...

Letzten Kommentare
Artikel Kommentar Joa, des Wochenende ...
Artikel Kommentar Ist wohl nix geworden ...
News Kommentare Achso, yo dann, dachte...
News Kommentare Bei der CryEngine3 is ...
News Kommentare Ja da werden sich die ...

Mitgliederstimme
Welche Programmier-/Scriptsprache benutzt du für dein derzeitiges Projekt?

C/C++

C#

Basic

Actionscript

PHP

Pascal/Delphi

Java

Andere

Du musst dich einloggen, um zu voten.

Partner
GameDev.de
GFX-Sector - Your GFX-Source No. 1
Partner werden!

 
Copyright © 2008 Isogames.de

604818 Eindeutige Besuche