Windows#
window is the game's own desktop window, and it is a client script global like camera and input.
You read its size, move it, retitle it and watch it for changes. window.open opens further windows
next to it, and window.overlay opens one that covers the whole screen.
The game window#
Title and icon#
window.title = "Crystal Rush"
window.icon = "res://ui/icon.png" -- an exported game uses icon.png at the place root by defaulttitle is the text in the title bar and the taskbar. icon takes a res:// image. An exported game
already uses icon.png at the place root, so you only set icon to change it while the place runs.
Size#
window.fullscreen = true -- for this session; settings.fullscreen is the saved option
window.width, window.height -- read, in pixels
window:resize(1280, 720)
window.minWidth = 800
window.minHeight = 450
window.resizable = falsewindow.fullscreenswitches this session only. The player's saved option issettings.fullscreen, on the settings page, and writing that one outlives the place.window.widthandwindow.heightare read only, in pixels. To change the size, callwindow:resize(width, height).window.minWidthandwindow.minHeightstop the player dragging the window smaller than your interface can cope with. Leave them alone and any size is allowed.window.resizable = falsetakes the drag handles away.
Position#
window.x, window.y -- read and write, in desktop pixels
window:moveTo(200, 120)
window:center()
window.canMove -- false only where the system refuses to move windowsx and y are in desktop pixels and you can both read and write them. window:moveTo(x, y) does the
same in one call, and window:center() centres the window. window.canMove is false only on a system
that refuses to move windows, and there a move does nothing.
State, cursor and screen#
window.focused -- read
window.minimized -- read
window.visible = false -- hide it, single player and exported games only
window.opacity = 0.85 -- needs a desktop that composites windows
window.cursor = "hand" -- arrow, hand, text, crosshair, resizeEW, resizeNS, resizeAll,
-- notAllowed, or "res://ui/cursor.png"
window.cursorHotspot = vec3(9, 0, 0) -- the pixel of the image that clicks
window.cursorVisible = false -- while the pointer is free
window.fps -- read
window.displayWidth, window.displayHeight -- the monitor the window is on
window:monitors() -- { { name, x, y, width, height, workX, workY, workWidth, workHeight, refreshRate, primary } }
window:flash() -- blinks in the taskbar
window:setClipboard("ABC-123") -- there is no reading the clipboardwindow.focusedandwindow.minimizedare read only. They tell you whether the player is looking at the game right now.window.visible = falsehides the game window. It works in single player and exported games only.window.opacitymakes the whole window see-through, and needs a desktop that composites windows.window.cursortakes one of the named cursors or ares://image of your own. Set it to"hand"over a clickable thing and back to"arrow"when the pointer leaves.window.cursorHotspotis the pixel of ares://cursor image that points, x and y counted from its top left corner. It starts atvec3(0, 0, 0), the corner, which suits an arrow; the tip of a finger nine pixels in isvec3(9, 0, 0), and the middle of a 32 pixel crosshair isvec3(16, 16, 0). It is in the image's own pixels and is kept inside the image.window.cursorVisibleapplies while the pointer is free.window.fpsis the frame rate the game is running at, andwindow.displayWidthandwindow.displayHeightare the size of the monitor the window is on. All three are read only.window:monitors()returns one table per monitor, with its position and size, the work area (workX,workY,workWidth,workHeight), itsrefreshRateand whether it is theprimaryone.window:flash()blinks the window in the taskbar, which gets the attention of a player who has alt-tabbed away.window:setClipboard(text)puts text on the system clipboard. There is no reading the clipboard, so a place can never look at what the player copied elsewhere.
Signals#
window.resized:connect(function(width, height) end)
window.moved:connect(function(x, y) end)
window.focusChanged:connect(function(focused) end)resized and moved fire when the player drags the window and when a script changes it.
focusChanged is handed true when the window takes focus and false when it loses it.
Everything here goes back to normal when the scripts reload.
Moving the window#
Moves apply on the frame they are asked for, and window.x and window.y answer what was set rather
than the position the system last reported, so a script can move the window every frame. There is no
shake function, because a shake is a move:
local function shake(strength: number, time: number)
local baseX, baseY = window.x, window.y
local left = time
local connection
connection = game.renderStepped:connect(function(dt)
left -= dt
if left <= 0 then
window:moveTo(baseX, baseY)
connection:disconnect()
return
end
local power = strength * (left / time)
window:moveTo(baseX + math.random(-power, power), baseY + math.random(-power, power))
end)
end- The base position is read once, before the shake starts, so the window comes back to where it began.
leftcounts down by the frame'sdt, andpowershrinks with it, so the shake fades out instead of stopping dead.- The connection disconnects itself on the last frame. Without that, the handler would keep running for the rest of the session.
Minecraft runs through XWayland on Wayland desktops, so moving works there too. A player who
switched Minecraft to native Wayland gets window.canMove == false, and moves do nothing for them.
Screenshots#
local path = window:screenshot("boss-room") -- "screenshots/boss-room.png"window:screenshot(name) saves what the window shows, interface included, as a PNG in the
screenshots folder of the game folder, and returns its path. The name may use letters, digits, -
and _; leave it out and the file is named after the time. The picture is written a moment later,
and game:quit() waits for it.
Closing#
window.closing fires when the player tries to close the game window. Calling window:preventClose()
inside the handler keeps it open:
window.closing:connect(function()
window:preventClose()
saveMenu.enabled = true -- "quit without saving?"
end)A place can hold the window open once. Closing it again within 10 seconds always closes it, so a game can never trap the player.
More windows#
window.open(properties) opens another desktop window and returns it. The window is an instance of
the Window class, so you set its properties whenever you like and tween them:
local popup = window.open({
title = "Warning",
width = 320, height = 180,
x = 200, y = 200,
decorated = false, -- no title bar
transparent = true, -- see-through where nothing is drawn; only when opening
alwaysOnTop = true,
resizable = false,
gui = world:find("WarningGui"),
})
popup:center()popup:tween({ x = popup.x + 600 }, { time = 0.4, easing = "back", direction = "out" })
popup.opacity = 0.5
popup.title = "Still here"| Property | |
|---|---|
title, x, y, width, height |
the player dragging or resizing it writes these back |
visible, decorated, alwaysOnTop, resizable, opacity |
applied straight away |
transparent |
only read when the window opens |
clickThrough |
the mouse passes through to whatever is behind |
clickable |
with clickThrough, a gui object inside gui that still catches the mouse |
focused |
read |
gui |
a ScreenGui drawn in the window instead of on the game screen, clicks and typing included |
camera |
a Camera whose view of the world's parts and models is drawn, with gui on top |
mirror |
shows what the game window shows |
Because the player writes title, x, y, width and height back by dragging, reading them tells
you where the window ended up, not only where you put it. transparent is the one property the window
only reads as it opens, so decide it in the table you pass to window.open.
popup:moveTo(900, 400)
popup:resize(400, 240)
popup:focus()
popup:close() -- or popup:destroy()
popup.moved:connect(function() end) -- the player dragged it
popup.resized:connect(function() end)
popup.closing:connect(function()
popup:preventClose() -- same rule as the game window: the second close wins
end)What a window can show:
- A
guiis cheap: it is drawn once more into the window. - A
cameradraws the world's parts and models again from that camera, like aViewportFrame. Blocks are not drawn: Minecraft can only draw its world once per frame. mirroris the cheapest: it shows the finished game frame.
Windows a script opens close when the scripts reload.
Overlays#
An overlay is a window that covers the whole primary monitor, has no title bar, is see-through and always on top, and lets the mouse through. It is how a character walks on the desktop or a hint floats over other programs.
local overlay = window.overlay({ gui = world:find("DesktopPet") })
overlay.clickable = world:find("DesktopPet/Pet") -- only the pet catches clickswindow.overlay(properties) is window.open with those settings filled in first. Anything you pass
wins, so you can take one of them back. Setting clickable to a gui object inside gui carves a hole
in the click-through: the pet above catches clicks while the rest of the screen still belongs to
whatever program is behind it.
Limits#
- Extra windows, overlays and hiding the game window only work in single player and exported games. On a server with other players they are refused with a warning.
- See-through windows and opacity need a desktop that composites windows. Nearly every desktop does. Without one they are drawn black where they should be see-through.
Ctrl + Shift + Escape, pressed in any of the game's windows, closes every extra window and overlay and shows the game window again. It is there for the player, and for you when an overlay covers the screen and the script that opened it has stopped answering.
