Artikkelit

Artikkelit olivat Suomipelit.comissa sivuston sulkeuduttua. Näin ollen niihin saattaa sisältyä rikkinäisiä linkkejä tai epämääräisiä viitteitä asioihin, joita ei enää ole olemassa, ja joskus jokin saattaa näkyä väärin.

Palaa artikkelilistaan

LUA:n alkeet

LUA on tehokas, pienet muistivaatimukset omaava ohjelmointikieli. Sitä on käytetty skriptikielenä peleissä kuten Grim Fandago, Baldur's Gate ja MDK2. Tämä artikkeli tutustuttaa lukijan kieleen lyhyesti ja kertoo kuinka LUA saadaan käyttöön omissa projekteissa.

8.3.2004 julkaistun artikkelin on kirjoittanut jarkko parviainen.

  1. Mikä on LUA?
  2. LUA-tulkin luominen
  3. Kielen alkeet
  4. Omien C-funktioiden kutsumiseen LUAn kautta
  5. Loppusanat

Mikä on LUA? #

LUA on tehokas, pienet muistivaatimukset omaava ohjelmointikieli, joka on suunniteltu ohjelmien toiminnallisuuden laajentamiseen. Toisin sanoen LUA on pääasiassa skriptikieli, mutta sillä voi tehdä myös omia itsenäisiä ohjelmia.

LUA on toteutettu pienenä C-funktiokirjastona ja on kirjoitettu ANSI C:llä. Tämän ansiosta se toimii useilla eri käyttöjärjestelmillä, kuten Windows, Windows CE, Linux, BeOs ja niin edelleen. Pienten muistivaatimustensa ansiosta sitä voi käyttää myös sulautetuissa järjestelmissä (esimerkiksi matkapuhelimet).

Aivan kuten Java, myös LUA toimii virtuaalikoneen kautta ja tulkitsee tavukoodia. Yhtenäisyydet loppuvat kuitenkin tähän, koska LUA on pyritty pitämään mahdollisimman yksinkertaisena ja pienet laitteistovaatimukset omaavana kielenä.

Jotta kieleen voisi tutustua mahdollisimman nopeasti, aloitamme artikkelin luomalla oman LUA-tulkin. Pidemmittä puheitta, siirtykäämme ohjelmoimaan!

(Artikkelin esimerkkiohjelmat voit ladata tästä http://www.suomipelit.com/files/artikkel... )

LUA-tulkin luominen #

Ennen kuin voimme aloittaa varsinaisen ohjelmoinnin, meidän tulee luoda projekti joka käyttää LUAa. Tarvitsemme siis joitakin tiedostoja www.lua.org -sivustoilta saatavasta LUA-jakelupaketista. LUAn käyttöönottaminen ei kuitenkaan vaadi koko jakelupaketin hankkimista, tiedostot lua.h, lualib.h, lauxlib.h sekä lua.lib, lualib.lib, lualib_dbg.lib (debuggausta varten) riittävät. Kyseiset tiedostot tulevat ensimmäisen esimerkkiohjelman mukana.

H-päätteiset tiedostot lisätään LUA-funktioita kutsuviin lähdekooditiedostoihin. LIB-päätteiset tiedostot lisätään projektin käyttämiin kirjastotiedostoihin. Visual C++ 6.0:ssa tämä tapahtuu seuraavasti (oletetaan että projekti on jo luotu):


  • Mene valikkoon Tools->Options...
  • Avaa Directories-välilehti
  • Lisää kohtaan Include Files hakemisto, jossa lua.h-tiedosto sijaitsee
  • Lisää kohtaan Library Files hakemisto,
  • jossa lua.lib-tiedosto sijaitsee
  • Paina OK


Kun yllä mainitut asiat on tehty, olemme valmiit ensimmäisen ohjelman tekemiseen.


Ensimmäinen ohjelma

Aivan ensimmäinen toimenpide LUAn käyttöön otossa on LUA-virtuaalikoneen luominen lua_open()-funktiolla. Tämän jälkeen valitaan käyttöön otettavat LUA-kirjastot, nyt otamme käyttöön vain peruskirjaston lua_baselibopen()-funktiolla (funktio ottaa parametrinaan käytössä olevan LUA-virtuaalikoneen osoitteen). Esimerkissä tulkittava LUA-koodi ladataan tiedostosta skripti1.lua funktiolla lua_dofile() (HUOM! Tiedostonimen päätteen EI tarvitse olla .lua, nyt se on käytössä vain esimerkin vuoksi). Funktio ottaa parametreinaan LUA-virtuaalikoneen osoitteen sekä avattavan tiedoston nimen. Lopuksi virtuaalikone suljetaan lua_close()-funktiolla, joka ottaa parametrinaan (jälleen kerran) virtuaalikoneen osoitteen.

Esimerkin C++-koodi:
//---------------------------------------------
// Nimi  : Main.cpp
// Kuvaus: Ensimmäisen LUA-projektin koodi.
//---------------------------------------------
#include <conio.h> //getch()
extern "C"
{
#include <lua.h>
#include <lualib.h>
}



//---------------------------------------------
// Nimi  : main()
// Kuvaus: Ohjelmakoodi
//---------------------------------------------
int main( void )
{
	//
	// Luodaan LUA-virtuaalikone.
	//
	lua_State* pLUAVM = lua_open(0);
	if( pLUAVM == 0 )
		return -1;

	//
	// Otetaan LUAn peruskirjastot käyttöön.
	//
	lua_baselibopen( pLUAVM );

	//
	// Luetaan ja tulkitaan LUA-skripti.
	//
	lua_dofile( pLUAVM, "skripti1.lua" );

	//
	// Lopuksi suljetaan LUA-virtuaalikone.
	//
	lua_close( pLUAVM );

	//
	// Odotetaan napin painallusta ennen poistumista.
	//
	getch();
	return 1;
}



Ensimmäinen LUA-skripti on yksinkertainen Terve Maailma -ohjelma. LUAssa tekstin tulostaminen tehdään print-käskyn avulla, aivan kuten C-kielessäkin. C-kielen tyyliin LUA-koodirivin perässä voisi olla puolipiste (;), mutta sitä ei vaadita.

Skripti1.lua-tiedoston koodi:
--
-- Ensimmäinen skripti.
-- Kuten huomaat, "--"-merkit tarkoittaa
-- kommenttiriviä.
--
print "Terve maailma!!!"


Ensimmäisen LUA-ohjelman tulosteEnsimmäisen LUA-ohjelman tuloste



Näin olemme päässeet alkuun. Tutustutaan seuraavaksi tarkemmin itse kieleen.

Kielen alkeet #

Nyt kun olemme toteuttaneet toimivan LUA-tulkin, voimme perehtyä tarkemmin itse kielen saloihin.


Muuttujat ja niiden tyypit

Muuttujat ovat LUAssa tyypittömiä, mikä tarkoittaa että yksi muuttuja voi sisältää minkä tahansa tyyppisen arvon. Lisäksi muuttujaa ei tarvitse esitellä ennen kuin sille asetetaan arvo. Toisin sanoen koodissa

kirja = "Moby Dick"
kirja = 1
kirja = 0.121

muuttuja kirja esitellään samalla kun sille asetetaan arvo "Moby Dick". Kirja-muuttujan tyyppi ei kuitenkaan ole asettunut kiinteäksi, vaan sen arvoksi voidaan asettaa lähes mitä tahansa.

Numeromuuttujien arvoja voidaan muokata perinteisin matemaattisin operaatioin, kuten yhteen-, vähennys-, kerto- ja jakolasku. Useimmista muista kielistä poiketen LUAssa on vain yksi numerotyyppi: kaksinkertaisen tarkkuuden liukuluku (double). Kaikki laskutoimitukset suoritetaan siis liukulukuina.


Kontrollirakenteet

LUA toteuttaa perinteiset kontrollirakenteet (if-elseif-else, while, for, jne), mitkä ovat varmasti useimmille tuttuja jo muiden ohjelmointikielten kautta. Skripti2.lua (koodi esitellään alempana) näyttää lyhyesti kuinka kyseisiä kontrollirakenteita käytetään LUAssa. Alla kontrollirakenteiden kuvaukset. Niissä kohta ehto voi olla jokin seuraavista:
  <  pienempi kuin
  >  suurempi kuin
  <= pienempi tai yhtäsuuri kuin
  >= suurempi tai yhtäsuuri kuin
  == yhtäsuuri kuin
  ~= erisuuri kuin



IF-lause
if <ehto> then
    <koodia>
end

if <ehto> then
    <koodia>
else 
    <koodia>
end

if <ehto> then
    <koodia>
elseif <ehto> then
    <koodia>
else
    <koodia>
end


WHILE-silmukka
while <ehto>
do
    <koodia>
end


FOR-silmukka
for <iterointialue>
do
    <koodia>
end


REPEAT-silmukka
repeat 
    <koodia>
until <ehto>



Skripti2.lua-tiedoston koodi:
--
-- Toinen skripti.
-- Kontrollirakenteet.
--


--
-- IF-lause.
--
print "IF-lause"
i = 2
if i == 2 then
    print "i on kaksi"
elseif i == 3 then
    print "i on kolme"
else
    print "i on jotain muuta"
end
print "---------"

--
-- WHILE-silmukka.
--
print "While-silmukka"
i = 0
while i < 10
do
    print(i)
	i = i + 1
end
print "---------"

--
-- WHILE-silmukka, ja poistuminen
-- siitä BREAK-komennolla.
--
print "While-silmukka, poistuminen breakilla"
i = 0;
while i < 10 
do
    print(i)
	if i > 5 then
	    break
    end
	i = i + 1
end
print "---------"


--
-- FOR-silmukka, lasketaan yhdestä kymmeneen.
--
print "FOR-silmukka"
for laskuri = 1,10 do
    print(laskuri)
end
print "---------"


--
-- Repeat-toistosilmukka. 
--
i = 0
repeat 
   print(i)
   i = i + 1
until i == 10



Osa skriptin 2 tulosteestaOsa skriptin 2 tulosteesta


Nyt kun kielen perusrakenteet ovat selvillä, voimme siirtyä LUAn käytön kannalta mielenkiintoisimpaan osioon: omien C-funktioiden kutsumiseen LUAn kautta.

Omien C-funktioiden kutsumiseen LUAn kautta #

LUAn tehokkuus kielenä perustuu sen kykyyn kutsua C-funktioita skriptikielen kautta. Tällä tavoin LUAn toiminnallisuutta voidaan helposti laajentaa halutunlaiseksi.

C-funktiot liitetään LUAan rekisteröimällä ne luodulle LUA-virtuaalikoneelle. Tämä tapahtuu lua_register()-funktiolla. Funktio ottaa parametrinaan käytetyn virtuaalikoneen osoittimen, merkkijonon jossa kerrotaan millä nimellä haluttua funktiota kutsutaan LUA-skripteissä sekä osoittimen C-funktioon. Kaikkien rekisteröitävien C-funktioiden on oltava muotoa int FunktionNimi( lua_State* pLUAVirtuaaliKoneOsoitin ).


Esimerkkiohjelma 2

Toinen LUA-esimerkkiohjelma kutsuu kahta eri C-funktiota LUA-skriptin kautta. Ensimmäinen funktio, nimeltään OmaFunktio, ei vaadi lainkaan parametreja. Toinen funktio, nimeltään ToinenOmaFunktio, sen sijaan vaatii 2 parametria. Parametrit välitetään funktiolle LUAn sisältämien komentojen avulla. Yleisimmät parametrien välitykseen käytetyt LUA-funktiot:

[DEFTABLE]
[D]lua_tonumber()[/D][E]Palauttaa numeron (aina tyyppiä DOUBLE)[/E]
[D]lua_tostring()[/D][E]Palauttaa merkkijonon[/E]
[D]lua_toboolean()[/D][E]Palauttaa boolean-arvon (tosi/epätosi)[/E]
[/DEFTABLE]

Kaikki yllä olevista funktioista ottavat ensimmäiseksi parametrikseen käytetyn LUA-virtuaalikoneen osoittimen ja toiseksi parametriksi halutun parametrin numeron. Eli ensimmäinen numeroparametri haettaisiin käskyllä lua_tonumber( pLUAVM, 1 ) ja toisena tuleva merkkijonoparametri käskyllä lua_tostring( pLUAVM, 2 ).


Toisen esimerkkiohjelman koodi:

//---------------------------------------------
// Nimi  : Main.cpp
// Kuvaus: Toisen LUA-projektin koodi.
//---------------------------------------------
#include <stdio.h> //printf()
#include <conio.h> //getch()
extern "C"
{
#include <lua.h>
#include <lualib.h>
}


//
// Globaali muuttuja, jonka arvoa muokataan
// LUAn kautta.
//
int g_iMuuttuja;


//---------------------------------------------
// Nimi  : OmaFunktio()
// Kuvaus: Oman funktion koodi.
//---------------------------------------------
int OmaFunktio( lua_State* pLUAVM )
{
	//
	// Lisätään muuttujan arvoa ja tulostetaan
	// lopputulos ruudulle.
	//
	++g_iMuuttuja;
	printf( "OmaFunktio kutsuttu LUAsta! Muuttujan arvo %i\n", g_iMuuttuja );
	return 0;
}



//---------------------------------------------
// Nimi  : ToinenOmaFunktio()
// Kuvaus: Toisen oman funktion koodi. Nyt
//         funktiolle välitetään LUA:n kautta
//         parametreja.
//---------------------------------------------
int ToinenOmaFunktio( lua_State* pLUAVM )
{
	//
	// Luetaan parametrit virtuaalikoneelta.
	//
    int   iLuku = (int)lua_tonumber( pLUAVM, 1 );
	float fLuku = (float)lua_tonumber( pLUAVM, 2 );

	//
	// Tulostetaan saadut tiedot.
	//
	printf( "ToinenOmaFunktio kutsuttu LUAsta!\n" );
	printf( "Parametri1 = %i, parametri2 = %f\n", 
		    iLuku, fLuku );
	return 0;
}



//---------------------------------------------
// Nimi  : main()
// Kuvaus: Ohjelmakoodi
//---------------------------------------------
int main( void )
{
	//
	// Nollataan muokattava muuttuja.
	//
	g_iMuuttuja = 0;

	//
	// Luodaan LUA-virtuaalikone.
	//
	lua_State* pLUAVM = lua_open(0);
	if( pLUAVM == 0 )
		return -1;

	//
	// Otetaan LUAn peruskirjastot käyttöön.
	//
	lua_baselibopen( pLUAVM );

	//
	// Rekisteröidään LUA-koodista 
	// kutsuttavat C-funktio.
	//
	lua_register( pLUAVM, "OmaFunktio",       OmaFunktio       );
	lua_register( pLUAVM, "ToinenOmaFunktio", ToinenOmaFunktio );

	//
	// Luetaan ja tulkitaan LUA-skripti.
	//
	lua_dofile( pLUAVM, "skripti3.lua" );

	//
	// Lopuksi suljetaan LUA-virtuaalikone.
	//
	lua_close( pLUAVM );

	//
	// Odotetaan napin painallusta ennen poistumista.
	//
	getch();
	return 1;
}


Skripti3.lua-tiedoston koodi:
--
-- C-funktion kutsuminen LUA-koodista.
--


--
-- Kutsutaan funktiota OmaFunktio()
-- joitakin kertoja. Tässä vaiheessa
-- funktio on vielä parametriton.
--
i = 0
while i < 5 
do
   OmaFunktio()
   i = i + 1
end


--
-- Kutsutaan funktiota ToinenOmaFunktio()
-- joitakin kertoja. Nyt funktiolle välitetään
-- kaksi parametria, kokonaisluku ja liukuluku.
--
i = 0
while i < 5 
do
   ToinenOmaFunktio( i, i/10 )
   i = i + 1
end



Omien C-funktoiden lisääminen LUA-skripteihin ei siis ole kovinkaan vaativa tehtävä. Mutta kuinka C++-luokkien metodeja voi kutsua LUAn kautta? Suoraan se ei onnistu, mutta kiertoteitä onneksi löytyy.

Yksi tapa on luoda LUAn kautta käsiteltävä luokka Singleton-tyyppisenä ja kutsua haluttua metodia C-funktion kautta. Tällöin luokan metodia kutsuttaisiin seuraavalla tavalla:

int OmaFunktio( lua_State* pLUAVM )
{
    OmaSingleton::AnnaInstanssi().TeeJotakin();
    return 0;
}



Singleton-luokkien lisäksi voi käyttää vaikkapa globaaleja osoittimia haluttujen objektien kutsuissa:

int OmaFunktio( lua_State* pLUAVM )
{
    g_pOmaLuokka->TeeJotakin();
    return 0;
}



Tapoja on yhtä monta kuin on tekijöitäkin. Loppujen lopuksi myös luokkien metodit saa LUAn kautta kutsuttaviksi suhteellisen pienellä vaivalla. Valittu menetelmä riippuu paljolti projektista, joten itse kukin voi kehittää oman tapansa asian hoitamiseen.

Loppusanat #

Olemme nyt tutustuneet LUA-ohjelmointikieleen hyvin tiukassa paketissa. On paljon asioita, joita tässä artikkelissa ei käsitelty. Lisää tietoutta LUAsta, tutoriaaleja sekä teknistä dokumentaatiota, löytyy LUAn kotisivuilta osoitteessa www.lua.org.