Saturday, May 20, 2006

Scripting with Lua in C#

Contents

  • What to get?

  • Installing

  • Basic Use of Lua

  • A notch, a crank, knarc, ahctona!

  • But wait! There's more amazing where that came from!



Lua allows us to easily add scripting to our game. It's a well known, widely used standard (it was used in Balders Gate). Once added it can process both compiled and uncompiled Lua scripts giving a good combination of flexibility and strength. The greatest advantage to Lua is how simply we can insert it into our C# programs. Well, hopefully now your convinced so let's get cracking.

What to get?


LuaInterface 1.3

Installing



Unzip LuaInterface 1.3 (or later version that you may have been able to download). Put it somewhere safe such as in the Visual Studio directory or some place your unlikely to accidentally delete.

Now boot up visual studio. Create a new C# Console Project.

Now go to the Solution Explorer window.

Play with my options!

(If you can't see it use this
Press me! Press me! and it should pop up!) Take your mouse over to the References icon and right click.

Choose add reference. This should all be pretty standard fare for starting a new project. We're just recovering the basics here in case you're reading this tutorial as a seperate chunk.

Press my buttons

Carefully survey your options and then choose "Browse". Then browse to where you unzipped LUA and go into the bin folder ... and select LuaInterface.dll. My copy of this .dll file is at the following location C:\LuaInterface\bin\LuaInterface.dll.

Once you have chosen this .dll file it will pop up in the the "Selected Components" part of the window. Confirm the selection and we're ready to go. I'm sure there's a method of playing with Visual Studio where you can avoid the "Browse" button all together and have the reference preloaded in, ready to select but after a few minutes fiddling I couldn't find it :(

Your game and your game players are going to need access to the Lua dll files. So you need to have them in project directory. My version of the console application executable that we're working on is currently located here: junk\Visual Studio Projects\ConsoleApplication3\bin\Debug

Open up that directory. Also open up the directory of were you installed Lua. Go into the bin\ directory and copy across (to project directory):


  
luanet.dll


Your working directory should end up looking a little like this:

The lua net dll

That concludes setting up Lua to work with this project. Not too painful.

Basic Use of Lua



Let's write some code. We want to make a C# program that's interfaces with Lua and we want to do it soon, very soon indeed.

using System;
using LuaInterface;

namespace ConsoleApplication
{
class Class1
{
static void Main(string[] args)
{

}
}
}


The above is our class skeleton. The only thing worth noting at the moment is that we're using the LuaInterface reference. We'll concentrate on the main method an make a small program that uses Lua.


  
static void Main(string[] args)
{

Lua lua = new Lua();


Here we create a Lua interpreter. We can create as many as we wish and they will all be independant. But for now we'll only have one.



Next we create some global variables. Remember we're programming in a scripting language so we're creating Lua global variables not C# global variables. In actual use this "programming" would be done ahead of time and then loaded in from a file. Curently we're doing it directly in the code just to get a feel of what's happening.



lua["num"] = 2;
lua["str"] = "a string";


This creates two Lua global variables. One is called num the other is called str. Note that the variables both hold different types. This is a feature of Lua it is not a strongly typed language in fact it's dynamically typed. Variables can be anything (though Lua only has a few data types). But I don't want to get pulled in to a distracting explanation of how Lua works as a programming language quite just yet. First I'd quite like to get it hooked up to C#.



Now we made these Lua varaibles let's get C# to read them. Now as you really should know by now C# is a strongly typed language. So we need to cast the variables.



double num = (double) lua["num"];
string str = (string) lua["str"];


Note that we must cast to a double here. An int won't cut it as Lua is storing double information. Now we've read this information out from the Lua interpreter, next let's read it out to the screen to see if everything is agreeable.



Console.WriteLine("str:" + str +"\tnum:" + num);
Console.ReadLine();

Agreeable indeed!


Okay so far not very exciting but we're still only getting a feel of what's going on. So let's crank it up a notch next and do something cool.


A notch, a crank, knarc, ahctona!



Okay grip the arms of your chair tightly and get ready to script!



First let's create two functions we might like our brand new scripts to make use of.


  
public void DanSays(string s)
{
Console.WriteLine("Dan>" + s);
}

public void ThorSays(string s)
{
Console.WriteLine("Thor>" + s);
}


Feel free to insert your own name instead of Dan or Thor. So now we have two cool functions bursting with game playing potential.



Let's hook them into Lua - so out main method will look like below:


 
class Program
{
static void Main(string[] args)
{
Program program = new Program();

Lua lua = new Lua();

//Register our C# functions
lua.RegisterFunction("DanSays" , program, program.GetType().GetMethod("DanSays" ));
lua.RegisterFunction("ThorSays", program, program.GetType().GetMethod("ThorSays"));



This lets the Lua scripting language make calls to our two C# functions. Incredibly easy isn't it? So in the Lua language we just make calls to DanSays("with a nice string here") or ThorSays("with an equally nice string here") and they will call the C# equivalents!



The RegisterFunction function



The RegisterFunction's first argument is what you'd like to name the function in Lua. Here we chose the same name because it's so simple. But in future function registration we might want to make the Lua name more simple or descriptive than our C# name.



The second argument is the object where the method is stored. Note the word object. That's why we have to instantiate our Program program. So if we have a class called person "Class Person" and it has a method talk then it allows us to do something like.


   
Person pete = new Person();
Person jeff = new Person();
pete.talk("Hello");
jeff.talk("Hello");


We can the register these talk functions seperately.



The third parameter uses reflection - something I currently know nothing about. All I know is that's it's magic like pixey dust and elevators. It magically gets all the knowledge about the method - it's arguments and so forth. Then Lua can call it effectively. You have to pass in a string of the methods name and that's it. Great!


The DoString function


Add this to the end of our main method.



lua.DoString("DanSays('Hello'); ThorSays('Hi! Dan')");
Console.ReadLine();


DoString executes a line of Lua code in string form. Wonderbar!


Dan and Thor have a bit of a chin-wag

But wait! There's more amazing where that came from!



So we've got a pretty groovy scripting language all by doing relatively little.



But now let's arrange it into a bit more of a game like usefullness. Go to your working directory for this project. The place where you copied all those dll files (mine is: My Documents\Visual Studio Projects\ConsoleApplication3\bin\Debug).



Okay create a new directory all call it "scripts"


A script directory.

Now in the scripts directory I've created a simple text file called "Thursdays"



It has the following not-so-witty-banter.


DanSays("Hey, Thor!");
ThorSays("Hi Dan! You know Thursdays . . . ");
DanSays("*sigh* yeah, Thor, I know Thursdays.");
ThorSays("Named after me you know!");
DanSays("Yeah, I know.");


Okay. So in scripts\Thursdays.txt with have some future-award-winning dialogue. It's written in Lua but only using custom functions designed by us because we're cool and groovy designers.

Back to El Code



Make your main method match mine pictured below:

  
static void Main(string[] args)
{
Class1 c1 = new Class1();

Lua lua = new Lua();

//Register our C# functions
lua.RegisterFunction("DanSays", c1, c1.GetType().GetMethod("DanSays"));
lua.RegisterFunction("ThorSays", c1, c1.GetType().GetMethod("ThorSays"));


lua.DoFile("scripts/Thursdays.txt");
Console.ReadLine();
}


This fine piece of programming hereafter known as "ScriptRunner3000" will run any script we call Thursdays - without recompling - yes you heard me correctly - without recompiling!

Of course if we want to get really fancy we could write some code to enumerate all the files in scripts and then produce a menu allowing you to choose which you'd like to use. But this is much more of a proof of concept deal. Anyway the output:

The extended edition

Now the script can be tinkered with to our hearts extent and the exutable will show those changes without having to be run. Of course it's a lot more powerful than it is here. We can uses Lua's code constructs like loops and structures and all those niceities - wasn't putting a scripting engine in really simple? :o


Now you should brush up your Lua!


Globals



Lua is simple and powerful I just wanted to show persistancy of globals then maybe we'll end this tutorial here. But later come back to really really groovy things like coroutines.

  
static void Main(string[] args)
{
Class1 c1 = new Class1();

Lua lua = new Lua();

lua.DoFile("scripts/Thursdays.txt");

string b = (string) lua["name"];
Console.WriteLine("Name:" + b);
Console.ReadLine();

}


If in Thursdays.txt we now include the line name = "rabbits" then the above code will outpt rabbits. See the scope isn't lost in the file which is super groovy and allows us to easily insert scripts into the game loop - yay!

Source Code


Download the source code.

References used



  • LuaInterface: User’s Guide by Fabio Mascarenhas

  • LUA 5.0 Reference Manual (avaliable from www.lua.org)

8 comments:

Anonymous said...

Why using LUA when you can use C# just like any scripting language?

With Microsoft.CSharp.CSharpCodeProvider you are able to compile and execute code at runtime.

It's most probably even faster than LUA!

balaam said...

C# is probably faster but Lua has a lot of nice features C# is missing. Like being able to return more than one return parameter from a function. It's also easy to write Lua code using Lua code and then run it.

Some of these things you can do in C# but they have a lot more messy syntax. (It gets even worse in C# version 3.0 - they add more power but the syntax is really - yuck :D)

Anonymous said...

Hi, I'm really interested in using Lua. But for some reason, my project can't seem to find the LuaInterface namespace.

By the way, my project has a class library in which I added Lua as reference.

Anonymous said...

Like being able to return more than one return parameter from a function. It's also easy to write Lua code using Lua code and then run it.


... Using out parameters on your methods gets you basically the same thing.

balaam said...

Yes you are right "out" provides the same (kind of) functionality just in less natural way. It can far more verbose and less efficent if you're only passing back arguments as error flags or metainformation.

I don't want the comment area to turn into a message board :D Maybe I should find a message board.

Anonymous said...

Why use Lua over C#? Because users can more easily write scripts in Lua than they can in C#. This makes Lua an excellent choice for any app that needs to extend its functionality to the user using a scripting language.

Anonymous said...

Thank You for the tutorial. Fantastic tutorial! This being very useful for my monographic work of the college… I am happy for the tutorial! Thanks a lot Daniel! Certainly I will go to collaborate with the topic!

Anonymous said...
This comment has been removed by a blog administrator.