Podstawy języka skryptowego Lua Wykorzystanie Lua w C++ Krzysztof Rossa - digiboy rbox_usuń_@o2.pl 2007-07-29 Kontakt z autorem: rbox_usun_@o2.pl
Zawartość Wstęp... 3 Po co używamy skryptów?... 3 Przygotowanie środowiska... 3 Pierwszy program... 3 Wywoływanie funkcji Lua w kodzie C++... 5 Wywoływanie funkcji C++ w kodzie Lua... 7 Strona 2 z 9
Wstęp Lua jest językiem skryptowym pierwotnie zaprojektowanym dla rozszerzenia funkcjonalności różnych aplikacji, jednak często jest wykorzystywany, jako samodzielny język. Język ten charakteryzuje się prostą składnią. Język zaimplementowany jest, jako mała biblioteka napisana w języku C. Celami implementacji są: prosta, wydajność i przenośność kodu. W artykule opierać się będę Lua w wersji 5.1.2. Po co używamy skryptów? Skrypty często są używane przez twórców gier, gdzie mogą zostać wykorzystane do zaprogramowania sztucznej inteligencji, logiki gry, GUI, systemu zdarzeń. Gra, wykorzystująca skrypty umożliwia łatwą rozbudowę poprzez dodawanie skryptów lub ich modyfikowanie. Wystarczy przytoczyć takie tytuły jak: FarCry, World of Warcraft, które korzystają właśnie z języka Lua. Pełna lista gier i programów (np. nowy program Adobe Lightroom, gdzie podobno 40% kodu zostało napisane w Lua) wykorzystująca język skryptowy Lua jest dostępna pod adresem http://www.lua.org/uses.html. Inne gry wykorzystują inne gotowe języki skryptowe lub piszą własne bardziej dostosowane do własnych potrzeb, np. UnrealScript wykorzystywany w silniku Unreal Engine 2 czy Unreal Engine 3. Przygotowanie środowiska Aby rozpocząć pracę z Lua musimy zaopatrzyć się z skompilowane wersje biblioteki do ulubionego kompilatora. Istnieje też możliwość pobrania kodu źródłowego i samemu go skompilowania. W zależności od środowiska instalacja bibliotek jest różna. W artykule kod jest przystosowany do środowiska Visual Studio 2005 Pierwszy program Zgodnie z tradycją napiszemy program wyświetlający napis Hello World. Za początku musimy dołączyć pliki nagłówkowe języka skryptowego Lua. Ponieważ Lua jest napisana w C, musimy poinformować o tym fakcie kompilator słowem kluczowym extern "C". Następnie definiujemy wskaźnik na interpretator Lua. Teraz już zostało nam utworzyć obiekt interpretatora poprzez wywołanie funkcji: L = lua_open(); Kiedy obiekt Lua jest nam już nie potrzebny możemy do usunąć z pamięci za pomocą wywołania: lua_close(l); Strona 3 z 9
Aby uruchomić skrypt znajdujący się w pliku zewnętrznym wywołujemy funkcję: lual_dofile(l, "plikskryptu"); main.cpp #pragma comment (lib,"lua5.1.lib") // zaimportowanie biblioteki Lua #include <iostream> using namespace std; extern "C" #include "lua.h" #include "lualib.h" #include "lauxlib.h" /* interpretator Lua */ lua_state* L; int main ( int argc, char *argv[] ) /* inicjalizacja Lua */ L = lua_open(); /* wczytanie głównych bibliotek Lua */ lual_openlibs(l); /* uruchominie skryptu znajdującego się w pliku test.lua */ lual_dofile(l, "test.lua"); /* zamkniecie Lua */ lua_close(l); system("pause"); return 0; test.lua print "Hello World" Strona 4 z 9
Rysunek 1 Wynik działania programu Wywoływanie funkcji Lua w kodzie C++ Teraz zajmiemy się rzeczami ciekawszymi. Zdefiniujemy sobie funkcję w skrypcie Lua i będziemy wywoływać ją z kodu C++. Definiowanie funkcji w Lua jest bardzo proste. Funkcje w Lua mogą przyjmować wiele parametrów i wiele parametrów zwracać. Do tego celu używają stosu. Prosta funkcja licząca sumę dwóch liczb będzie miała postać: test.lua function add ( x, y ) początek funkcji return x + y end -- koniec funkcji Aby umożliwić wywoływanie funkcji ze skryptu Lua musimy zdefiniować kodzie C++ funkcję o dowolnej nazwie i w której odłożymy na stos parametry funkcji, a następnie zdejmiemy ze stosu wartość zwracaną z funkcji main.cpp #pragma comment (lib,"lua5.1.lib") // zaimportowanie bibliteki Lua #include <iostream> using namespace std; extern "C" #include "lua.h" #include "lualib.h" #include "lauxlib.h" /* interpretator Lua */ lua_state* L; Strona 5 z 9
int luaadd ( int x, int y ) int sum; /* nazwa funkcji w pliku test.lua */ lua_getglobal(l, "add"); /* pierwszy argument */ lua_pushnumber(l, x); /* drugi agrument */ lua_pushnumber(l, y); /* wywołanie funkcji z dwoma argumentami i jedną wartościa zwracaną */ lua_call(l, 2, 1); /* pobranie wyniku, zdjęcie ze stosu */ sum = (int)lua_tointeger(l, -1); lua_pop(l, 1); return sum; int main ( int argc, char *argv[] ) int sum; /* inicjalizacja Lua */ L = lua_open(); /* wczytanie głownych bibliotek Lua */ lual_openlibs(l); /* wczytanie skryptu znajdującego się w pliku test.lua */ lual_dofile(l, "test.lua"); /* wywołanie funkcji */ sum = luaadd( 10, 15 ); /* wyświetlenie wyniku */ cout << "Suma: " << sum << endl; /* zamkniecie Lua */ lua_close(l); system("pause"); return 0; Strona 6 z 9
Rysunek 2 Wynik działania programu Wywoływanie funkcji C++ w kodzie Lua Teraz zajmiemy się sytuacją odwrotną. Wywołamy funkcję z kodu C lub C++ w skrypcie Lua. Funkcje w Lua są wywoływane używając wskaźnika na funkcję: typedef int (*lua_cfunction) (lua_state *L); Oznacza to tyle, że funkcje przyjmują jako jeden argument tylko interpretator Lua i zwracają tylko wartość całkowitą int. My napiszemy funkcję w C++, która będzie nam liczyła średnią liczb przekazanych do funkcji, a ilość będzie zmienna. Funkcją tą będzie: static int srednia(lua_state *L) Na samym początku funkcji w lini int n = lua_gettop(l); odczytujemy ilość argumetów podanych do funkcji. Następnie w pętli sumujemy wartość agrumentów. W kolejnych liniach odkładamy na stos średnią, a następnie sumę. Jako wartość zwracaną zwracamy liczbę argumentów jaką odłożyliśmy na stos, w naszym przypadku 2. Strona 7 z 9
main.cpp #pragma comment (lib,"lua5.1.lib") // zaimportowanie bibliteki Lua #include <iostream> using namespace std; extern "C" #include "lua.h" #include "lualib.h" #include "lauxlib.h" /* interpretator Lua */ lua_state* L; static int srednia(lua_state *L) /* pobranie liczby argumentów */ int n = lua_gettop(l); double sum = 0; int i; /* sumowanie agrumentów */ for (i = 1; i <= n; i++) /* suma argumentów */ sum += lua_tonumber(l, i); /* zwrocenie sredniej */ lua_pushnumber(l, sum / n); /* zwrocenie sumy */ lua_pushnumber(l, sum); /* zwracamy ilosc wartosci zwracanych przez funkcję */ return 2; int main ( int argc, char *argv[] ) /* inicjalizacja Lua */ L = lua_open(); /* wczytanie głownych bibliotek Lua */ lual_openlibs(l); /* rejestracja funkcji srednia pod nazwa f_srednia */ lua_register(l, "f_srednia", srednia); /* uruchomienie skryptu */ lual_dofile(l, "test.lua"); /* zamkniecie Lua */ lua_close(l); system("pause"); return 0; Strona 8 z 9
test.lua srednia, sum = f_srednia(10, 20, 30, 40, 50) print("srednia wynosi: ", srednia) print("suma wynosi ", sum) Rysunek 3 Wynik działania programu Strona 9 z 9