Skip to content

Lua scripting

Mikulas Florek edited this page Sep 9, 2023 · 150 revisions

Calls from Lua to engine are not checked, they might crash if called incorrectly.

Lua script plugin provides an option to script using Lua using Luau library. It provides a new type of component, which points to a lua script file. This file is executed when a game starts. Each component has lua sandboxed - it means that all variables and function are not accessible from other components unless they are put in the global table _G or accessed using entity.lua_script[idx]

Typechecking

  1. Download and install [Luau language server(https://marketplace.visualstudio.com/items?itemName=JohnnyMorganz.luau-lsp) for VS Code
  2. open data folder in VS Code
  3. create VS Code workspace settings (data/.vscode/settings.json)
  4. put following json there
{
    "luau-lsp.types.definitionFiles": ["scripts/lumix.d.lua"],
    "luau-lsp.types.roblox": false
}
  1. create .luaurc in data\scripts\ and put following json there
{
	"languageMode": "nonstrict",
	"lint": { "*": true, "FunctionUnused": false },
	"lintErrors": true,
	"globals": ["expect"]
}
  1. typechecking should now work, you can test with following script:
function onInputEvent(event : InputEvent)
    return event.some_prop
end

Update function

Each script can contain function update(dt) and this function is called each frame.

Watch video tutorial

API

See API definition.

Engine

function LumixAPI.logError(msg)
function LumixAPI.instantiatePrefab(universe, position, prefab)

Example:

ext_prefab = -1
Editor.setPropertyType(this, "ext_prefab", Editor.RESOURCE_PROPERTY, "prefab")
-- alternative
-- local ext_prefab = LumixAPI.loadResource(LumixAPI.engine, "models/cube.fab", "prefab")

function start()
local s = 20
for i = 1, s do
    for j = 1, s do
        for k = 1, s do

            local position = { i * 3, j * 3, k*  3 }
            LumixAPI.instantiatePrefab(Lumix.main_universe, position, ext_prefab)
        end
    end
end
end

Universe

Lumix.Universe = {}
function Lumix.Universe:create()
function Lumix.Universe:destroy()
function Lumix.Universe:load(path, callback_fn)
function Lumix.Universe:createEntity()
function Lumix.Universe:createEntityEx(desc)
function Lumix.Universe:findEntityByName(parent, name)

-- `Main` universe can be accessed through `Lumix.main_universe`

Entity

Lumix.Entity = {}
function Lumix.Entity:new(_universe, _entity)
function Lumix.Entity:destroy()
function Lumix.Entity:createComponent(cmp)
function Lumix.Entity:getComponent(cmp)

-- properties
local e = universe:createEntity()
-- e.universe == universe
e.name = "new name"
e.position = {1, 2, 3} -- vec3 x, y, z
e.rotation = {0, 0, 0, 1} -- quaternion x, y, z, w
e.scale = 1.0

Components are directly exposed as properties of entity

-- e is entity
e.gui_rect.top_points = 50
-- is the same as 
e.getComponent("gui_rect").top_points = 50

A new lua object is created every time component is accessed this way, so cache the component to avoid the performance hit

local rect = e.gui_rect
rect.top_points = 50
rect.top_relative = 0.5

You can access environments of other script like this:

local env = e.lua_script[script_index]

Properties

Every Vec3, color, decimal, integer and boolean property (you can see properties in the property grid in the editor) can be accessed from Lua. However name of a property is transformed in following way:

  1. convert to lowercase
  2. replace all nonalphanumeric characters with _,

For example:

-- property Fog color
-- "this" is current Entity accessible in from lua
local e = this.universe:createEntity()
local c = e:createComponent("environment")
c.fog_color = {1, 0, 1}

Types

Other than standard lua types there following types:

  • Vec3 - a table with 3 numbers
  • Quat - a table with 4 numbers

Input system

Constants

Callback

onInputEvent function is called on every input event.

function onInputEvent(event)
	if event.type == "button" then
		if event.device.type == "keyboard" then
			if event.key_id == LumixAPI.INPUT_KEYCODE_UP then
				LumixAPI.logInfo("keyboard button event")
			end
		end		
	elseif event.type == "axis" then
		if event.device.type == "mouse" then
			yaw = yaw + event.x * -0.005;
			pitch = pitch + event.y * -0.005;
		end
	end
end

Physics

If two entities with a physical component collide, onContact function is called in script on both of them:

function onContact(entity)
end

If a physical entity enters a trigger, onTrigger function is called in script on both of them:

function onTrigger(other_entity, touch_lost)
end

Navigation

function onPathFinished() ...

Lua function onPathFinished is called on an entity which finished its path.

ImGui

x = 0
checked = true
function onGUI()
    -- button
    if ImGui.Button("Test") then
        LumixAPI.logError("Test clicked.")
    end

    -- drag float
    local changed = false
    changed, x = ImGui.DragFloat("Drag float", x)
    if changed then
        LumixAPI.logError("X changed, new value = " .. tostring(x))
    end

    -- checkbox
    changed, checked = ImGui.Checkbox("Checkbox", checked)
    if changed then
        if checked then
            LumixAPI.logError("Checkbox is checked")
        else
            LumixAPI.logError("Checkbox is unchecked")
        end
    end
end

Snippets

-- create grid of 50x50x50 cubes
local s = 50
for i = 1, s do
for j = 1, s do
for k = 1, s do

local e = Lumix.main_universe:createEntity()
e.position = { i * 3, j * 3, k*  3 }
local c = e:createComponent("model_instance")
c.source = "models/shapes/cube.fbx"

end
end
end

Clone this wiki locally