Skip to content

Scripting basics ​

KHIM games are scripted in Luau, the fast, safe language Roblox made from Lua, with KHIM's own small API on top. If you have written any Lua, JavaScript or Python, you'll read it straight away.

Three kinds of script ​

KindRuns onUse it for
ScriptThe game server - onceThe game itself: health, coins, scores, rounds, doors, anything that matters
ClientScriptEvery player's device - once eachInput, UI, the camera, effects only one player sees
ModuleWherever it is requiredCode and settings shared by several scripts

Add one with Insert Object (right-click a row in the Hierarchy) or the top bar's Insert menu. Double-click it to open it.

Where a script sits decides whether it runs:

  • A Script runs anywhere except StorageService and UI. Put game logic in ServerService, where players never see it, or inside the part it controls.
  • A ClientScript runs on every player unless it sits in ServerService or StorageService. Put it in ClientService, or inside the UI it drives.
  • A Module runs the first time something requires it. Shared ones go in ReplicationService (shared in scripts).

KHIM Engine dims a script that won't run where it is, and its tooltip says why.

Your first script ​

Insert a Part, then right-click it in the Hierarchy > Insert Object > Script. Open the script and type:

lua
local part = script.parent  -- the part this script is in

part.touched:connect(function(hit)
    local player = players.fromPart(hit)
    if player then
        print(player.name .. " touched me!")
        part.color = rgb(255, 80, 80)
    end
end)

Press F5, walk into the part: it turns red and the Console says who touched it.

Three rules cover the whole API ​

  1. Members are camelCase: part.position, player.walkSpeed, button.clicked.
  2. Modules use a dot, objects use a colon: net.send(...), players.all() - but part:tween(...), folder:find("Coin").
  3. A choice between a few options is a string: part.shape = "Ball", label.textAlign = "Center", input.onKey("E", fn).

Finding things ​

Everything in the world is an object with a name, a parent and children:

lua
local door = world.Door                 -- a child by name
local lamp = world.House.Kitchen.Lamp   -- deeper
local tree = world:find("Big Tree")     -- names with spaces
local shop = world:waitFor("Shop")      -- waits until it exists
local config = require(shared.Config)   -- a Module in ReplicationService
worldThe World container: every part players see
sharedReplicationService
game.ServerService, game.StorageService, game.ClientServiceThe other containers
scriptThis script; script.parent is what it is in
players.meIn a ClientScript: the player whose device it is

find(name) returns nil when there is nothing by that name; waitFor(name) waits for it (and warns after 5 seconds). Use waitFor in ClientScripts, where the world arrives a piece at a time.

Events ​

Things that happen are events. Connect a function to one and it runs every time:

lua
local connection = part.touched:connect(function(hit) print("touched by", hit.name) end)
connection:disconnect()                        -- stop listening

part.touched:once(function(hit) print("only the first time") end)

local hit = part.touched:wait()                -- wait for the next time, then go on

Common ones: players.joined, players.left, part.touched, button.clicked, prompt.triggered, player.died, game.frame (every frame, with the time since the last).

Waiting and time ​

lua
task.wait(2)                                    -- wait 2 seconds (without freezing the game)
task.spawn(function() print("alongside") end)   -- run something alongside
task.delay(5, function() print("later") end)    -- run something in 5 seconds
print(time())                                   -- seconds since the game started

Loops need a wait

A script that runs for more than one second without waiting is stopped. Every while true do loop needs a task.wait() inside it.

Values ​

lua
local spot = vec3(0, 10, 0)              -- a position or direction
local red = rgb(255, 0, 0)               -- a colour, 0-255 (or rgb("#ff0000"))
local sky = hsv(0.6, 0.5, 1)             -- a colour from hue, saturation, value (0-1)
local where = transform.lookAt(vec3(0, 5, 10), vec3(0, 0, 0)) -- a position and a facing
local size = dim(0.5, 0, 0, 40)          -- UI: half the width, 40 points high

See Value types.

Making objects ​

lua
local ball = new("Part")
ball.shape = "Ball"
ball.size = vec3(2, 2, 2)
ball.position = vec3(0, 20, 0)
ball.anchored = false
ball.parent = world        -- nothing appears until it has a parent

local copy = world.Tree:clone()
copy.parent = world

ball:destroy()             -- gone, with everything inside it
ball:destroyAfter(5)       -- gone in 5 seconds

new(className, parent) takes the parent as a second argument too.

Moving things smoothly ​

lua
local door = world.Door
door:tween({position = door.position + vec3(0, 8, 0)}, 1)                -- up 8 units over 1 second
door:tween({color = rgb(0, 255, 0), transparency = 0.5}, {time = 2, style = "Bounce"})

Parts, UI objects, Lighting and the camera all tween. Options: time, style ("Linear", "Sine", "Quad", "Cubic", "Quart", "Back", "Bounce", "Elastic"), direction ("In", "Out", "InOut"), repeats (-1 forever), reverses, delay. A tween returns an object with completed and cancel().

Modules ​

A Module returns a value - usually a table - and every require of it gets the same one:

lua
-- Module "Config" in ReplicationService
return {
    startingCoins = 50,
    swordDamage = 20,
}
lua
-- any Script or ClientScript
local Config = require(shared.Config)
print(Config.startingCoins)

A Module runs once per server and once per player's device.

The editor helps ​

  • Suggestions as you type: after . and : it lists what fits there - players. lists joined, left, all...; inside players.joined:connect(function(player), player. lists a player's members. Ctrl Space asks anywhere.
  • Snippets: type touched, savedata or whileloop at the start of a line - see Snippets.
  • Hover a name to see its type and what it does.
  • Mistakes are underlined as you type, with a "did you mean": 'pirnt' is not defined; did you mean print?. A misspelt member at run time says it too: postion is not a valid member of Part; did you mean position?.
  • F12 on world.Door.Hinge selects that Hinge in the Hierarchy.

Roblox names work too ​

If you know Roblox, its names still run: game:GetService("Players"), Instance.new("Part"), Vector3.new, part.Position, :Connect, FindFirstChild, RemoteEvent and so on. The docs use KHIM's names; Coming from Roblox has the table.

Next ​