Last Updated:

Integrating Lua in C++ | Game Project Examples

It was decided to put the integration of Lua with C/C++/C# in a separate series of materials. The language itself is essentially universal, and its most frequent use is in game projects. You can read the entire introductory part on Lua, its basic syntax, in the series of materials "Lua for games and not only". Now let's delve into highly specialized niches.
 
Today we'll start with the basics of integration, install Lua, and make some simple examples for C#.
 
For the lesson in this part you will need Visual C# or MSVS (Microsoft Visual Studio) 2003 and above (I will show everything for 2008), as well as LuaInterface (a variant for the .NET CLR) downloaded from the http://luaforge.net/projects/luainterface. Unzip it to a convenient folder for you, but at the same time, so that you cannot accidentally delete it.
 
 

Creating a new application using Lua & C#

 
 
So, first of all, create a new Visual C# (Console Application) project, let's call it LuaTest01.
 
Integrating Lua in C++
 
Once the basic modification is loaded, we need to attach LuaInterface.dll. This is done through Solution Explorer: you click the right mouse button on References, in the context menu that opens, select the Add Reference item, as a result of which the window of the same name appears.
 
Integrating Lua in C++
 
In it, go to the Browse tab (in 2003 this is done with a separate button) and specify the path to the LuaInterface library.dll, which is located in the Built directory of the folder to which you unpacked LuaInterface.
 
Integrating Lua in C++
 
As a result of the actions performed, you should have a new entry in the Reference item. The fat is in the fire.
 
Now go directly to the file with the code and write in the first lines:
 
using System;

using LuaInterface;

 

namespace LuaTest01

{

class Program

{

static void Main(string[] args)

  {

  }

}

}
 
Now in the Main function () we will write a small fragment of code, which, by the way, is given in the luaInterface documentation, but in this case we will also make some output of the results in the console window.
 
static void Main(string[] args)
{
Lua lua = new Lua();
lua["num"] = 2;
lua["str"] = "a string";
double num = (double) lua["num"];
string str = (string) lua["str"];
Console.WriteLine("str:" + str + "\tnum:" + num);
Console.ReadLine();
}
 
So, if we translate line by line, first we start the Lua interpreter, then create two global variables "num" and "str" and assign them some values. We then read and convert the values of these global variables to C# and display them in a console window. It should be noted that LuaInterface itself is designed primarily to provide interoperability / compatibility between the two languages, or better said, between the Lua language and a particular platform. In particular, in our case, we use the library for the .NET CLR. ClR (Common Language Runtime) is a common language runtime of the .NET platform, and CTS (Common Type System) and CLS (Common Languages Specification) are often used in parallel with this concept.
 
 
Basically, all this applies more to .NET, and in our case we will not dig deep. The main thing to understand is that there is a runtime clr, there is a common type system, and LuaInterface provides a full translation and conversion. Within the framework of the above code, the string was automatically translated into System.String and number into System.Double.
 
 
DoString
 
 
Now, let's fix our example a bit using the DoString() command, in which you can insert executable blocks of code (or as they are also called chunks) in Lua:
 
static void Main(string[] args)

{

Lua lua = new Lua();

lua. DoString("num=2");

lua. DoString("str='a string'");

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

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

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

Console.ReadLine();

}
 
As you can see, the situation has not changed in any way with the only difference that we began to use portions of the Lua code.
 
Integrating Lua in C++
 
 

RegisterFunction

 
 
In an executable environment, the Lua class has a RegisterFunction method that enables you to register CLR methods as global Lua functions. The first argument to it is the name that you would like to assign to this function within Lua. The second argument is the object or class that contains the method, and the third argument is the method itself.
A little unclear, OK, let's expand on our example.
So, let's add two C# functions to the code, IvanSays and MikeSays:
 
public void IvanSays(string s)

{

Console.WriteLine("Ivan>" + s);

}

public void MikeSays(string s)

{

Console.WriteLine("Mike>" + s);

}   
 
As you can see, they do not carry anything complicated, and simply display a line on the console screen.
Now let's redo our Main function, or rather, rewrite it in a new way. So:
 
static void Main(string[] args)

{

Program program = new Program();

Lua lua = new Lua();

lua. RegisterFunction("Ivan", program, program. GetType(). GetMethod("IvanSays"));

lua. RegisterFunction("Mike", program, program. GetType(). GetMethod("MikeSays"));

lua. DoString("Ivan('Hi'); Mike('Hi, Ivan!')");

Console.ReadLine();

}
 
The main thing here is not to get confused, so we will explain everything in detail. Program is the default base class in which we do everything, now create an instance of it, start the Lua interpreter, then register the Lua functions using RegisterFunction. The first line with the redirect should read as follows: The IvanSays method of the program object is registered as the Ivan Lua function. Next, as part of using the DoString method, we ran portions of Lua code, in which we called functions with their names and passed parameters to them.
 
I also draw attention to the fact that according to the syntax of Lua, the operator ";" as such is conditional, that is, it can not be put at the end of each executable line.
 
 
 
On the other hand, with its presence, the code is more familiar to understanding among C/C++/C# programmers. In principle, very large files are not often written on Lua - the main thing here is that it does not turn out as with the reduction in the volume of "War and Peace" by Leo Tolstoy after the removal of the letter "yat" :) from the alphabet.
 
 

DoFile

 
 
Strings, reading them, translating executable code from them is very good, but scripting languages like to use for the reason that they allow you to take part of the code beyond the compiled part. In other words, everything that we have now prescribed inside can be transferred to an external file.
 
So, let's create our own directory for scripts at... ...\LuaTest01\bin\Debug\, let's call it scripts. This placement is chosen for convenience, so as not to enter absolute links into the code later. And now let's create a file with the dialog in the scripts folder, calling it dialog.txt. What do we put in it? Same function calls:
 
Ivan('Hi')

Mike('Hi, Ivan!')

Ivan('How are you?')

Mike('Tip-Top')

Now, instead of DoString, we use the DoFile method, which gives a command to execute the specified file. And the main function we will already look like this:

static void Main(string[] args)

{

Program program = new Program();

Lua lua = new Lua();

lua. RegisterFunction("Ivan", program, program. GetType(). GetMethod("IvanSays"));

lua. RegisterFunction("Mike", program, program. GetType(). GetMethod("MikeSays"));

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

Console.ReadLine();

 
As you can see, we replaced only one line, and the commands themselves to call the functions of the executable code were transferred to a separate file. In this case, one of the main mechanisms of Lua is demonstrated, thanks to which you can separate a block of the main code from the auxiliary.
 
 
 
 

To summarize

 
 
We will move in thematic blocks and in the next part we will begin to work with classes. I must say that many developers often avoid inserting script components into their projects, in fact, it is very convenient. On the other hand, some programmers/designers translate everything they can into scripts in order to improve portability (into other languages and/or platforms). The latter includes the author of these lines. There are also disadvantages to this approach, because with the wrong prioritization, you can lose in the performance of applications. That is, it is always necessary to look for a middle ground.
 
In general, the use of Lua for the .NET platform is now often perceived by many as a specific deviation. This is both true and not at the same time, given that .NET is constantly expanding. In general, we started with it, because the demonstrated constructions are simple enough to understand and minimalistic in terms of code. Today we will talk about the interrelated implementation of classes.
 
In the last article we demonstrated two very simple examples that allowed readers to learn how to manipulate C# functions from Lua scripts. We also touched on the rather weighty topic of the CLR (Common Language Runtime), CTS (Common Type System) and CLS (Common Languages Specification).
 
In simple terms, all of this refers to Microsoft's successful attempt to totally unify many related languages within a single .NET platform that programmers of various ranks have heard of. The Microsoft Visual Studio (MSVS) family of languages in recent versions differ more in syntax than functionality. The code written in them is converted into CIL instructions (CIL - Common Intermediate Language, Common Intermediate Language), which in turn are compiled by the online (JIT - just-in-time) compiler. For example, in C#, string concatenation is indicated by the "+" sign, in VB.NET it is also indicated by the ampersand "&". But for all this difference, the corresponding . NET compilers, namely csc.exe and vbc.exe compile a similar set of CIL instructions. This, in fact, is the main meaning of the common language specification CLS.
 
It should be noted that the LuaInterface library we use in the examples.dll is now directly related to .NET, because it is mainly responsible for translating Lua code and converting data within environments or programming languages that support the common language runtime (CLR). The library is written mostly in C#, although it has about 30 lines in regular C.
 
Of course, in fact, functionally the languages are still different and at some points quite strongly, but now there is a variant of general mixing. It should be noted that the main types for most programming languages are defined within the framework of CTS and the conversion goes with the appropriate binding. Classes, enumerations, structures all apply to CTS.
 
The most clunky point in this situation is that the main language for the .NET platform, which can rightly be considered C#, changes periodically, so the example listings within many lessons published in books and on Internet sites are often outdated. So check the versions all the time. Our narrative is based on VC# 2008 and Lua 5.1 (also quite different from 5.0).
 
 

Example Assignment #3

 
 
Let's say we have a training group. You need to: in C#, define the main classes and methods, and within the framework of Lua scripts, write code to create and load a list of students, including their names and brief characteristics.
 
 

What is required initially?

 
 
Similar to past examples, we will naturally need Visual C# or MSVS right away. If the LuaInterface archive we used before doesn't have a luanet library.dll (for example, I didn't have one) additionally download the full distribution LuaForWindows_v5.1.4-45.exe with www.lua.org and install it. There is a luanet.dll will be located in the clib folder. Of course, it would be possible to engage in self-assembly, but for this lesson such a deviation will be superfluous, because a novice specialist needs to feel what Lua can do within the framework of integration.
 
Now run VC# and create a new project (Console Application), let's call it LuaTest03. In Reference, add the LuaInterface library.dll, and also copy the luanet.dll to the folder ...\LuaTest03\bin\Debug.
 
In the header of our main cs-file we write the following:
 
using System;

using System.Collections.Generic;

using LuaInterface;
 
With System and LuaInterface, everything is clear in this case - we have already worked with them, and we will need System.Collections.Generic to use generalized types of classes that implement enumerations.
 
Now let's start writing the code.
 
 

Writing in C #

 
 
So, within the framework of the application, we have to create two of our own classes - for the student and for the group.
 
First, let's define a class for the student, in the LuaTest03 namespace we write the following lines:
 
class Student

{

public string Description = "";

public string Name = "";

public void GiveName(string name)

{

Name = name;

}

}
 
As you can see, we mean a normal structure with two properties (name Name and Description Description) and one GiveName() method. We need this separation to take a closer look at the integration of the scripts and the part to be compiled.
 
For convenience and speed, I renamed the second class from Program, the name of which is generated by default when starting a new project, to Group.
 
Now we need to create an enumeration, based on the List<T> option from System.Collections.Generic, as well as the functions for viewing the entire list and adding to the list. Therefore, we enter the following lines of code into the Group class:
 
List<Student> students = new List<Student>();

public void Look()

{

foreach (Student s in students)

{

Console.WriteLine(s.Name + ": " + s.Description);

}

}

public void AddStudentToGroup(Student s)

{

students. Add(s);

 
So, the first line of the added is the creation of a new list. The Look() function views its contents, and AddStudentToGroup() adds a new record by using the add() method available in the standard set for List<T>. As you can see, it's very simple.
 
Now it's time to start Lua, so add the following lines below:
 
Lua lua = new Lua();

public Lua Lua

{

get { return lua; }

}

public Group()

{

lua. RegisterFunction("JoinGroup", this,

GetType(). GetMethod("AddStudentToGroup"));

}
 
Basically, you won't see anything special here either: we start the Lua interpreter, and using the RegisterFunction() method, we access the C# addStudentToGroup function through Lua, and within the Lua code it will be called JoinGroup.
 
Now it remains to fill in the key function Main(), although we will not rush in this case, and proceed to create a Lua-script.
 
 

Programming in Lua

 
 
So, in the folder ...\LuaTest03\bin\Debug, create a new directory scripts, in which again we create a text file, calling it students.txt. In it we put the following code:
 
luanet.load_assembly ("LuaTest03")

Student = luanet.import_type ("LuaTest03.Student")

-- Mikhail Ivanov

mike_iv = Student()

mike_iv.Description = "Diligent Student"

mike_iv:GiveName("Mikhail Ivanov")

JoinGroup(mike_iv)

-- Stefania Ulyanova

step_ul = Student()

step_ul. Description = "Talented Scientist"

step_ul:GiveName(Stefania Ulyanova)

JoinGroup(step_ul)
 
In this case, we are using a luanet library.dll, with the load_assembly method loading a specific namespace, and import_type as the name implies, imports a type defined in that name. In our case, we have access to the Student class, and now we have access to its full use. Next, we create the first instance of the class for Michael Ivanov, enter a characteristic for him and, attention (!), call the method that adds the name. And as you can see, the property is written after the period, and the method is written after the colon.
 
Of course, we could enter a name, as well as a characteristic, but in this case it is simply demonstrated that it is possible to use the methods defined within the class. After the actions performed with the string JoinGroup(mike_iv), we place the student Ivanov in the list of Students of the main program.
 
Next, for the sake of mass participation, we will add another student – Stefania Ulyanova.
 
 

Main() function in C #

 
 
So, the Main() function doesn't require anything particularly complicated to write, we just need to run the Lua script to run, and then look at the loaded list of students. Thus, everything will look like this:
 
static void Main(string[] args)

{

Group group = new Group();

group. Lua.DoFile("scripts/students.txt");

group. Look();

Console.WriteLine("Press Enter to exit.");

Console.ReadLine();

}
 
As you can see, we create an instance of the Group class, then within its framework we call the file with the script for execution, and then view the loaded list.
 
 
 
 

Summing up example #3

 
 
 
So you've seen how you can work with classes defined in C# code from external sources. Moreover, going to the global and used by the environment namespaces such as System.Windows.Forms or System.Drawing, you can actually program literally everything directly from Lua-files, namely, call any types and methods.
 
So far, things may seem very simple. In fact, there are not many difficult moments in using Lua, although the language is now beginning to cover an increasing number of areas. The most important thing is the competent separation of the compiled part from the scripts.
 
Separately, it is worth saying that now we are considering examples of using Lua within the .NET framework, where everything is done somewhat specifically compared to other options. At the same time, .NET has already spread to many other languages, the full list of which can be seen on the http://dotnetlanguages.net website. Here you will see that there are many such languages, and .NET works not only in Windows, but also in other common operating systems. Therefore, the bet of Lua developers, who from version to version brought LuaInterface closer to the .NET platform is quite understandable.
 
In the next part, we will make a simple game example with the creation of NPCs.