The Script component allows you to create game logic using the Lua programming language. Scripts are added to game objects exactly like any other component and Defold will execute the Lua code as part of the engine lifecycle functions.
There are three types of Lua script in Defold, each has different Defold libraries available.
Defold executes Lua scripts as part of the engine lifecycle and exposes the lifecycle through a set of predefined callback functions. When you add a script component to a game object the script becomes part of the game object’s and its component(s) lifecycle. The script is evaluated in the Lua context when it is loaded, then the engine executes the following functions and passes a reference to the current script component instance as parameter. You can use this
self reference to store state in the component instance.
self is a userdata object that acts like a Lua table but you can’t iterate over it with
ipairs() and you can’t print it using
function init(self) -- These variables are available through the lifetime of the component instance self.my_var = "something" self.age = 0 end
function final(self) if self.my_var == "something" then -- do some cleanup end end
dtcontains the delta time since the last frame.
function update(self, dt) self.age = self.age + dt -- increase age with the timestep end
on_message(self, message_id, message, sender)
msg.post()the engine calls this function of the receiver component.
on_input(self, action_id, action)
acquire_input_focus) the engine calls this function when input is registered.
function on_reload(self) print(self.age) -- print the age of this game object end
A game object with a script component implements some logic. Often, that logic is dependent on some external factor. An enemy AI might react to the player being within a certain radius from the AI; a door might unlock and open as a result of player interaction, etc, etc.
update() function allows you to implement complex behaviors defined as a state machine running each frame—sometimes that is the adequate approach. But there is a cost associated with each call to
update(). Unless you really need the function you should delete it and instead try to build your logic reactively. It is cheaper to passively wait for some message to trigger a response than it is to actively probe the game world for data to respond to. Furthermore, solving a design problem reactively also often leads to cleaner and more stable design and implementation.
Let’s look at a concrete example. Suppose that you want a script component to send a message 2 seconds after it has been initiated. It should then wait for a certain response message and after receiving the response, it should send another message 5 seconds later. The non reactive code for that would look something like this:
function init(self) -- Counter to keep track of time. self.counter = 0 -- We need this to keep track of our state. self.state = "first" end function update(self, dt) self.counter = self.counter + dt if self.counter >= 2.0 and self.state == "first" then -- send message after 2 seconds msg.post("some_object", "some_message") self.state = "waiting" end if self.counter >= 5.0 and self.state == "second" then -- send message 5 seconds after we received "response" msg.post("another_object", "another_message") -- Nil the state so we don’t reach this state block again. self.state = nil end end function on_message(self, message_id, message, sender) if message_id == hash("response") then -- “first” state done. enter next self.state = "second" -- zero the counter self.counter = 0 end end
Even in this quite simple case we get fairly tangled up logic. It’s possible to make this look better with the help of coroutines in a module (see below), but let’s instead try to make this reactive and use a built in timing mechanism.
local function send_first() msg.post("some_object", "some_message") end function init(self) -- Wait 2s then call send_first() timer.delay(2, false, send_first) end local function send_second() msg.post("another_object", "another_message") end function on_message(self, message_id, message, sender) if message_id == hash("response") then -- Wait 5s then call send_second() timer.delay(5, false, send_second) end end
This is cleaner and easier to follow. We get rid of internal state variables that are often hard to follow through the logic—and which might lead to subtle bugs. We also dispose of the
update() function completely. That relieves the engine from calling our script 60 times a second, even if it’s just idling.
The Defold editor supports Lua script editing with syntax coloring and auto-completion. To fill out Defold function names, press Ctrl+Space to bring up a list of the functions matching what you are typing.