Ideas for Lua integration in a game engine.

Started by
15 comments, last by ddn3 11 years, 5 months ago
I am working on my first ACTUAL game enigne and I wanted some advice from the good people who roam these fourms.

I plan on using LuaJIT as a scripting language for the game but I am not sure how it is usually integrated. I can come up with some simple way to get it in the engine but what I wanted some advice on was.. really what to use Lua for.

In what systems should I integrate Lua (I know AI is one but that's it) and, if you don't mind, could you spare some tips for integrating it?

Thanks in advance,
Dartos
Advertisement
I'm not sure what LuaJIT is, but Lua can be used to control any parts of your game. It's nice because you can easily change logic and not have to recompile the game.

I use it for scripting events in the game. Take for example, you have a 3d shooter. You might have a script function for doors. Like 'IsPlayerAtWpt('Door1')'. While that's false, do nothing and wait a second and check again. Then if they are, you can call something 'OpenDoor('Door1')'. So, when they move towards a door, it opens.... stuff like that is great for scripts.

I'm not sure if that answered your question. Both of those functions IsPlayerAtWpt(name) and OpenDoor(name) would call down into more elaberate C++ functions that check your distance against a sphere perhaps and the other would simply play an animation (dynamic or premade)

Jeff.

I'm not sure what LuaJIT is,


Well one thing you can do is use c code from within it.

I've just started looking at LuaJIT recently, and an idea I had apart from scripting world objects, was to use it to setup all the monotonous code, such geometry data (eg vertexarrayobjects, vertex buffers, attribute bindings, index buffers etc), shader programs, textures, texture samplers etc. Using luajit ffi to minimise the amount of cbindings necessary.

And then in your main program, load those 'resource files' into a table and just say:

load_resource("shader_program.lua");
lua_getfield(L,-1,"prog");
GLuint program = *((GLuint*)lua_touserdata(L,-1));
lua_pop(L,2);
..

glUseProgam(program);


A resource file might look like:

prog = CreateShaderProgram {
{"vert",
[[
attribute vec3 a_pos;
void main() {
..
}
]]},

{"frag",
[[
void main() {
..
}
]]},
{"attrib", "a_pos", 0}}

vao = CreateVertexArrayObject {
..
}
If you're gonna script your game, go balls to the walls! Kinda like unity.

Speaking of.... Take a look at my post here: http://www.gamedev.net/topic/633218-howwho-create-the-gameobjects/page__p__4992951#entry4992951 It describes a component system for game entities.

Following that pattern, a script component would need a string for what script file to execute and a dictionary (hash table) of variable values to set.

Then, each script has an Awake, Update, LateUpdate, Destroy, etc... Attached to a game object, the script component (written in c++, don't care about what binding you use) can call the update function of the lua object it owns. You will need to expose math and other utility functions to the script, but done right your whole engine can be scripted. You should be able to make a full game using your ending without needing to touch C++.

Hope that makes sense, i can go more in detail if you want.
So basically I would write functions into the C++ classes (components) and just make lua bindings for them then call them in lua? that sounds.... genius.
Continuing this brainstorming topic, should I bind the C++ functions to lua in the constructor or init function?
Constructor.

Ideally you would want: Constructor > [optional] Deserialization > Initialization. The nice thing about LUA reading plain text is you can at any point save the state of a lua file (Or lua object as i like to think of it), then read it back in.

Not related to my last post, but here is some pseudocode:
[source lang="cpp"]#include <iostream>

class ScriptComponent : Component {
protected:
WhateverLuaWrapperYouHaveForInternalObject* lua_obj;
std::vector<LuaCoroutineWrappers*> lua_coroutines;
ScriptComponent(const ScriptComponent&);
ScriptComponent& operator=(const ScriptComponent&);
public:
Component(char* luaFile) {
// Some arbitrary way to get your lua "Object"
lua_obj = LoadLuaFile(luaFile)->newObject("type");
lua_obj->Awake();
ObjectEnabled();
Start();
}

~Component() {
ObjectDisable();
lua_obj->Destroy();
delete lua_obj;
}

void ObjectEnabled() {
lua_obj->OnEnable();
}

void ObjectDisable() {
lua_obj->OnDisable();
}

void Start() {
lua_obj->Start();
}

void Update(float dt) {
lua_obj->Update(dt);
for (int i = 0, size = int(lua_coroutines.size()); i < size; ++i) {}
lua_coroutines->ProcessYieldingForNull();
lua_coroutines->ProcessYieldingForTime(dt);
}
}

void FixedUpdate(float dt) { // dt being a constant at this point
lua_obj->FixedUpdate(dt);
for (int i = 0, size = int(lua_coroutines.size()); i < size; ++i)
lua_coroutines->ProcessYieldingForFixedUpdate();
}

void LateUpdate(float dt) {
lua_obj->LateUpdate(dt);
for (int i = 0, size = int(lua_coroutines.size()); i < size; ++i)
lua_coroutines->ProcessYieldingForEndOfFrame();
}

void ObjectWillRender() {
lua_obj->OnWillRenderObject();
}
};[/source]

Referring to an object lifecycle that looks something like so:
unity-lifetime.png

So basically I would write functions into the C++ classes (components) and just make lua bindings for them then call them in lua? that sounds.... genius.


That is exactly what I do in my game engine. Once the engine is compiled, you can use Lua to create everything in game without ever having to recompile again. Menus, Logic, AI, serialization, synchronization, everything.

xenos_diagram.jpg

Here is a link to my engine's source in case you want to check it out :

http://sourceforge.net/projects/xenosengine/

Here is the engine compiled, with a game.

http://www.indiedb.com/games/dungeon-tactics

If you're gonna script your game, go balls to the walls!

Agreed!

So that I can do a lot of prototyping and be free to try out new ideas, I make everything scriptable. I have a core executable that fires up the Lua VM in a thread and registers some built in systems in Lua and it's own system of extensions, such as memory management, event handling, streaming, logging, etc. It then pulls in DLLs from an extensions directory, passes it's own API pointer to an entry function in each so that the DLLs can register their own functions as extensions in the core executable. At this point, DLLs can request the APIs of other DLLs and all functions can be called from Lua. Most of these functions are for setup with a low-level "update" function handling the bulk. So, there's not much loss in performance.

With the Lua VM fired up, a kernel script is loaded that manages other scripts as "applications". This allows control over what each script has access to (see function environments) so that player scripts don't have access to low-level functionality. I also have a debug feature that uses Lua's built in line-by-line debug feature to "wrap" a script without affecting other scripts (would be insanely slow otherwise).

I've been working with this for awhile and it's saved me incredible amounts of time. Have fun! =D
Quit screwin' around! - Brock Samson
Why should I have a separate function for initialization when I can just start everything in the constructor?

P.S. Thanks everyone for all this great feedback!

This topic is closed to new replies.

Advertisement