Programming in Rooms.xyz (Part 1)

Bruno Oliveira
11 min readApr 21, 2023

--

Part 1 | Part 2 | Part 3

Rooms.xyz isn't exactly a fantasy console (a topic about which I have written a few articles in the past) but it shares a lot of the same principles: as it's a tool that allows you to create voxel-based mini-games and interactive experiences very easily using voxel objects and Lua scripting.

Disclosure: I’m an employee at“Things, Inc.” the 3-person startup behind Rooms.xyz. The whole “, Inc” part may look a bit corporate but really we’re just 3 people who don’t know what they are doing but want to build something cool.

This article is a (very shallow) tour of the programming model and the basics of how to write Lua code in Rooms to customize the behavior of objects.

In Part 1, we will cover the basics:

  • Rooms and Things
  • Basic programming
  • Moving things around
  • Sequencing
An example of a room in Rooms.

Do I have to program?

Well, do you have to eat dessert? The premise for this article is that you're into programming and programming is fun. In fact you and I know that it would take some effort to stop you from programming. You'd program the toaster if it had an API. Maybe it does have an API. Stop programming the toaster. Come back.

All this to say: no, you don't have to program in Rooms, you can just make beautiful rooms based on existing objects (there's a lot of them in the library!), a lot of which already come with existing behaviors. But if you want to reach that high ceiling and create something that behaves in an extra-fun and unique way, programming is the way to do it!

What is a Room?

A room is a small interactive 3D environment. That's the thing you're building. If you're not into rooms or interactive 3D environments, oh boy, you're not going to enjoy Rooms.xyz. A room can be a mini-game or just a pleasant (or not, but we hope it is) experience that the user can access.

Example of a room: https://rooms.xyz/btco/bowl

This is an example of a mini-game. It's a mini bowling game where you can bowl by clicking on the bowling ball. I think that's how bowling works.

What is a Thing?

A Thing is an object. Really, Thing? Could we have chosen a more generic name? Well, yes, object would have been even more generic. Sometimes we call them objects too, when we haven't had enough coffee.

So, each of the objects in the room is a Thing. In the room above, the bowling ball is a Thing, each pin is a Thing, the table is a Thing, the jukebox is a Thing. You get it. Everything is a thing (*).

(*) The walls and floor are Things too but don't tell anybody, because that's a weird part of our design and I don't know if it makes sense yet.

What makes a Thing behave the way it does?

Spoiler alert: it's code. What did you think it was going to be?

If you click on a Thing while in edit mode and click the Code button, you will see a code editor.

Click the Code button to open the Code Editor.

In the code editor you can enter Lua code. Lua is a lovely scripting language created by a Brazilian. Not me. Another Brazilian. There's a lot of us out there.

Anyway, think of Lua as Javascript without the annoying parts.

Code Editor for the Table thing

Because we don't have any code yet, we are prompted to select a template so we don't have to start from scratch. Select the Hello template, which is the simplest. Best to use that one because if you go off exploring the more complex ones you'll inevitably find out you can learn everything on your own and you'll stop reading this article.

Note that we also have full documentation of all API functions, so after you've read this tutorial (or during) you can find out everything that the API can do.

Before we go into coding…

Note that if you want to save your work in Rooms.xyz, you'll need to create an account. Because (as of this writing) Rooms.xyz is in alpha, you'll need to request early access via the home page (just click the "Request Early Access" button).

You can, however, follow this tutorial without creating an account; the only caveat is you won't be able to save and share the rooms you create.

Saying Hello

Okay, when you click the Hello template, you get this code pre-written for you. It’s a truly fascinating script that makes the object say “Hello” when clicked.

The "Hello" template code

Now run it, either in the preview window (click Update Preview), or get out of the code editor and enter Preview mode.

This thing at the top is how you switch from Edit to Preview (that is, Play) mode. You probably already figured this out, but just in case, it's here:

Toggle to switch between Edit and Preview (aka Play) mode.

You can also toggle between Edit and Preview with the ESC key.

Now go into Play mode and click the table. This is what you get:

Not loving the chatty furniture? No worries, we'll move on to something else soon.

From now on I'll stop taking screenshots of code (because that's a silly way to show code and the other devs in the coffee shop are already looking at me weird for doing it) and instead I'll write the code directly in little blocks like this:

-- This function runs when the user clicks.
function onClick()
say("Hello")
end

Of course you can modify that string to make that table say anything you can imagine, even a drastically different phrase like:

The extent of my creativity

I'm sure you can find better things for it to say.

Handlers (predefined functions)

In the previous example our function was called onClick. This is not a coincidence: that's the function that gets executed when the user clicks something. Every Thing can have its own onClick function.

This may seem redundant but the onClick function needs to be called onClick. If you call it "onclick" or "ONCLICK" or "clementine", it's not going to work, because the engine looks for precisely the name onClick, case-sensitive.

Some other examples of handler functions are:

  • onStart(): gets called when the room starts.
  • onCollision(): gets called when the Thing collides against another Thing.
  • onButtonDown(): gets called when a virtual joystick button is pressed.
  • onButtonUp(): gets called when a virtual joystick button is released.
  • onUpdate(): gets called once per frame (60 times per second).

Don't worry about them now, we'll see some of them next, and I'll ignore some of them too but at that point we'll be so far ahead you won't remember this list and won't notice I skipped it.

The onStart() function

It's useful if you want to do something when the room starts independently of user action.

-- This function runs when the room starts.
function onStart()
say("Click me!")
end

-- This function runs when the user clicks.
function onClick()
say("Thanks for clicking")
end

You can also put initialization code outside of any functions, if you prefer, instead of inside onStart(). It's a matter of style:

-- This code runs when the room starts.
say("Click me!")

-- This function runs when the user clicks.
function onClick()
say("Thanks for clicking")
end

I think it's neater if it's inside onStart(). Personal preference.

A word about physics

If you're following along and trying the code, make sure the object you're working on is set to Kinematic as its Physics type. We'll explain more later, but Kinematic objects do what you want. Dynamic (non-kinematic) objects do what they want. We're the boss for now, so set it to kinematic so we can move them around without them giving us attitude.

Take it for a spin!

Try this code on an object. If you want a starting point, just start from https://rooms.xyz/btco/tutostart. Click the taxi and set its code to this:

-- This function runs when the user clicks.
function onClick()
startSpin()
end

Now go to Preview mode and click it. It should start to spin! Don't try this with a real car. In general, don't take any driving advice from this tutorial.

A spinning taxi.

Check out the result at: https://rooms.xyz/btco/tutospin.

Moving Instantaneously (without animation)

If you just want to move a Thing to a new location, use the setPosition() function and tell it the new coordinates:

function onClick()
-- Move it to the center of the room.
setPosition(0, 0, 0)
end

Click it and pop, it moves to the center of the room all of a sudden (probably a very rough experience to the car's occupants).

Check out the result at: https://rooms.xyz/btco/tutosetpos.

Moving Smoothly (with animation)

Cool, now let's make that object move smoothly by a certain amount, not just teleport. Just use the startMoveBy() function, which takes a delta (x, y, z) and a total time and moves the object as requested.

-- This function runs when the user clicks.
function onClick()
say("Here we go")
-- Move 30 units south in 1 second.
startMoveBy(0, 0, -30, 1)
end

Why 0, 0, -30? Well, that's because of how our coordinate system works:

Room coordinate system.

So +Z is towards the left wall, +X is towards the right wall. As for the cardinal directions, let's establish that:

  • When we say "north" we mean towards +Z
  • When we say "east" we mean towards +X
  • When we say "south" we mean towards -Z
  • When we say "west" we mean towards -X
  • When we say "up" we mean towards +Y
  • When we say "down" we mean towards -Y

By the way, the center of the room is (0, 0, 0). That's the point on the floor at the center of the room. The room's surface is about 95x95 and it's 75 units tall so this means the X and Z coordinates go from about -47.5 to +47.5, and the Y coordinate goes from 0 to 75.

Check out the result at: https://rooms.xyz/btco/tutomove

A Car that Starts and Stops

The best cars in the market today are capable of both moving and stopping. Let's see if we can do that. We'll make it so that if you click it, it starts to move, and if you click it again, it stops, and so on and so forth. Here's the code:

moving = false

-- This function runs when the user clicks.
function onClick()
if moving then
stopMove()
moving = false
else
startMoveBy(0, 0, -48, 10)
moving = true
end
end

This illustrates how to call stopMove() to stop a motion started by startMoveBy(), and also shows how to have a boolean variable that lets us keep track of the state of the car (moving or not moving). It also shows that variables can exist outside of functions, so they can keep their value across function invocations (which would not be the case if it were declared inside of the function).

Check out the result at: https://rooms.xyz/btco/tutostartstop

Sequencing

One thing that might not be intuitive is that functions like say(), startMoveBy(), etc, are asynchronous, meaning that they will run in the background while your code's execution continues. So if you call more than one of these, they will run at the same time, not one after the other. For example:

-- WARNING: This doesn't do what it seems to do
function onClick()
-- Move 20 units south, in 1 second.
startMoveBy(0, 0, -20, 2)
-- Move 20 units west, in 1 second.
startMoveBy(-20, 0, 0, 2)
-- Say we're done.
say("Done")
-- Start to spin around.
startSpin()
end

You might think that the car would first move 20 units south, then 20 units west, and then say "Done", and then start to spin. But in fact it tries to do all at the same time and it's a complete mess.

How do you do this, then? We have to add another function to our repertoire, also because repertoire is a cool French word that I wanted to work into this text. That function is wait(). It will wait for a given number of seconds and then call another function. We can use this for sequencing.

function onClick()
-- Move 20 units south, in 1 second.
startMoveBy(0, 0, -20, 1)
-- This waits 1 second (the time it takes to move)
-- and then calls moveWest
wait(1, moveWest)
end

function moveWest()
-- Move 20 units west, in 1 second.
startMoveBy(-20, 0, 0, 1)
-- This waits 1 second (the time it takes to move)
-- and then calls sayDone
wait(1, sayDone)
end

function sayDone()
say("Done")
-- Start to spin around.
startSpin()
end
Sequencing

This works. Well, apart from the fact the car drifts sideways and doesn't turn, but I already told you not to take driving advice from me.

Check out the result at: https://rooms.xyz/btco/tutoseq

Doing Something Periodically

If you want a function to be called periodically every given interval, you can use every().

Here is an example that might make you sleepy: Counting sheep. Make sure you have some coffee to reverse the effects of this example.

In this example, the sheep has this code:

c = 0

function onStart()
-- Call the count() function every 1 second.
every(1, count)
end

function count()
c = c + 1
say(c .. " sheep")
end

So on onStart() we say that we want the count() function to be called every 1 second. So the engine will call our count() function every second, in which we increment the counter and make the sheep say the current count.

Is this what people mean when they say “counting sheep” to sleep? I don’t know.

Check out the result: https://rooms.xyz/btco/tutocount

Note: For performance reasons, you can’t have the interval be smaller than 0.25s. You can still ruin performance in other ways though, so don’t be discouraged.

End of Part 1

Great! You've made it this far and tolerated my sense of humor and writing style. It doesn't get any better in Part 2, but please click the link below to continue.

Go to Part 2 →

--

--