Mirror Engine
V7
How To
Mirror Engine Logo

Scripting

A TypeScript Game Engine

Mirror Engine is built on TypeScript, a superset of JavaScript - the most popular programming language in the world.1

If you're only familiar with JavaScript, don't worry! TypeScript is a superset of JavaScript, so you can use all your existing JavaScript knowledge - anything TypeScript-related is optional.

TypeScript helps us JS developers stay sane, and we hope you'll be quite pleased.

Your First Script

Select an Entity, click the Script button on the bottom-left toolbar, and click Add Script.

Add Script

You'll see a basic script like this. Don't let it scare, this will all become second nature as you build.

import { MirrorScript } from '@mirror-engine/core'

export class NewEntity extends MirrorScript {
  // Example property that can be referenced via this.rotateAmount
  rotateAmount = 1

  // Needed, but likely ignore. Use initialize() for init logic.
  constructor(app) {
    super(app)
  }

  // Called first when the script starts
  initialize() {}

  // Called every frame. dt, deltaTime, is the time since the last frame.
  update(dt: number) {
    this.entity.rotateLocal(0, this.rotateAmount, 0)
  }

  // This is called everytime the script is hot reloaded
  swap() {}
}

After this script is added, you'll see the entity rotate around the Y-axis.

Key Things to Know

The 2 main methods to note are initialize() and update().

  • initialize() is called once when the script is initialized.
  • update() is called every frame, with dt being the deltaTime since the last frame. Use this for frequent logic checks, but be cautious to not bottleneck performance. dt can be used to smoothly interpolate, such as for rotation and movement as we do with rotation above.
New to Game Dev?

You can safely ignore these for now. This is boilerplate code that's required for the script to work.

  • export class
  • extends MirrorScript
  • constructor(app){ super(app) }
  • swap()

The only thing you need to focus on is initialize() and update().

Hot Reloading

swap() is called every time the script is hot reloaded, such as when you or a contributor saves changes to the script. As of Alpha V5, we're still refining swap logic a bit, but it's useful to reininitialize any values as-needed since initialize() is only called once, i.e. not called when the script changes via saving changes to the script.

This is Build Mode Only and will not occur in Play Mode.

Scripts Are Automatically Added to Entities

Behind the scenes, a ScriptComponent is automatically added to the entity for you when you add a Script via the UI. You'll never need to use the MirrorScript class directly.

You only need to manually add a ScriptComponent if you're creating a new Entity via code, for example:

import { MirrorEntity, MirrorScript } from '@mirror-engine/core'

export class EntityCreator extends MirrorScript {
  // don't use the constructor for initialization; the `initialize()` method waits until the engine is ready.
  // However, the constructor with super(app) must still be called, so leave this boilerplate code as-is.
  constructor(app: AppBase) {
    super(app)
  }

  // called when the script first runs
  initialize() {
    const newEntity = new MirrorEntity(this.app)
    // Add the ScriptComponent to the new Entity
    newEntity.addComponent('script')
  }
}

What's Mirror Engine built on?

Mirror Engine stands on the shoulders of giants with numerous open-source libraries and we especially owe a huge thank you to PlayCanvas, which powers key parts of Mirror Engine. We're incredibly grateful for the team's work.

API Similarities

Most functionality in the PlayCanvas API is available via scripting in-game. However, to help you build games quickly, Mirror Engine abstracts many low-level details, such as mirrored multiplayer, scaling, asset loading, real-time collaboration, runtime optimizations, TS compilation, player accounts, databases, publishing, and more.

API Differences

We're still polishing these Mirror Engine implementations:

  • Events: These do work from a single-player perspective, but may have breaking changes as we polish mirrored multiplayer.
  • Asset loading: This happens automatically behind-the-scenes. We'll be deprecating some exposed APIs on the engine core and streamlining Mirror Engine's implementation.
  • UI: Since everything is mirrored, we're still fine-tuning sync. vs nosync entities, which UI is particularly something you don't want to sync across players.
  • Dynamic rigidbody physics do work, but they're not fully synced, so the physics will look different on others players' game clients. Kinematic and static rigidbodies are synced.

Thank you for your support as we continue to fine-tune and add functionality in the alpha!

Footnotes

  1. Stack Overflow Survey 2024. JavaScript is #1 and TypeScript comes in at #5.

Mirror Engine Logo