Office Management 101 is a satirical office life simulation tycoon game set in a fictional capitalistic dystopia. Step over competitors, drive your staff to the limit and milk your customers for every penny in the pursuit of spiraling success!

Report RSS Lua scripting in LibGDX

Lua is a popular scripting language used for many games. Here's how I integrated it into Office Management 101, the upcoming business sim. Possible future mod support?

Posted by on

Hey y'all!

When I picked up the development of Office Management 101 again, I first decided to rework the whole AI system, which had become cumbersome and a bit limited for what I had planned for the future. While I was figuring out the approach, I thought that since I'm changing it all anyway, I might as well make it more flexible in terms of coding too.

Do the work (.gif animation) Coffee vending machines now fueling the office Adam and Eve

This led me to look into scripting languages that I could integrate into my game engine, which runs on the LibGDX Java framework. I considered several different ones, but finally settled on Lua, which is fairly simple to write and a popular choice for games.

To my surprise it turned out there's a number of Lua libraries for Java and it wasn't immediately clear to me which would be the best one. The final choice for me came down to picking the one which seemed to be updated most often - LuaJ, although I found the documentation of it somewhat lacking.


I did find a simple example how to use it with LibGDX though. The example is outdated, since LibGDX has gone through some major changes this year, but at least it was a start.

So here I'll give an overview how I set it up. I won't claim it's the best way to do it or even a good one, but if you're reading this and find anything I could improve in my approach or code, feel free to let me know. It's not meant to be much of a tutorial, but rather just an explanation of how I implemented it, especially since I have no real Java experience out of this project.

I'm using the Gradle setup that LibGDX ships with these days, so to add any 3rd party Java libraries I just drop them in the libs directory in my project root. In my build.gradle file I have defined the core dependencies as:

bash code:
project(":core") {
    apply plugin: "java"

    dependencies {
        compile "com.badlogicgames.gdx:gdx:$gdxVersion"
        compile "com.badlogicgames.gdx:gdx-ai:$aiVersion"
        compile fileTree(dir: '../libs', include: '*.jar', exclude: '*-sources*.jar')
    }
}

This includes any .jar files that may be in the libs directory, but excludes the source files of those libraries.

The other dependencies include the core as well, so there would be no need to define that row again. The desktop for example:

bash code:
project(":desktop") {
    apply plugin: "java"

    dependencies {
        compile project(":core")
        compile "com.badlogicgames.gdx:gdx-backend-lwjgl:$gdxVersion"
        compile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"
    }
}

After adding a new .jar file, you need to refresh Gradle dependencies and I've noticed that sometimes I also have to restart my Eclipse client.

Based on the example I linked earlier, I created a base interface for the scripts (Script.java), so if needed, I could even support different scripting languages. Maybe not a realistic need, but I do like to keep things flexible if possible.

The implementing LuaScript class makes use of JsePlatform.standardGlobals() of LuaJ that loads the script file from disk. It then defines the functions that take the parameters passed to it and converts them to a form that Lua can understand with CoerceJavaToLua.coerce(object) and runs a function in the loaded Lua file with globals.get(functionName).invoke(parameters).

You can download the full file here: LuaScript.java

Then I have something like a global class called ScriptManager that manages the Lua scripts cache by loading them upon first request (so scripts are only loaded when required) from list in a .json file and has the methods to call the functions defined in Lua scripts. Note that it expects the files to be in assets/data/scripts directory.

The full class is here: ScriptManager.java

This could still use some improvements like removing rarely used scripts from the cache after some time, so I'll work on it some more when time permits.

In addition I have a singleton ExecuteScript that is made available in Lua scripts to call other script functions, which can be in separate files.

The class itself is here: ExecuteScript.java

Okay, so how does it all come together? So say I have the scripts defined in the scripts.json file like this (just simple key-value pairs):

bash code:
{
    GraphicalObject: "graphical_object.lua",
    Character: "character.lua",
}

And say the character.lua file looks like this:

lua code:
-- INIT --
function init(character)
    print("Character init")
end

-- UPDATE --
function update(character, delta)
    print("Character update")
end

To call the Lua init function in my Java Character class, I just need to add the following line in the constructor:

java code:
ScriptManager.executeInit("Character", this);

The ScriptManager will load the character.lua file based on the scripts.json key and call the function init while passing the Character object that calls the function as a parameter to the function. To call the update function I could do either:

java code:
ScriptManager.executeUpdate("Character", this, delta);

or

java code:
ScriptManager.executeFunction("Character", "update", this, delta);

And that's it.

To call the same function from Lua itself I'd have to do:

lua code:
ExecuteScript("Character", "update", {character, delta})

Now all this doesn't mean that 3rd party modding of the game would be possible yet. It would need to check for files in external directories first, since right now it loads all the files from the Java package. There would also need to be better error checking, lots of security improvements and more integration to the rest of the game classes, not to mention proper documentation. But this means that should I ever decide that adding full modding support is a good use of my time, I at least have a base to start from instead of having to rewrite half of the code.

The system could use some performance testing though and I definitely want to add a console where Lua command input is possible for easier debugging purposes, so those are my main concerns for now.

That's all for now, thanks for reading and I hope it was useful or at least interesting for at least some of you. Next time I'll continue with the behavior tree based AI that I implemented and which will refer to this article as well in some parts. If there's any questions you want to ask or advice you want to give, feel free to leave a comment here, email me at riho@tulevik.eu or contact me on Twitter (@tulevikEU).

Cheers,
Riho


Forum | @tulevik.EU | Facebook | officemanagement101.com | DevLog on TIGForums

Post comment Comments
Guest
Guest - - 688,627 comments

Interesting, did you use lua in combination with gdx-ai?
BTW I'm davebaol the developer of gdx-ai :)

Reply Good karma Bad karma+1 vote
tulevik.EU Author
tulevik.EU - - 34 comments

Hey! So easy to miss comments here, since I don't get a notification about it.

But yes, I'm using your module, which is great btw. I actually have some additional behavior tree classes I've been meaning to submit to you (RandomSelector and RandomSequence).

Reply Good karma+1 vote
Guest
Guest - - 688,627 comments

Would be great :)

BTW, I was not logged in when I posted the previous comment and looks like it's not publicly readable. I only see the message "This comment is currently awaiting admin approval".

Reply Good karma Bad karma+1 vote
Guest
Guest - - 688,627 comments

I'm planning on using Lua scripts in my LibGDX project, and your article is a great starting point. Thank you!

Just a suggestion about setup: rather than downloading the Luaj jar file in your project, you can retrieve it from the sonatype repository. In your Gradle build file's project(":core") dependencies, include this line:

compile "org.luaj:luaj-jse:3.0"

Reply Good karma Bad karma+2 votes
Post a comment

Your comment will be anonymous unless you join the community. Or sign in with your social account: