Programming in Rooms.xyz (Part 2)

2023

.

04

.

22

/

Log

2

Author: Bruno Oliveira (@btco)

Welcome to Part 2 of this tutorial! In this part we're going to cover:

  • Interacting with other things
  • Physics
  • Collisions

Other Things

If you want to call API methods on another Thing that’s not the current one, you can use the getThing() API function to get another Thing by name, and then use Lua’s method call syntax (the colon ‘:’ operator). Here is an example of moving to another thing’s position.

The initial setup

Here is the car’s script:

function onClick()
  -- Get the tree.
  local tree = getThing("Tree")
  -- Get the tree's position.
  local x, y, z = tree:getPosition()
  -- Move towards the tree's position.
  startMoveTo(x, y, z, 2)
end

Or more succinctly, if you prefer:

function onClick()
  local x, y, z = getThing("Tree"):getPosition()
  startMoveTo(x, y, z, 2)
end

Here is the result, after you click the car:

More great driving

Check it out at: https://rooms.xyz/btco/tutootherthings

Sharing data between Things

Sooner or later you'll want one thing to communicate with another in some way. However, due to performance and isolation constraints, scripts for different Things run on different Lua contexts. This means they don't share functions, variables or fields.

As a result, you can't call another Thing's functions, and you can't access its variables.

All communication must happen through messages. We'll cover that next.

Broadcasts

If you want to notify other things that something happened so that they may react to it, you can use broadcasts. A broadcast is a message sent by a Thing and that causes a named function to be invoked on all other things.

Here is an example. Let’s say I have three animals and a boombox.

Now I music to start playing and the animals to start dancing when the user clicks the boombox. By "dancing" I mean bouncing and spinning, which is way better than any dance moves I've seen an ostrich do in real life, if you ask me.

Here’s how we write the code for the boombox:

on = false

function onClick()
  -- Toggle on/off:
  on = not on
  if on then
    -- Start the music.
    playSound("boombox.mp3")
    -- Tell everyone that the music started.
    broadcast("MusicStarted")
  else
    -- Stop the music.
    stopSound("boombox.mp3")
    -- Tell everyone that the music stopped.
    broadcast("MusicStopped")
  end
end

Ensure you have an MP3 file called boombox.mp3 in your project (it should come with the Boombox if you imported it from the library).

We start by toggling the on variable from true to false or vice-versa, then we either start or stop the music, and broadcast the “MusicStarted” or “MusicStopped” message depending on the case.

Now let’s program the animals to react to the music starting and stopping. Here is the code (same code for all 3 animals):

function onMusicStarted()
  startSpin()
  startBounce()
end

function onMusicStopped()
  stopSpin()
  stopBounce()
end

Warning: if you are confronted with a wild animal in real life, do not attempt to program it.

Notice that when it gets the MusicStarted message (which causes the engine to call the onMusicStarted() function), it will start spinning and bouncing. Likewise, when it gets the MusicStopped message (onMusicStopped() function), it will stop spinning and bouncing.

This is the rule: if the message is called Foo, then the function that gets called is onFoo(). We automatically capitalize the message name to figure out the function name, so if the message is called foo, the function called is still onFoo(), NOT onfoo(). For clarity, we advise you to always start the name of your messages with a capital letter.

Now you can enjoy this animal dance party. Click the boombox to start/stop the music to see what happens:

Check it out at: https://rooms.xyz/btco/tutdance

Sending a Message to Only One Object

If instead of broadcasting to all objects, you want to send a message to a single object, you can use send() instead of broadcast().

When you call send(), you are sending a message to a single Thing rather than to all Things in the scene. For example:

local t = getThing("Tiger")
send(t, "MusicStarted")

This sends the MusicStarted message only to the Tiger, meaning the engine will call the onMusicStarted() message of the Tiger’s code, but not on the Ostrich or the Turkey.

Physics

Until now we have only used things with the Kinematic physics type. We did that because it was easier to control the position of the things directly from our code, like telling the taxi to go to (0, 0, 0) or telling it to move this way and that. The only reason it obeys is because it’s Kinematic.

The opposite of kinematic is dynamic, that is, the physics engine will control the object, not us. We can influence it with forces or by controlling its velocity, but we can’t move it around directly (well, we can, but it’s best not to).

In Rooms, a dynamic object can be “Upright” or “Tumbly”. Upright means it’s controlled by physics but remain in its upright position (it doesn’t tumble), like maybe a character in a video game. “Tumbly” means it’s completely affected by physics and moves and tumbles along like a beach ball or a bowling pin.

To see this in action, add a ball and place it high up above the floor:

Then, set its physics type to Tumbly, if it isn’t already.

Set the Physics type of the Ball to Tumbly

Go into Preview mode and you’ll see that it will drop to the floor. That’s physics! Newton would be happy. I mean, he’d be happier if this were an apple and not a ball, and it dropped on his head and let him to discover gravity and stuff.

Now add a character to the room, maybe Tiny Bruno, just below the ball but off to a side, so it’s funny when the ball bounces off of him:

Comedy setup

Now go to Preview mode and notice how the ball bounces off his head.

Now let's make him say "ouch" when the ball hits him.

Just set Tiny Bruno's code to:

function onCollision()
  say("Ouch")
end

Check out the result here → https://rooms.xyz/btco/tutophysics

As a bonus, wouldn't it be fun if you could click on the ball and make it move towards Tiny Bruno so you could hit him again?

This is the code that does that:

function onClick()
  -- Apply force towards Tiny Bruno
  
  -- Get Tiny Bruno's position
  local tx, ty, tz = getThing("Tiny Bruno"):getPosition()
  -- Get my position
  local x, y, z = getPosition()
  -- Apply force towards Tiny Bruno's position
  -- The Y component is 200 (up) because we want the ball
  -- to always launch upwards, as it's more fun.
  applyForce(tx - x, 200, tz - z)
end

Here we're doing vector math (oh no!): we're figuring out the target's position (in this case, poor Tiny Bruno) and subtracting our own position, which gives us a vector from us to Tiny Bruno, and then we're using that as a force. Except we're using 200 for the Y component, in order to make the ball bounce up, as that's funnier (otherwise the ball would just fly directly towards Tiny Bruno).

What collides with what

Due to how physics engines work, not all types of object collide with all types. In particular kinematic objects don't collide with other kinematic objects, but kinematics and dynamics always collide. If collisions are not triggering, it may be because you have a bad combination of physics types, so it's always worth double-checking.

Detect collisions with specific objects

If you want to figure out what you've just hit, you can receive an argument in your onCollision() function. It will be the Thing that you collided with.

Let's add 2 other balls (and, very randomly, a paint roller) to the room and have Tiny Bruno say the name of what just hit him:

Update Tiny Bruno's code to:

function onCollision(other)
  say("Ouch, " .. other:getName())
  particles("puff", getPosition())
end

Notice that here we are receiving an argument called other. This is simply the Thing that we collided with, so we can call other:getName() to get its name, other:getPosition() to get its position in the room, etc.

So if you wanted to do something specific when Tiny Bruno gets hit by the Paint Roller, for instance, you could do:

function onCollision(other)
  say("Ouch, " .. other:getName())
  particles("puff", getPosition())

  -- If I'm hit by the Paint Roller, turn green.
  if other:getName() == "Paint Roller" then
    setTint("#00ff00")
  end
end

Try it out here → https://rooms.xyz/btco/tutophysics2

End of Part 2

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.