Tabletop Simulator

Tabletop Simulator

Not enough ratings
The art of scripting: Object Movement
By Floaty
A guide on how to script object movement. Basic lua knowledge required.
   
Award
Favorite
Favorited
Unfavorite
Requirements
  • An object to move
  • Optional: A table of destination positions (e.g. list of snap points)
Basic object movement
The tabletop simulator currently defines 3 different functions to move an object:
  • setPosition( vector)
  • setPositionSmooth( vector, collide, fast)
  • translate(vector)

All 3 functions take vector (in the form {x=?,y=?,z=?}) to describe the destination position. Whilst both setPosition and translate will serve as an instantaneous teleport, the setPositionSmooth will actually move the object in the world. It therefore requires additional instructions. The most important variable is the collide bool which will decide if the object is able to clip through other objects on its path (which would equal false). The variable fast can normally be set to false, as it only decides if the object is moved in a slower or faster pace.

In most cases it is advisable to use the setPositionSmooth function to move an object, because it simulates object movement in the most natural way.
To actually move the object, you have to call the respective method on the object reference
object.setPositionSmooth(destination,true, false)

To help you understand the concept, here a quote from the knowledge base:
X is right/left, Y is up/down, Z is forward/back.
Positions
Object movement requires requires a vector which describes a point in the game space. Two different types of vectors are possible: the global and the local position. Whilst the global position describes a point in the game room, the local position describes a point in relation to the center of a specific object. To obtain the current position of an object and convert it, following functions can be used:
getPosition() positionToLocal(vector) positionToWorld(vector)
The getPosition() function returns a vector of the form {x=?,y=?,z=?} which describes its position relative to the world.
The other two functions are self-descriptive functions which can be used to convert vectors between world and local coordinates.
Basic object rotation
The functions offered to rotate an object are comparbale to the ones used to move objects:
  • setRotation( vector)
  • setRotationSmooth( vector, collide, fast)
The setRotation function will instantaneously rotate an object, whilst the setRotationSmooth function will actually rotate the object in the world. Both functions take up an vector of the form {x=?,y=?,z=?}, where each assigned value specifies the rotation around this axis in ° (values can range from -180 to 180). The variables fast and collide do the same they did in the setPositionSmooth function.

As for the movement, it is advisable to use the setRotationSmooth function to rotate an object, because it simulates object movement in the most natural way.
To actually rotate the object, you have to call the respective method on the object reference
object.setPositionSmooth(rotationVector,true, false)

To help you understand the concept, here a quote from the knowledge base:
X is pitch (nodding your head), Y is yaw (shaking you head), Z is roll (tilting your head).
Advanced object positioning
Now that we know how to obtain the position of objects and move them, we are theoretically able to move objects at our discretion. But how do we get positions to move our objects to without laborious chore of checking every position we need with scripted objects or trial and error?
The simple answer is: Snap points

Snap points provide a convinient way to obtain global coordinates. At first we have to obtain the respective snap points associated with an object:
snappoints = object.getSnapPoints()

This function will provide us a table with following content:
{ position{...} , rotation{...}, bool rotation_snap }

The position and rotation tables for each snap point describe its current global position as well as its rotation. The bool rotation_snap is an indicator if the snap point is a "rotation" snap point.

Using this list, we are able to move our object to a respective snap point. But there is one last thing to consider: The volume of the object. Snap points can be found on the surface of an object and are thus far nearer than e.g. a game tile would be. So the position of the snap point has to be adjusted to accomodate for the colume of the respective object. Without that, the moved object would most likely land inside the destination point, rather than on the destination point. A graphical example would be a tile in a board game that would drive inside the board instead of setting itself on the board.
For this kind of adjustment, you can write a simple function:
function moveTile(tile, move) corr = tile.getBounds().size["y"] move["y"] = move["y"] + corr tile.setPositionSmooth(move, false, false) move["y"] = move["y"] - corr end

In this function, tile represents the respective object, move is the position vector of the snap point, which can be addressed via the following call on the table of snap points:
snapPoints[index].position

In my example, the y coordinate of the position will be adjusted to place a game piece on top of a board. The position has after the transaltion to be corrected again to the original value by subtracting the height of the object.
You can access the boundaries of an object, which are necessary for such adjustments, via the following call:
object.getBounds()

This will provide you with an vector of the following form, describing the maximal extension of each axis:

{ x = ?, y = ?, z = ? }
3 Comments
deckanddice 19 Mar, 2021 @ 8:23am 
I haven't used the API yet (I have just started digging through the docs). But from what I can tell from the Knowledge Base portion of the TTS site (https://api.tabletopsimulator.com/object/#getsnappoints) the list returned is just a numbered list -- so it may be a pain to sort through if you have set a ton of snap points on the object you are accessing i.e. the entire table object.

But the good part is, it looks like the above excerpt showing "object.getSnapPoints()" implies that if you set your snap points to sub-objects of the entire table object (so that you have a 1-1 relationship) you will only return 1 snap point per object.

Example: Imagine you have a bunch of standees with cards in them, then each standee would probably only have 1 snap point per object, and so each list returned would only have 1 entry in it.

I haven't tested this yet but I will need to create this functionality in order to put my project into TTS. I will post an update when I do.

I hope this helps
dixit | Sven 14 Jun, 2020 @ 1:07pm 
Hi. Thanks for the guide. But how do I know in the script which snap point returned by getSnapPoints() is for what purpose (i.e. how to find out what 'index' must be in snapPoints[index])? Does getSnapPoints() give me the points in a well defined order? What happens to my script when I add a new snap point to the area?
machineguy 21 Aug, 2019 @ 8:29pm 
Nice. You should add the new vector function that was added in the last major update..