The LAND auction has started, participate and buy LAND before it ends! Learn More
Hello! Please choose your
desired language:
Dismiss

In this quick overview, we’ll look at transition and rotation settings in the Decentraland SDK and how they can be used with the entities of your scenes.

It’s important to remember that in Decentraland, you specify the exact location of an entity using x, y, and z coordinates. The Decentraland engine then renders the entity at that location. If you want to move an entity from one position to another, you simply change the x, y, and z coordinates. The engine then re-renders the entity at the new position.

So, for example, if a box is in one corner of your scene and you change its coordinates to the center of the scene, then the engine figures out which “path” the entity needs to follow in order to move to the new position.

The same applies to rotations, in that you set the angle an entity is facing. If you ever change that angle, then the engine will rotate the entity to face the new direction.

Transitions determine the rate at which changes in a scene appear to a player. These transitions are often referred to by game developers as “lerping”. Even though the state change that describes the new position may be instantaneous, transitions make the changes appear smoothly.

Imagine moving a box from point A to point B. You don’t necessarily want your box to magically teleport to point B. Instead, you want it to slide across your scene from point A to point B.

Let’s take a look at where transition and rotation settings are applied to entities and how different timing selections impact the appearance of transitions.

Throughout this tutorial, we’ll use the same basic scene to showcase different ways in which transitions can be used. Since transitions are only manifested when something changes over time, we’re going to use a simple PointerUp and PointerDown event listener to trigger changes in the scene.

import * as DCL from 'decentraland-api'
import { createElement, ScriptableScene } from 'decentraland-api'

export default class Movement extends ScriptableScene {
  state = {
    buttonState: 2
  }

  async sceneDidMount() {
    this.subscribeTo('pointerDown', e => {
      this.setState({ buttonState: 2 })
    })
    this.subscribeTo('pointerUp', e => {
      this.setState({ buttonState: 3 })
    })
  }

  async render() {
    return (
      <scene>
        <cone
          position={{ x: this.state.buttonState, y: 1, z: 3 }}
          transition={{Add your transition here.}}
        />
      </scene>
    )
  }
}

For reference, we’ll use the Entity Interface section in the documentation.

Let’s start off by looking at what happens when there is no transition applied to a position change:

When we click, or release our click, on the cone to trigger the change in position, it happens instantly. This is exactly what we should expect! While the transition is jumpy, it still changes position.

Now, let’s look at what happens when we add a linear transition to our cone object.

transition={{
  position: {
    duration: 200,
    timing: 'linear'
  }
}}

Since we added a linear transition, the movement between the two states happens more smoothly. Furthermore, the acceleration of the object is constant throughout the 200ms runtime.

Let’s look at the transition when we change the timing to quadratic-in.

Using different timings affects how your transition appears. Here, the quadratic-in timing adds some punchiness to our transition. The acceleration of the cone has a quadratic progression.

You can see all of the transition timings listed on the Base Entity portion of the Entity Interface page on our documentation. If you’re using a source code editor like Visual Studio Code, the editor also shows you the list of possible values as an autosuggestion. Just place the cursor where you’d add the timing value and type ctrl + space to reveal the list.

Let’s see how we can use transitions for rotation.

Here’s our updated code:

import * as DCL from 'decentraland-api'
import { createElement, ScriptableScene } from 'decentraland-api'

export default class Movement extends ScriptableScene {
  state = {
    buttonState: 30
  }

  async sceneDidMount() {
    this.subscribeTo('pointerDown', e => {
      this.setState({ buttonState: 30 })
    })
    this.subscribeTo('pointerUp', e => {
      this.setState({ buttonState: 90 })
    })
  }

  async render() {
    return (
      <scene>
        <cone
          rotation={{ x: 90, y: this.state.buttonState, z: 0 }}
          position={{ x: 2, y: 1, z: 3 }}
          transition={{
            rotation: {
              delay:400,
              duration: 200,
              timing: 'bounce'
            }
          }}
        />
      </scene>
    )
  }
}

Let’s see what happens to our cone now.

We’ve added and changed a few things in our code. As you’ll notice in the video, our transition is delayed. You can add a delay attribute to add a brief pause to the movement defined in your code. We also changed the timing to bounce, which, like quadratic-in, adds dynamism to your transition.

Let’s see what a transition can do to a scale change, or changing the size of an entity.

Transitions work marvels with scaling entities up or down. We only had to add a few lines of code to achieve the effect above.

Now, let’s look at some different rotation settings. Let’s start with a new scene and a new object:

import * as DCL from 'decentraland-api'
import { createElement, ScriptableScene } from 'decentraland-api'

export default class Rotation extends ScriptableScene {
  state = {
    buttonState: 0
  }

  async sceneDidMount() {
    this.subscribeTo('pointerDown', e => {
      this.setState({ buttonState: 90 })
    })
    this.subscribeTo('pointerUp', e => {
      this.setState({ buttonState: 0 })
    })
  }

  async render() {
    return (
      <scene>
        <cylinder
          rotation={{ x: 90, y: 0, z: this.state.buttonState }}
          position={{ x: 5, y: 1, z: 5 }}
          transition={{
            rotation: {
              duration: 200,
              timing: 'linear'
            }
          }}
        />
      </scene>
    )
  }
}

We can change the rotation of our cylinder with a simple mouse click. In this first example, we rotate our cylinder around the Z axis whenever we click on the entity. Here’s what happens when we run our scene:

For individually rotating objects, this method is straightforward and easy.

For more complicated rotations, we can use a parent entity to rotate multiple objects at once, or to change the axis of rotation.

Let’s make some quick changes:

<scene>
  <entity
    position={{ x: 3, y: 0, z: 3}}
    rotation={{ x: 0, y: 0, z: this.state.buttonState }}
    transition={{
      rotation: {
        duration: 200,
        timing: 'linear'
      }
    }}>
    <cylinder
      position={{ x: 4, y: 1, z: 4 }}
    />
  </entity>
</scene>

We’ve created a parent entity that conducts the rotation instead of the individual object. Let’s see what happens…

If the axis of rotation is around the center of the parent entity, as our cylinder isn’t centered inside the parent, then it will appear to rotate following an arc.

Now that we’ve seen rotation examples with both independent and nested entities, let’s look at Decentraland’s billboard mode.

Billboard mode overrides all rotation properties and automatically orients the entity to look at the user. We can add this feature to our cylinder or entity with:

billboard={2}

You can find the billboard properties on the Base Entity section of the Entity Reference page.

So, now we have:

<cylinder
  position={{ x: 5, y: 1, z: 5 }}
  rotation={{ x: 90, y: 23, z: 55 }}
  billboard={2}
/>

We’ve set some rotation values, but as you see in the video below, the values are overridden by the billboard property.

Similarly, using the lookAt property overrides all preset rotation values. lookAt is a handy way to rotate an entity so that it faces a specific point, expressed using x, y, and z coordinates. This saves you from having to perform trigonometric calculations to figure out the rotation values you would need on each axis. With the code below, we can see how the property removes rotation settings.

export default class Rotation extends ScriptableScene {
  state = {
    buttonState: 1
  }

  async sceneDidMount() {
    this.subscribeTo('pointerDown', e => {
      this.setState({ buttonState: -15 })
    })
    this.subscribeTo('pointerUp', e => {
      this.setState({ buttonState: 15 })
    })
  }

  async render() {
    return (
      <scene>
        <cylinder
          position={{ x: 5, y: 1, z: 5 }}
          rotation={{ x: 90, y: 23, z: 55 }}
          lookAt={{ x: 90, y: this.state.buttonState, z: 3 }}
        />
      </scene>
    )
  }
}

If you’d like to dig a little deeper into the transition and rotation settings for entities, please refer to our docs. Many of the concepts we reviewed in this blog post are already covered in the Entity Reference and Scene Content Guide.

We’re working hard to make our docs the source of truth for everything related to the SDK — please open a pull request or an issue on GitHub if we’re missing something that you need.

Start Building!
Our SDK provides everything you need to start developing games and applications.
Get Started