Skip to content

Scripting UI ​

UI belongs to each player, so it is driven by ClientScripts. You can build it in KHIM Engine (Designing UI) and script it, or build it entirely from code.

Reaching UI made in KHIM Engine ​

Each player gets a copy of everything under UI when they join. A ClientScript inside a UI object has it as script.parent:

lua
-- ClientScript inside the "Shop" Canvas
local shop = script.parent
local buy = shop.Panel.BuyButton

buy.clicked:connect(function()
    net.send("buy", "Sword")
end)

Any other ClientScript finds it in the player's gui:

lua
local shop = players.me.gui:waitFor("Shop")
shop.visible = false

Building UI from code ​

lua
-- ClientScript: a coin counter at the top of the screen
local canvas = new("Canvas", players.me.gui)

local label = new("Label", canvas)
label.anchor = vec2(0.5, 0)
label.position = dim(0.5, 0, 0, 80)    -- the middle of the screen, 80 points from the top
label.size = dim.px(200, 40)
label.text = "Coins: 0"
label.textSize = 22
label.textColor = rgb(255, 255, 255)
label.backgroundColor = rgb(20, 20, 20)
label.backgroundTransparency = 0.3
new("Corner", label).radius = 12

net.on("coinCollected", function(total)
    label.text = "Coins: " .. total
end)

Sizes and positions: dim ​

UI is laid out in dim values - a scale (a share of the parent) and an offset (UI points) for X and Y:

lua
dim(xScale, xOffset, yScale, yOffset)
dim(0.5, 0, 0, 40)    -- half the parent's width, 40 points high
dim.scale(1, 1)       -- the parent's whole size
dim.px(200, 50)       -- 200 x 50 points

anchor (a vec2, 0 to 1) is the point of the object that position places: vec2(0.5, 0.5) centres it.

UI points grow with the player's HUD scale, so a layout looks the same on a big monitor. camera.viewportSize gives the screen's size in UI points.

Common properties ​

Every UI object (Panel, Label, Button, TextInput, Image, ImageButton...):

Member
position, sizedim
anchorvec2
backgroundColor, backgroundTransparencyThe box
visibleShown or not
zIndexDrawn over lower ones (never under its parent)
layoutOrderIts place in a ListLayout or GridLayout sorted by LayoutOrder

Text objects (Label, Button, TextInput) add text, textColor, textSize, textAlign ("Left", "Center", "Right"), textWrapped, textScaled and font ("Default", "Bold", "Display").

Images add image (a picture's name - "coin", "icon-star"... - or "asset:123"), imageColor, imageTransparency.

Buttons and input ​

lua
button.clicked:connect(function() print("clicked") end)           -- Button, ImageButton

box.focusLost:connect(function(enterPressed)                       -- TextInput
    if enterPressed then print("typed: " .. box.text) end
end)
box.placeholder = "Enter a code"

KHIM's own controls:

lua
local bar = new("ProgressBar", canvas)       -- health, XP, loading
bar.min, bar.max, bar.value = 0, 100, 75
bar.fillColor = rgb(80, 220, 120)

local slider = new("SliderInput", canvas)    -- the player drags it
slider.min, slider.max, slider.step = 0, 1, 0.05
slider.changed:connect(function(value) print("volume", value) end)

local toggle = new("Toggle", canvas)         -- on/off
toggle.text = "Music"
toggle.changed:connect(function(on) print("music", on) end)

Layout objects ​

Put these inside a UI object to arrange or style it:

lua
local list = new("ListLayout", panel)
list.direction = "Vertical"        -- or "Horizontal"
list.padding = 8                   -- points between children
list.alignX = "Center"
list.sortOrder = "LayoutOrder"     -- or "Name"

local grid = new("GridLayout", panel)
grid.cellSize = dim.px(80, 80)
grid.cellPadding = dim.px(6, 6)

local pad = new("Padding", panel)
pad.top, pad.bottom, pad.left, pad.right = 10, 10, 12, 12

new("Corner", panel).radius = 10

local border = new("Border", panel)
border.color = rgb(255, 255, 255)
border.thickness = 2
border.transparency = 0.6

local grad = new("Gradient", panel)
grad.color0 = rgb(40, 40, 60)
grad.color1 = rgb(10, 10, 20)
grad.rotation = 90                 -- top to bottom
ModifierMembers
AspectRatioaspectRatio, aspectType ("FitWithinMaxSize", "ScaleWithParentSize"), dominantAxis
Scalescale - draws it and its contents bigger or smaller (tween it for a hover effect)
SizeLimitminSize, maxSize (vec2, points)
TextSizeLimitminTextSize, maxTextSize
BorderapplyTo = "Text" outlines the letters instead of the box
GradientcolorMid, midpoint - a third colour in the middle

Scrolling lists ​

A ScrollPanel scrolls its contents with the mouse wheel or a drag:

lua
local scroller = new("ScrollPanel", canvas)
scroller.size = dim.px(300, 400)
scroller.automaticCanvasSize = "Y"     -- grows to fit what is inside
new("ListLayout", scroller).padding = 6

for i = 1, 20 do
    local row = new("Button", scroller)
    row.size = dim(1, 0, 0, 40)
    row.text = "Item " .. i
end

A button inside a ScrollPanel clicks when released without dragging, so scrolling a shop with a finger doesn't buy things on the way.

Animating UI ​

UI objects tween like parts:

lua
panel.position = dim(0.5, 0, 1.2, 0)                        -- off the bottom
panel:tween({position = dim(0.5, 0, 0.5, 0)}, {time = 0.4, style = "Back", direction = "Out"})

local grow = new("Scale", button)
-- on hover, a script can tween grow.scale from 1 to 1.1

Showing a menu over a first-person game ​

In first person or camera lock the pointer is held in the middle. Free it while a menu is open:

lua
input.onKey("B", function()
    shop.visible = not shop.visible
    input.mouseFree = shop.visible
end)

Text over the world ​

For text attached to a part - a name, a price, a health bar over an enemy - use a Billboard on the part instead of screen UI.