From GDScript to Lua
Some examples on how to translate GDScript code to Lua.
This document assumes you know how to write GDScript code. Check out GDScript Basics for more detail into the GDScript language.
Empty script
GDScript:
extends Node # optionally, give your class a name class_name MyClass
Lua:
local MyClass = { extends = Node, -- optionally, give your class a name class_name = 'MyClass', } -- Returning the class table is mandatory when creating Godot Lua scripts return MyClass
Declaring methods
GDScript:
extends Node
func _ready():
print("I'm ready!")
func receives_argument(arg1):
pass
func returns_value():
return 42
Lua:
local MyClass = { extends = Node, } -- Use:to declare methods, so the function implicitly -- declares theselfargument function MyClass:_ready() print("I'm ready!") end -- ^ The above is the same as: function MyClass._ready(self) print("I'm ready!") end -- You need theselfargument to access properties and -- other methods on the object instance function MyClass:receives_argument(arg1) end function MyClass:returns_value() return 42 end -- Assigning functions to the class table works too MyClass.another_function = function(self, delta) end return MyClass
Calling methods on instance
GDScript:
extends Node
func _ready():
set_process(true)
Lua:
local MyClass = { extends = Node } function MyClass:_ready() -- Object methods need to be accessed fromself-- Note the method call notation with:self:set_process(true) -- Just like when defining methods, the:implicitly -- passesselfto the function, so both are the same self:set_process(true) self.set_process(self, true) end return MyClass
Lua function call reference, explaining the method syntax sugar: https://www.lua.org/manual/5.1/manual.html#2.5.8
Declaring and accessing properties
GDScript:
extends Node var some_property = "Hello!" func _ready(): print(some_property) #--> "Hello!" some_property = "another value" print(some_property) #--> "another value" print(property_not_declared) #--> Error!!! # ^ identifier not declared in current scope another_property_not_declared = "value" #--> Error!!! # ^ identifier not declared in current scope
Lua:
local MyClass = { extends = Node } MyClass.some_property = "Hello!" MyClass.some_property_with_metadata = property({ default = 42, type = int, }) function MyClass:_ready() -- object properties need to be accessed fromselfprint(self.some_property) --> "Hello!" self.some_property = "another value" print(self.some_property) --> "another value" -- This does not error, you simply get anilvalue print(self.property_not_declared) --> nil -- This does not error, the value is set correctly self.another_property_not_declared = "value" -- WARNING: properties not declared inMyClassare -- only available in Lua -- GDScript / C# will not be able to access them end return MyClass
Declaring exported properties
GDScript
extends Node export var some_value = "" export(Array) var some_typed_value export(int, 1, 10) var some_int_from_1_to_10 = 1
Lua:
local MyClass = { extends = Node } -- Use theexportfunction to export properties MyClass.some_value = export("") MyClass.some_typed_value = export({ type = Array, }) -- Special export hints needhintand optionallyhint_string-- It's not a nice API, as it just interfaces directly with GDNative -- Maybe someday we'll implement something nicer... MyClass.some_int_from_1_to_10 = export({ default_value = 1, type = int, hint = PropertyHint.RANGE, hint_string = "1,10", }) return MyClass
As noted in the limitations document, instances in editor are not reloaded when a script is edited.
That means that adding/removing/updating an exported property won't show in
the editor and tool scripts won't be reloaded until the project is reopened.
Getting nodes
GDScript:
extends Node
func _ready():
var some_child_node = $child_node
# $child_node is the same as get_node("child_node")
some_child_node = get_node("child_node")
Lua:
local MyClass = { extends = Node } function MyClass:_ready() -- Lua does not have the$node_pathsyntax -- Use theget_nodemethod explicitly instead local some_child_node = self:get_node("child_node") end return MyClass
onready properties
GDScript:
extends Node
onready var some_child_node = $child_node
# ^ it's the same as getting the value in _ready method:
#
# var some_child_node
#
# func _ready():
# some_child_node = $child_node
Lua:
local MyClass = { extends = Node } -- Lua does not have theonreadysyntax -- Just get the value in_readymethod explicitly function MyClass:_ready() local some_child_node = self:get_node("child_node") end return MyClass
Property setter functions
GDScript:
extends Node
# Declaring
var with_setter setget setter_function
func setter_function(new_value):
print('with_setter new value = ' + new_value)
# setting value without self. bypasses setter
with_setter = new_value
# Calling
func _ready():
# using self. calls the setter function
self.with_setter = 'set via setter'
# without self., setter function won't be called
with_setter = 'this will not call setter function'
Lua:
local MyClass = { extends = Node } -- Declaring MyClass.with_setter = property({ type = String, set = function(self, new_value) print('with_setter new value = ' .. new_value) -- bypass call to setter function using rawset self:rawset('with_setter', new_value) end, }) -- Calling function MyClass:_ready() -- using indexing syntax calls the setter functions self.with_setter = 'set via setter' -- to bypass setter functions, use the rawset method self:rawset('with_setter', 'this will not call setter function') end return MyClass
Property getter functions
GDScript:
extends Node # Declaring var with_getter setget , getter_function = 'default value' func getter_function(): return 'constant from getter' # Calling func _ready(): # usingself.calls the getter function print(self.with_getter) #--> 'constant from getter' # withoutself., getter function won't be called print(with_getter) #--> 'default value'
Lua:
local MyClass = { extends = Node } -- Declaring MyClass.with_getter = property({ default = 'default value', type = String, get = function(self) return 'constant from getter' end, }) -- Calling function MyClass:_ready() -- using indexing syntax calls the getter function print(self.with_getter) --> 'constant from getter' -- to bypass getter functions, use the rawget method print(self:rawget('with_getter')) --> 'default value' end return MyClass
Arrays
GDScript:
extends Node
func _ready():
# Creating an array
var array = [1, 2, 3]
# Accessing individual elements
assert(array[0] == 1)
assert(array[1] == 2)
assert(array[2] == 3)
# Setting elements
array[3] = 4
# Accessing out of bounds elements
print(array[10]) #--> Error!!!
# ^ Invalid get index '10'
# Get element count
print(array.size())
# Iterating
for array_element in array:
print(array_element)
Lua:
local MyClass = { extends = Node } function MyClass:_ready() -- Creating an array local array = Array(1, 2, 3) -- Accessing individual elements -- In Lua, arrays are indexed from 1 just like Lua tables assert(array[1] == 1) assert(array[2] == 2) assert(array[3] == 3) -- To use 0-based indexing, callgetorsafe_getdirectly assert(array:get(0) == 1) assert(array:safe_get(0) == 1) -- !!! WARNING:getwill crash your game if out of bounds !!! --safe_get, on the other hand, will just returnnil-- Setting elements array[4] = 4 -- To use 0-based indexing, callsetorsafe_setdirectly -- !!! WARNING:setwill crash your game if out of bounds !!! --safe_setmay resize the array before setting the value array:safe_set(3, 4) -- Accessing out of bounds elements is not an error --nilis returned print(array[10]) --> nil -- Get element count with the length operator#print(#array) -- Thesizemethod works as well print(array:size()) -- Iterating for iteration_index, array_element in ipairs(array) do print(iteration_index, array_element) end end return MyClass
Declaring and emitting signals
GDScript:
extends Node
signal my_signal()
signal my_signal_with_args(arg1, arg2)
func _ready():
emit_signal('my_signal')
emit_signal('my_signal_with_args', 'hello', 'world')
Lua:
local MyClass = { extends = Node } MyClass.my_signal = signal() MyClass.my_signal_with_args = signal('arg1', 'arg2') function MyClass:_ready() self:emit_signal('my_signal') self:emit_signal('my_signal_with_args', 'hello', 'world') end return MyClass
Loading Resources
GDScript:
extends Node
func _ready():
var game_icon = load("res://icon.png")
Lua:
local MyClass = { extends = Node } function MyClass:_ready() local game_icon = GD.load("res://icon.png") end return MyClass
Coroutines and signals
GDScript (reference docs: https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html#coroutines-signals):
extends Node
func _ready():
print('This happens some frame')
yield(get_tree(), 'idle_frame') # await for next frame
print('And this happens in the next frame')
Lua:
local MyClass = { extends = Node } MyClass.my_signal = signal() MyClass.my_signal_with_args = signal('arg1', 'arg2') function MyClass:_ready() print('This happens some frame') GD.yield(get_tree(), 'idle_frame') -- await for next frame print('And this happens in the next frame') end return MyClass
GD.yield should be used instead of Lua's coroutine.yield if you want to wait for Godot Objects' signals. Lua methods that call GD.yield return LuaCoroutine objects, which are analogous to GDScript's GDScriptFunctionState.
Tool scripts
GDScript:
tool extends Node
Lua:
local MyClass = { is_tool = true, extends = Node, } return MyClass
TODO: dictionary (mainly pairs)