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 theself
argument 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 theself
argument 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 -- passesself
to 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 fromself
print(self.some_property) --> "Hello!" self.some_property = "another value" print(self.some_property) --> "another value" -- This does not error, you simply get anil
value 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 inMyClass
are -- 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 theexport
function to export properties MyClass.some_value = export("") MyClass.some_typed_value = export({ type = Array, }) -- Special export hints needhint
and 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 asget_node("child_node")
some_child_node = get_node("child_node")
Lua:
local MyClass = { extends = Node } function MyClass:_ready() -- Lua does not have the$node_path
syntax -- Use theget_node
method 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 theonready
syntax -- Just get the value in_ready
method 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 withoutself.
bypasses setter with_setter = new_value # Calling func _ready(): # usingself.
calls the setter function self.with_setter = 'set via setter' # withoutself.
, 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, callget
orsafe_get
directly assert(array:get(0) == 1) assert(array:safe_get(0) == 1) -- !!! WARNING:get
will 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, callset
orsafe_set
directly -- !!! WARNING:set
will crash your game if out of bounds !!! --safe_set
may resize the array before setting the value array:safe_set(3, 4) -- Accessing out of bounds elements is not an error --nil
is returned print(array[10]) --> nil -- Get element count with the length operator#
print(#array) -- Thesize
method 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)