Szymon Jabłoński M1ISI
Głównym celem stosowania języków skryptowych jest możliwość ingerencji w istniejący system(np. silnik gry) bez konieczność znajomości i ingerencji w kod źródłowy. Przykładami języków skryptowych są np. Bash, Python, Perl, PHP, UnrealScript czy interesująca nas Lua.
Języki skryptowe znalazły swoje zastosowanie w procesie tworzenia gier komputerowych np. w agorytmach sztucznej inteligencji(sterowanie postaciami Non-Playable Characters), obsługi interfejsu gry czy sterowaniem przebiegiem fabuły(np. dialogi). Jednym z najczęściej wybieranym językiem okazał się język Lua. Zastosowany został w takich tytułach jak: Crysis, FarCry, Baldur's Gate, World of Warcraft czy nasz rodzimy Wiedźmin.
Innym popularną aplikacją wykorzystującą język Lua jest Adobe Lightroom, którego ok. 40% kodu źródłowego napisanego jest w Lua. Pełna lista gier i programów wykorzystujących Lua dostępna jest na stronie http://www.lua.org/uses.html
Lua jest językiem skryptowym łączącym ze sobą elementy języka proceduralnego o składni zbliżonej do języka Pascal, rozszerzonego o bardziej złożone konstrukcje opisu danych opartych na tablicach asocjacyjnych. Język został zaprojektowany w celu rozszerzenia funkcjonalności aplikacji, często jednak jest wykorzystywany jako samodzielny język. Lua jest projektem open-source zaimplementowanym w języku C zgodnie ze standardem ANSI C,którego główną zaletą jest prostota, wydajność i przenośność kodu.
W celu rozpoczęcia pracy z językiem Lua musimy pobrać ze strony http://luabinaries.sourceforge.net/download.html skompilowane wersje bibliotek dla naszego systemu operacyjnego(do wyboru Windows, Linux oraz MacOS). Istnieje również możliwość pobrania kodu źródłowego i samodzielnej kompilacji : http://www.lua.org/ftp/.
Interesującymi projektami są również Lua for Windows : http://code.google.com/p/luaforwindows/, Lua for Linux: http://luaforge.net/projects/luaforunix/ oraz LuaRocks: http://www.luarocks.org/. Poza binarkami zawierają również edytory do tworzenie i debugowania skryptów Lua.
Strona ma na celu ukazanie sposobów i możliwości integracji języków C++ oraz Lua, jednak przedstawię krótko podstawy języka Lua wraz z przykładami. W celu dogłębnego poznania składni i możliwość języka polecam dokumentacje umieszczoną na stronie internetowej.
Na początek standardowy Hello World (Listing 1)
Listing 1 - program Hello World w Lua
-- HelloWorld.lua print("Hello from Lua!"); --[[ Przykład komentarza umieszczonego w kilku liniach ]]
W Lua podobnie jak w innych językach skryptowych nie deklarujemy typów zmiennych. Zamiast tego każda zmienna ma dynamiczny typ określany przez jej wartość.
Istnieje 8 podstawowych typów danych:
Ciekawym mechanizmem języka jest poleceniem type pozwalające na sprawdzenie typu zmiennej. Przykład pokazano na Listingu 2.
Listing 2 - przykład polecenia type w Lua.
print(type(a)) --> nil (zmienna 'a' nie została zainicjalizowana) a = 10 print(type(a)) --> number a = print a(type(a)) --> function
W Lua dostępne są następujące operatory:
Lua posiada standardowe instrukcje znane z języków Pascal/C:
Listing 3 - przykład stosowania funkcji i warunków logicznych, funkcja rekurencyjna obliczająca silnie.
-- obliczenie n! function factorial (n) if n == 1 then return 1; else return n * factorial(n-1); end end -- obliczenie 5! print(factorial(5)); --> 120
Najistotniejszymi strukturami danych w Lua są tablice asocjacyjne reprezentowane przez kolekcję par (klucz, wartość). Kluczem może być wszystko poza wartością nil. Elementami tablicy mogą być wszystkie wartości podstawowych typów Lua. Oznacza to, że również mogą to być omawiane w tym miejscu Tablice. Pozwala to na tworzenie takich struktur jak lista, wektor, kolejka czy stos.
Listing 4 pokazuje przykłady wykorzystania tablic w Lua realizując proste zbiory danych, listę jednokierunkową, czy strukturę zawierającą zarówno atrybuty jak i metody.
Listing 4 - przykład wykorzystania tablic do realizacji zbiorów danych.
-- prosty wektor liczb squares = {1, 4, 9, 16, 25, 36, 49, 64, 81} -- zbiór danych creature = { name = "Ghoul", health = 100 } print(creature["name"]); --> Ghoul print(creature.name); --> Ghoul -- lista list = nil; list = {next = list, value = 2}; list = {next = list, value = "test"}; list = {next = list, value = math.pi}; ptr = list; while ptr do print(ptr.value); ptr = ptr.next; end; --> 3.141592 test 2 -- struktura/klasa Vec3 = {}; Vec3.new = function(x, y, z) return {x=x, y=y, z=z, length=function() return math.sqrt(x*x+y*y+z*z) end;} end; v = Vec3.new(4, 2, 4); print(v:length()); --> 6
Lua jak każdy porządny język, wyposażona została w bogatą standardową bibliotekę zawierającą bogaty zestaw narzędzi wyższego poziomu. Należą do niej następujące moduły:
Poniższy kod pokazuje kilka przykładów wykorzystania biblioteki standardowej.
Listing 5 Wykorzystanie biblioteki standardowej Lua
-- losowanie liczb math.randomseed(os.time()) print(math.random(10)); -- wyświetlenie liczby wylosowanej z przedziału [1,10] -- obliczenie wartości cos(pi/5) print(math.cos(math.pi*0.2)); -- zapis do pliku f = assert(io.open("output.txt", "w")) f:write("test"); f:close();
Zapoznaliśmy się już z podstawami języka Lua, część przejść do najważniejszej sprawy czyli integracji z językiem C++.
W celu rozpoczęcia pracy z Lua musimy wykonać dwie następujące operacje:
Prosty przykład ukazujący wspomniane operacje i wywołujący z poziomu kodu C++ skrypt Lua pokazany jest na Listingu 6.
Listing 6 Inicjalizacja maszyny wirtualnej Lua i wywołanie skryptu Lua z poziomu kodu C++
#include <iostream> // dołączenie nagłówków Lua extern "C" { #include "lua.h" #include "lualib.h" #include "lauxlib.h" } int main() { // utworzenie maszyny wirtualnej Lua. lua_State* L = lua_open(); // inicjalizacja standardowych bibliotek luaL_openlibs(L); // wczytanie skryptu Lua if (luaL_dofile(L, "zpr.lua")) { // Walidacja wcztania skryptu std::cout << "Error while running script: " << lua_tostring(L, -1) << std::endl; return 1; } // zamknięcie maszyny wirtualnej lua_close(L); return 0; }
Potrafimy już z poziomu kodu C++ zainicjalizować maszynę wirtualną Lua i uruchomić skrypt napisany w Lua. Przejdźmy krok dalej - spróbujmy wywołać funkcję zdefiniowaną w skrypcie Lua z poziomu kodu C++. Zacznijmy od przykładowej funkcji obliczającą różnicę dwóch liczb.
Listing 7 Funkcja Lua obliczającą różnicę dwóch liczb.
--sub.lua function(x,y) return x - y end
Aby umożliwić wywoływanie funkcji ze skryptu Lua musimy zdefiniować kodzie C++ funkcję, w której odłożymy na stos parametry funkcji, a następnie zdejmiemy ze stosu wartość zwracaną przez funkcję. Ilustruje to poniższy przykład:
Listing 8 Przykład wywołania funkcji Lua w kodzie C++.
#include <iostream> // dołączenie nagłówków Lua extern "C" { #include "lua.h" #include "lualib.h" #include "lauxlib.h" } // Interpretator Lua lua_State* L; // Funkcja wywołującą funkcję ze skryptu Lua i zwracająca otrzymany wynik int luaSub(int x, int y) { int sub = 0; // nazwa funkcji w pliku sub.lua lua_getglobal(L, "sub"); // pierwszy argument lua_pushnumber(L, x); // drugi agrument lua_pushnumber(L, y); // wywołanie funkcji z dwoma argumentami i jedną wartością zwracaną lua_call(L, 2, 1); // pobranie wyniku i zdjęcie ze stosu sub = static_cast<int>(lua_tointeger(L, -1)); lua_pop(L, 1); return sub; } int main() { int sub = 0; // utworzenie maszyny wirtualnej Lua. L = lua_open(); // inicjalizacja standardowych bibliotek luaL_openlibs(L); // wczytanie skryptu Lua if (luaL_dofile(L, "sub.lua")) { // Walidacja wczytania skryptu std::cout << "Error while running script: " << lua_tostring(L, -1) << std::endl; return 1; } // wywołanie funkcji sub = luaSub( 10, 15 ); std::cout << "Substraction: " << sub << std::endl; // zamknięcie maszyny wirtualnej lua_close(L); return 0; }
Spróbujmy zrobić wykonać to samo zadanie odwrotni. Wywołajmy funkcję napisaną w C++ w kodzie Lua. Funkcje w Lua są wywoływane używając wskaźnika na funkcję:
typedef int (*lua_CFunction) (lua_State *L);
Wynika z tego, że funkcje przyjmują jako argument interpretator Lua i zwracają wartość int. Napiszmy funkcję w C++, która będzie liczyła sumę liczb przekazanych do funkcji, których liczba będzie zmienna:
Listing 9 Funkcja liczącą sumę podanych liczb w C++ wywoływana z poziomu kodu Lua.
#include <iostream> // dołączenie nagłówków Lua extern "C" { #include "lua.h" #include "lualib.h" #include "lauxlib.h" } // Interpretator Lua lua_State* L; // Funkcja przygotowana do wywołania w kodzie Lua static int suma(lua_State *L) { // pobranie liczby argumentów int n = lua_gettop(L); int suma = 0; // sumowanie argumentów for (int i = 0; i < n; ++i) suma += lua_tonumber(L, i); // zwrócenie sumy lua_pushnumber(L, suma) // zwracamy ilość wartości zwracanych przez funkcję return 1; } int main() { int sub = 0; // utworzenie maszyny wirtualnej Lua. L = lua_open(); // inicjalizacja standardowych bibliotek luaL_openlibs(L); // rejestracja funkcji suma pod nazwą f_suma lua_register(L, "f_suma", suma); // wczytanie skryptu Lua if (luaL_dofile(L, "zpr.lua")) { // Walidacja wczytania skryptu std::cout << "Error while running script: " << lua_tostring(L, -1) << std::endl; return 1; } // zamknięcie maszyny wirtualnej lua_close(L); return 0; }
Listing 10 Przykład wywołania w skrypcie Lua funkcji napisanych w C++
--zpr.lua suma = f_suma(10, 20, 30, 40, 50) print("Suma wynosi: ", suma)
http://www.lua.org - strona domowa Lua.
http://www.lua.org/manual/5.1 - dokumentacja on-line Lua.
http://lua-users.org/wiki/ - zbiór artykułów, dodatków i przykładów Lua
http://www.inf.puc-rio.br/~roberto/pil2/ - programowanie w Lua
http://code.google.com/p/luaforwindows/ - biblioteki i narzędzi dla systemu Windows.
http://luaforge.net/projects/luaforunix/ - biblioteki i narzędzi dla systemu Unix.
http://www.luarocks.org/ - deployment and management system for Lua modules.