Game objects

As your scene becomes more complicated, it’s useful to put some of the logic into separate game object files. By doing this, you can keep the main code for the scene clean and simple to read, while encapsulating reusable parts that control several entities in the scene.

A Game Object holds all the properties and methods for a type of entity you might find in your scene, for example a door or a button or a monster. Thanks to this abstraction, the files with your scene’s main logic can instance full game objects through just one line of code. You can also call the methods on these objects with the same ease.

We recommend keeping game object definitions each in a separate file.

Below is an example of a Door game object. This object has a constructor that requires that you pass it a shape, a sound and a position. It also exposes an openDoor, closeDoor and toggleDoor method, that can be called from other files.

export class Door extends Entity {
  public isOpen: boolean

  // Allow each room to specify a unique look and feel
  constructor(
    model: GLTFShape,
    transform: TranformConstructorArgs,
    sound: AudioClip
  ) {
    super()
    engine.addEntity(this)

    this.addComponent(model)
    this.addComponent(new Transform(transform))

    this.addComponent(new Animator())
    this.getComponent(Animator).addClip(
      new AnimationState("Door_Open", { looping: false })
    )
    this.getComponent(Animator).addClip(
      new AnimationState("Door_Close", { looping: false })
    )

    this.addComponent(new AudioSource(sound))
  }

  /**
   * Exposing `openDoor` as an action this object is capable of doing
   * This contains the open door experience (animation and sound) while allowing
   * the scene to decide when the action occurs
   */

  public openDoor(playAudio = true): void {
    if (!this.isOpen) {
      this.isOpen = true

      this.getComponent(Animator).getClip("Door_Close").stop() // bug workaround
      this.getComponent(Animator).getClip("Door_Open").play()

      if (playAudio) {
        this.getComponent(AudioSource).playOnce()
      }
    }
  }

  // Similiarly we can close the door.
  public closeDoor(playAudio = true): void {
    if (this.isOpen) {
      this.isOpen = false

      this.getComponent(Animator).getClip("Door_Open").stop() // bug workaround
      this.getComponent(Animator).getClip("Door_Close").play()

      if (playAudio) {
        this.getComponent(AudioSource).playOnce()
      }
    }
  }

  // Or toggle the state between open and closed
  public toggleDoor(playAudio = true): void {
    if (this.isOpen) {
      this.closeDoor(playAudio)
    } else {
      this.openDoor(playAudio)
    }
  }
}

A few things to note from the code above:

  • export makes the class available to other files in your scene
  • extends Entity makes this definition inherit everything from the base Entity class
  • this refers to the current instance of the game object

None of the code above produces any changes in the scene on its own. It just exposes constructors and methods that can be called from other files, keeping what’s possible separate from what is to be done.

Once the Door class is defined in your scene, you can use it in your game.ts or any other file:

import { Door } from "./door"

const door = new Door(
  new GLTFShape("models/Door.glb"),
  { position: new Vector3(24, 0, 24) },
  new AudioClip("sounds/door_squeak.mp3")
)
door.addComponent(
  new OnClick((): void => {
    door.toggleDoor()
  })
)

The code above imports the Door class, then instances a door, and adds an OnClick behavior to it that simply calls the toggleDoor method in the Door class.

Tip: To learn more about using game objects in Decentraland scenes, see this Tutorial video

The Type Object Pattern

If your scene uses various types of game objects that have much in common, you might want to take this refactor even further, and define a base game object type with all the things that these have in common. You can then create sub classes that inherit from this base class.

You can read more about this pattern in Game Programming Patterns.