Scripts

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.

Script types

There are three types of Lua script in Defold, each has different Defold libraries available.

Logic scripts
Extension .script. Run by script components in game objects. Logic scripts are usually used to control game objects and the logic that binds the game together with level loading, game rules and so forth. Logic scripts have access to all Defold library functions except GUI and Render functions.
GUI scripts
Extension .gui_script. Run by GUI components and usually containing the logic required to display GUI elements like heads up displays, menus etc. GUI scripts have access to the GUI library functions.
Render scripts
Extension .render_script. Run by the rendering pipeline and containing the logic required to render all app/game graphics each frame. Render scripts have access to the Render library functions.

Script execution, callbacks and self

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 pairs() or ipairs() and you can’t print it using pprint().

init(self)
Called when the component is initialized.
function init(self)
    -- These variables are available through the lifetime of the component instance
    self.my_var = "something"
    self.age = 0
end
final(self)
Called when the component is deleted. This is useful for cleaning up purposes, for instance if you have spawned game objects that should be deleted when the component is deleted.
function final(self)
    if self.my_var == "something" then
        -- do some cleanup
    end
end
update(self, dt)
Called once each frame. dt contains the delta time since the last frame.
function update(self, dt)
    self.age = self.age + dt -- increase age with the timestep
end
fixed_update(self, dt)
Frame-rate independent update. dt contains the delta time since the last update. This function is called when engine.fixed_update_frequency is enabled (!= 0). Useful when you wish to manipulate physics objects at regular intervals to achieve a stable physics simulation when physics.use_fixed_timestep is enabled in game.project.
function fixed_update(self, dt)
    msg.post("#co", "apply_force", {force = vmath.vector3(1, 0, 0), position = go.get_world_position()})
end
on_message(self, message_id, message, sender)
When messages are sent to the script component through msg.post() the engine calls this function of the receiver component. Learn more about message passing.
  function on_message(self, message_id, message, sender)
      if message_id == hash("increase_score") then
          self.total_score = self.total_score + message.score
      end
  end
on_input(self, action_id, action)
If this component has acquired input focus (see acquire_input_focus) the engine calls this function when input is registered. Learn more about input handling.
  function on_input(self, action_id, action)
      if action_id == hash("touch") and action.pressed then
          print("Touch", action.x, action.y)
      end
  end
on_reload(self)
This function is called when the script is reloaded through the hot reload editor function (Edit ▸ Reload Resource). It is very useful for debugging, testing and tweaking purposes. Learn more about hot-reload.
function on_reload(self)
    print(self.age) -- print the age of this game object
end

Reactive logic

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.

The 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.

Preprocessing

It is possible to use a Lua preprocessor and special markup to conditionally include code based on the build variant. Example:

-- Use one of the following keywords: RELEASE, DEBUG or HEADLESS
--#IF DEBUG
local lives_num = 999
--#ELSE 
local lives_num = 3
--#ENDIF

The preprocessor is available as a build extension. Learn more about how to install and use it on the extension page on GitHub.

Editor support

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.

Auto completion