Table of contents:
WARNING!!! this is a alpha version of the module made for Godot v4-alpha. Please see the branch v1.1-stable for a more stable build.
This is a Godot engine module that adds lua support via GDScript. Importantly this is NOT meant to be a replacement for GDScript. The main purpose of this module is to add runtime execution of code for tasks such as modding or in game scripting.
While the original purpose of this module was for my own use I understand more may find it useful. If a feature is missing that you would like to see feel free to create a Feature Request or submit a PR
To use you can either Compile from source or you can download one of the nightly builds.
By default the lua print function is set to print to the GDEditor console. This can be changed by exposing your own print function as it will overwrite the existing one.
- Run lua directly from a string or a text file.
- Push any Variant as a global.
- Call lua functions from GDScript.
- Choose which libraries you want lua to have access to.
- Custom LuaCallable type which allows you to get a lua function as a Callable. See examples below.
- LuaError type which is used to report any errors this module or lua run into.
- LuaThread type which creates a lua thread. This is not a OS thread but a coroutine.
- Object passed as userdata. See examples below.
- Objects can override most of the lua metamethods. I.E. __index by defining a function with the same name.
- Callables passed as userdata, which allows you to push a Callable as a lua function. see examples below.
- Basic types are passed as userdata (currently: Vector2, Vector3, Color, Rect2, Plane) with a useful metatable. This means you can do things like:
local v1 = Vector2(1,2)
local v2 = Vector2(100.52,100.83)
v2 = v2.floor()
print(v2.x) -- "100"
print(v1+v2) -- "(101,102)"
change_my_sprite_color(Color(1,0,0,1)) -- if "change_my_sprite_color" was exposed, in GDScript it will receive a Color variant.
- π§ Linux Editor
- π§ Linux Templates
- π¨ Windows Editor
- π¨ Windows Templates
- π MacOS Editor
- π MacOS Templates
- Finish v2 documentation
- Stability testing
- More up to date todo list on the v2 project
This build is for godot 4.0.0-alphaX. X being the latest version. Will not be supporting older alpha builds.
-
Start by cloning the Godot 4.0.0-alpha source with this command
git clone https://github.com/godotengine/godot
-
Next change directories into the modules folder and clone this repo with this command
git clone https://github.com/Trey2k/lua
-
Now you can follow the Godot build instructions on their site.
Running lua from a string:
extends Node2D
var lua: Lua
func _ready():
lua = Lua.new()
lua.do_string("for i=1,10,1 do print('Hello lua!') end")
Running lua from a file:
extends Node2D
var lua: Lua
func _ready():
lua = Lua.new()
lua.do_file("user://luaFile.lua")
Pushing a Variant as a global:
extends Node2D
var lua: Lua
var test = "Hello lua!"
func _ready():
lua = Lua.new()
lua.push_variant(test, "str")
lua.do_string("print(str)")
Exposing a GDScript function to lua:
extends Node2D
var lua: Lua
func luaAdd(a, b):
return a + b
func _ready():
lua = Lua.new()
# Functions are passed the same as any other value to lua.
lua.push_variant(luaAdd, "add")
lua.push_variant(func(a, b): return a+b, "addLamda")
lua.do_string("print(add(2, 4), addLamda(3,3))")
Calling a lua function from GDScript:
extends Node2D
var lua: Lua
func _ready():
lua = Lua.new()
lua.do_file("user://luaFile.lua")
if( lua.function_exists("set_colors")):
# call_function will return a Variant if lua returns nothing the value will be null
var value = lua.call_function("set_colors", ["red", "blue"])
if value != null:
print(value)
else:
print("no value returned")
if( lua.function_exists("set_location")):
# Assumeing lua defines a function set_location this will return a callable which you can use ot invoke the lua function
var set_location = lua.pull_variant("set_location")
var value2 = set_location.call(Vector2(1, 1))
if value2 != null:
print(value2)
else:
print("no value returned")
Error handling:
extends Node2D
var lua: Lua
func test(n: int):
if n != 5:
# This will raise a error in the lua state
return LuaError.new_err("N is not 5 but is %s" % n, LuaError.ERR_RUNTIME)
return n+5
func _ready():
lua = Lua.new()
lua.push_variant(test, "test")
# Most methods return a LuaError
# calling test with a type that is not a int would also raise a error.
var err = lua.do_string("test(6)")
# the static method is_err will check that the variant type is LuaError and that the errorType is not LuaError.ERR_NONE
if LuaError.is_err(err):
print("ERROR %d: " % err.type + err.msg)
Bind libraries:
extends Node2D
var lua: Lua
func _ready():
lua = Lua.new()
#all libraries are avalible. Use OS and IO at your own risk.
lua.bind_libs(["base", "table", "string"])
Passing objects as userdata:
extends Node2D
var lua: Lua
class Player:
var pos = Vector2(0, 0)
#If lua_funcs is not defined or returns a empty array, all functions will be aval
func lua_funcs():
return ["move_forward"]
#lua_fields behaves the same as lua_funcs but for fields.
func lua_fields():
return ["pos"]
func move_forward():
pos.x+=1
var player2: Player
func _ready():
lua = Lua.new()
player2 = Player.new()
lua.push_variant(func(): return player2, "getPlayer2")
lua.expose_constructor(Player, "Player")
lua.do_string("player = Player() player.move_forward() print(player.pos.x)")
lua.do_string("player2 = getPlayer2() player2.pos = Vector2(50, 1) print(player2.pos)")
var player = lua.pull_variant("player")
print(player.pos)
print(player2.pos)
Object metamethod overrides:
extends Node2D
var lua: Lua
class Player:
var pos = Vector2(1, 0)
# Most metamethods can be overriden. The function names are the same as the metamethods.
func __index(index):
if index=="pos":
return pos
else:
return LuaError.new_err("Invalid index '%s'" % index)
func move_forward():
pos.x+=1
func _ready():
lua = Lua.new()
lua.expose_constructor(Player, "Player")
var err = lua.do_string("player = Player() print(player.pos.x) player.move_forward() -- this will cause our custom error ")
if LuaError.is_err(err):
print(err.msg)
var player = lua.pull_variant("player")
print(player.pos)
Using Coroutines:
extends Node2D
var lua: Lua
var thread: LuaThread
func _ready():
lua = Lua.new()
# Despite the name this is not like a OS thread. It is a coroutine
thread = LuaThread.new_thread(lua)
thread.load_string("
while true do
-- yield is exposed to lua when the thread is bound.
yield(1)
print('Hello world!')
end
")
var yieldTime = 0
var timeSince = 0;
func _process(delta):
timeSince += delta
if thread.is_done() || timeSince <= yieldTime:
return
# thread.resume will either return a LuaError or a Array.
var results = thread.resume()
if LuaError.is_err(results):
print("ERROR %d: " % results.type + results.msg)
return
yieldTime = results[0]
timeSince = 0
All contributions are welcome, if you would like to contribute submit a PR.
Additionally if you do not have the time and or the knowledge you can create a Feature Request