As promised, I continue to share with you those technical details that we encounter in the process of creating our game.
This time let's talk about the language for writing in-game scripts.
In this article I will tell why Lua, and not a handwritten bicycle. Why in general the game may need a scripting language. What subtleties are there when screwing this case to Unity and show how this is done using the example of UniLua integration.
')
I must say that the latest information on the Internet is almost zero, and half of this zero is in Chinese. So, you can say - keep exclusive.
Why do we need scripts?
In our game we have the need to show a variety of scripted scenes.
I will give a typical example of a quest. The character enters the store and sees that there is a robbery. A picture showing thugs holding a bat at the temple of a frightened seller is shown. Then some kind of dialogue is shown. Then we see how our character comes to the mess and the action selection window appears - to help the seller and distribute to the racketeers or fit in for them.
Obviously, you need to move sprites here, change animations for them, show the player different dialogues and pictures ... There are not many options here - either hardcore each quest, or try to script this thing.
Obviously, such things are hard work at all.
Why Lua?
Actually, initially there was a choice between own bike and Lua.
It would seem that from the first approximation the language does not require much and you can write your own. Call yourself a team in order and that's it. But if you think deeper ... Will the script events be related to the game parameters? For example, a previously killed NPC should not appear in the scenes. Or something else. And this already means some conditions, triggers, etc.
As a result, the “unpretentious language” parser can turn into a very complicated thing, which will have to parse heaps of logical expressions, etc. etc.
Without thinking, it was decided to use someone else's and proven. Lua. Perhaps there are also other languages ​​... but Lua is what I see constantly in other games. In the same World of Warcraft, the mods were written in this strange language, where indexation starts from one.
So, again, it was decided to use a solution proven by others.
Unity Integration
Here begins the first fun. The very first library that implements Lua in Unity, which you will find, will look good. But if you dig deeper, it turns out that it uses some specific .Net methods that, for example, are not available on mobile phones (and, possibly, some other platforms).
And we would like a library that would be supported everywhere (just in case) and preferably even fully with the sources, and not in a closed DLL.
Digging in the internet, we found the free creation of Chinese programmers -
UniLua . Full sortsy and works everywhere.
It's all good except that the docks are incredibly scarce and partly written in Chinese.
Well, okay, we also have the source! And the brain ... =) Downloading, throwing the UniLua folder into the plugins (so as not to be recompiled each time) and forth.
Call Lua Script from C #
It's all relatively simple:
using UniLua; private ILuaState _lua;
You can try to run. If no one curses, then everything is fine. An empty script was successfully executed.
Calling C # Functions from Lua
Now we need to learn how to steer at least something from this script. Obviously, we need to learn how to call C # code from Lua.
Let's write a method that simply writes a parameter to the log:
private int L_Trace(ILuaState s) { Debug.Log("Lua trace: " + s.L_CheckString(1));
As you can see, we used the class
ILuaState
. It is there that all input parameters are stored (which we want to transfer from Lua and that is where the result should be returned. Note! The result in Lua is not returned via
return
, but through
s.PushInteger()
,
s.PushString()
, etc.
The function is written. Now you need to connect it to Lua.
private int OpenLib(ILuaState lua) { var define = new NameFuncPair[]
Next, after creating the _lua object, we need to add a connection to this library description:
_lua.L_OpenLibs(); _lua.L_RequireF("mylib", OpenLib, true);
Done! Now you can do this:
string lua_script = @" local lib = require ""mylib"" lib.trace(""Test output"") ";
It would seem that all? But no. Now the most difficult.
Yield
A little thought, it can be understood that our Lua script should not run continuously. There will obviously be pauses, waiting for the end of some kind of animation, keystroke, etc. That is, the script should return control back to the Sharps, and then, at some point, continue.
It was here that I broke a lot of copies. Explanatory description of how to do this was very difficult to find (and that was for another library).
The first thing we need to do is to run the script not by Call, but through a separate stream:
Now imagine that we in C # wrote the function “wait for the animation to end” (
L_WaitForAnimationStop
), which we call from Lua. The implementation here may be different, then I will describe the general principle.
In this function, we need to hang up some callback at the end of this animation, and most importantly, instead of
return 1
we should do this:
private int L_WaitForAnimationStop(ILuaState s) {
And directly in the callback, we will need to continue executing the script from the place where it stopped.
if (_temp_state.GetTop() > 0) _thread.Resume(null, 0);
That's all. Now script type:
lib.trace("starting") lib.wait_for_animation_stop() lib.trace("stopped")
after
lib.wait_for_animation_stop()
pause and continue only when you want it (i.e. in the case described above, call the callback, which
Resume()
will do).
What has been achieved
Using the above method, as well as
shamanism to simulate OOP , we managed to achieve the following syntax:
local ch1 = CharacterGfx() ch1.create("char_0") local ch2 = CharacterGfx() ch2.create("char_1") ch1.moveto("workout") ch2.moveto("fridge") ch2.wait_move_finish() ch1.wait_move_finish() vh.trace("finished ok")
The script creates two sprites of characters, moves the first one to the “workout” point, the second one - to the “fridge” point, then waits for both to finish their movement, and only then writes “finished ok”.
From the documentation I can advise only the
Lua 5.2 Reference Manual , where all these shamanism are described, albeit slightly for a different implementation.
All articles in the series:- Idea, vision, choice of setting, platform, distribution model, etc.
- Shaders for styling images under the CRT / LCD
- We fasten the scripting language to Unity (UniLua)
- Shader for fade in on the palette (a la NES)
- Subtotal (prototype)
- Let's talk about the indie games
- 2D animations in Unity ("as in flash")
- Visual scripting of cut scenes in Unity (uScript)