Lua Scripting Just Have Been Added
While I was doing the porting, I have found SWIG, a very useful tool for Lua integration. Actually it supports so many other languages and interfaces, but I was interested only in game scripting so far.
Why scripting? Scripting is an important feature of a typical game engine, and Merlin3D is not an exception. Prototyping, game and GUI coding is much more easy if I do not have to recompile the whole game engine or DLL. With SWIG adding scripting to the engine is quite easy, so it seemed to worth a try.
Why Lua? There were some other options like GameMonkey, AngelScript or Pawn, but finally I have chosen Lua because it is widespread, well supported, fast, easy to learn and SWIG makes integration easy. Obviously it is not the perfect choice. For example AngelScript has simpler C++ interface, it has static type system and it is truly object-oriented, but not as fast as Lua, it has less support and SWIG does not know it at all.
The language is dynamically typed and it is a drawback from the user’s point of view. The scripting interface has to check parameter types on each call to engine functions. With a statically typed language those checks are performed at compile time, only few type checks are performed at runtime. This is why I thought of using static typed AngelScript, maybe I would switch on it some day… 🙂
After some interface definition and C++ template hacking, the scripting seems to work now. All important scene, game and UI classes are exposed in the Lua interface. I can create scene and game objects, register UI and game event handlers, execute engine startup scripts, etc. In this picture, the “CLICK ME!” button is created and inserted by a script. The click event is also handled by a script, this inserts new labels under the button on each click.
I have replaced the static XML config file processing too with a single Lua script that initialize the engine, register console variables, loads the initial scene, UI, HUD and so on. I can get rid of the XML processing on this part of the code now. This is a part of the initialization script:
M3D.Console_RegisterVariable("LockViewFrustum", "Disables view frustum updates.", false); M3D.Console_WriteLine("Starting up engine...") engine = M3D.Engine() engine:LoadCompositorGraph("Data/Config/DeferredTest.xcg") engine:LoadUISkin("Data/UI/Default.xskin") engine:LoadUI("Data/Test/Menu.xui") engine:LoadHUD("Data/Test/HUD.xui") engine:LoadWorld("Data/Test/Test.xscn") engine:GetDesktop():SetVisible(false) button = M3D.Button() button:SetName("TestButton1") button:SetText("CLICK ME!") button:SetSize(M3D.V2f(10, 24)) button:SetLayoutMode(M3D.LayoutItem_DockTop); button:SetVisible(true) button:SetClickAction("ButtonClick()") engine:GetDesktop():FindElement("SimOptionsPanel"):AddChild(button) engine:Run()
Lua has own object lifetime management, so I should take extra care about the classes those are managed by the engine. By default, Lua manages its own objects, so I have to tell Lua interpreter to not delete objects I want to create and pass to the engine from a script but it seems to be a quite small cost for a working scripting system in return.
There is only one more small thing remaining for me. Maybe I could create a language pack for Notepad++ to support highlighting and autocompleting for my own engine objects, but I do not know how much work needed to complete this.