Getting started#
This page walks through a whole place, from the project hub to a floor you can stand on and a camera that switches to third person when you sneak. It takes two files and no setup beyond creating the project.
Create a place#
Start Moud and the title screen opens the project hub. From the repository, that is
./gradlew :mod:runClient with no -Pplace. New project asks for a name, a folder (Moud in
your home folder unless you pick another) and one of three starts:
- Baseplate gives you a 128 m floor to build on. Use it when you want to try something out straight away.
- Main menu gives you a menu drawn over a camera flying along a
CameraPath. Play hands the player a body, Escape opens a pause menu, andplace.tomlsets a loading screen. Players wait in the menu because the template setsgame.players.autoSpawn = false. The menus are built inclient/main.luau, and the scene holds the floor, theSpawnLocation, the camera path and theMenuremote. - Empty gives you a place with nothing in the scene.
Each template is a folder of ordinary files. Nothing in it is generated at build time, so you can read and change every part of it.
Pick Empty for this page, so the floor you stand on is the one your script makes. Create
opens the new place in the editor. Its server/main.luau and client/main.luau only print a line
each; the next two sections replace them. Right click the project in the hub and choose
Show the folder to find the files, and see
Editing scripts in your code editor to set up
completion for them.
Write the server script#
server/main.luau runs on the server. It builds the world and decides what happens to the players in
it:
local world = game.world
world:add("Part", {
name = "floor",
size = vec3(40, 1, 40),
cframe = cframe(0, 64, 0),
color = color(0.35, 0.42, 0.38),
anchored = true,
})
game.players.joined:connect(function(player)
local body = player:spawn(vec3(0, 66, 0))
body.humanoid.walkSpeed = 6
end)Line by line:
world:add("Part", { ... })creates a part and parents it to the world.sizeis in metres,cframeplaces its centre at x 0, y 64, z 0, andanchored = truekeeps physics from moving it, which is what you want for a floor.game.players.joinedfires once for each player who connects. It exists on the server only, because the server is the side that knows about everyone.player:spawn(position)gives that player a body, puts it where you asked and hands it back. Two metres above the floor is enough to land on it.bodyis that body, the same oneplayer.characterreturns later. Itshumanoiddecides how it moves, so settingwalkSpeed = 6makes that player walk six metres a second.
Write the client script#
client/main.luau runs inside each player's own game. It sets the camera, reads input and draws
interface for that player alone:
camera.fov = 80
game.renderStepped:connect(function()
camera.mode = if input:down("sneak") then "thirdPerson" else "firstPerson"
camera.distance = 6
end)camera and input are globals that exist on the client only. renderStepped runs once a frame,
just before the frame is drawn, so the camera you set inside it is the camera the world is drawn
against.
That is a whole place. There is no registration step and nothing to declare: a file that exists is a file that runs.
Press Play at the top of the editor, or F6, and you land on the floor in first person. Hold sneak to see yourself from behind. F6 again, or Right Shift, goes back to editing.
Each Play starts the place the way a player joining it does, so what you test is what players get. Client scripts run once the client has the world the server built for play, and they never see an instance that only exists in the editor.
Change something while it runs#
Save any file in the place and Moud restarts it. Everything the place created is destroyed and made again, which means you can change a number, save, and see the result without leaving the game.
It also means a variable that holds an instance from before the reload points at something that no longer exists:
-- wrong: points at an instance the reload destroyed
local statue = game.world:find("statue")
-- right: looked up when it is needed
local function statue()
return game.world:find("statue")
endEvery variable in your scripts is cleared by a reload. Anything that has to survive one goes in
game.persist, a plain table the engine hands back after each restart:
game.persist.joins = (game.persist.joins or 0) + 1The two clocks#
Each side has its own loop:
-- the server, once a tick. delta is seconds
game.stepped:connect(function(delta) end)
-- the client, once a frame. delta is seconds
game.renderStepped:connect(function(delta) end)Write game state on stepped, where every player gets the same answer. Write the camera and anything
that has to look smooth on renderStepped, which runs often enough to follow the frame rate.
Waiting without blocking#
task lets a script wait without stopping the tick that everyone else is on. Added to the server
script, this makes a lantern next to the spawn blink:
local lantern = world:add("Part", {
name = "lantern",
size = vec3(1, 1, 1),
cframe = cframe(0, 66, -4),
anchored = true,
})
task.spawn(function()
while true do
lantern.color = color(1, 0.88, 0.5)
task.wait(0.6)
lantern.color = color(0.3, 0.25, 0.2)
task.wait(0.6)
end
end)
task.delay(3, function() end)
local handle = task.spawn(function() end)
task.cancel(handle)task.spawn starts a piece of work that can yield, task.wait yields for a number of seconds,
task.delay runs something once later, and task.cancel stops work you started.
A loop without a task.wait inside it never gives the tick back, and the server stops answering
anybody. If you write while true do, put a wait in it.
When something goes wrong#
A value an enum does not accept is an error that names what was allowed, instead of quietly falling back to a default:
camera.mode = "birdseye"
-- error: mode: unknown value 'birdseye', expected one of firstPerson, thirdPerson, scriptableErrors show up in the editor's Output panel: press Right Shift to open the editor
over the running game. They are also written to the game log, logs/latest.log. See
Debugging for what else the editor tells you about the running place.
Next steps#
- Places, modules, scenes and scripts explains
place.toml,require, scene files and the script instances you can put in the tree. - Instances covers parts, properties, signals and raycasts in full.
- Players and bodies covers spawning, teams and walking bodies around.
- Interface covers the screen, billboards and surface guis.
