dot big bang

Developer API
Menu
All
  • Public
  • Public/Protected
  • All

Moving objects with script is super useful for building an interactive world. In dot big bang this is pretty easy to do!

Every Entity has a Transform Component that can be accessed in world space or local space. Local space is the same as world space if the Entity is not parented to another Entity, otherwise local space will be relative to the parent Entity's transform.

Note: Any changes made to an object's Transform (position, rotation and scale) made by scripts will revert to the values set in the Entity Panel when the game stops.

Contents

  1. Basic Movement
  2. Basic Rotation
  3. Other Ways To Rotate
  4. Basic Scaling
  5. Transforming Over Time
  6. Easing Motion

Basic Movement

Moving or translating an Entity can be done a couple of different ways depending on what sort of motion you are trying to achieve. In general you will be changing the Transform.position of the Entity. This will apply the change immediately on the owner's client, but if the Entity is networked there could be some interpolation of that change over time on other clients. There is also an Entity.teleport() method that will immediately move a networked Entity on all clients. For more information on networking see the Multiplayer Basics and Networking State pages.

Example 1 - A simple position change

This example will simply show various ways you can move an Entity. Since a position is just a Vector3 you can use any methods on that to move an Entity.

// Move to a specific world position.
this.entity.worldTransform.position.set(10, 20, 30);

// Move along one axis.
this.entity.worldTransform.position.x += 42;

// Move to a target position.
const targetPosition = new Vector3(10, 20, 30);
this.entity.worldTransform.position.copy(targetPosition);

// Move by a specific amount.
const moveAmount = new Vector3(10, 0, 0);
this.entity.worldTransform.position.add(moveAmount);

// Teleport to a target.
const targetTransform = otherEntity.worldTransform;
const useLocalTransform = false;
this.entity.teleport(targetTransform, useLocalTransform);

Basic Rotation

Rotating an Entity is quite similar to moving it. While you can use a Vector3 with a conversion you will more often be using the Euler and Quaternion classes.

Transform.rotation is a Euler, but using Quaternions to get a target rotation and converting that back to a Euler is done a lot since Quaternions have more functionality and they avoid issues like Gimbal Lock.

Note: All rotation values are in radians, not degrees. You can use the MathUtils.degToRad() utility to convert degrees if you prefer working with them.

Example 2 - A simple rotation change

Here we have a few ways to rotate an object using Eulers.

// Rotate to a specific angle.
this.entity.worldTransform.rotation.set(0, 0, MathUtils.degToRad(45));

// Rotate one axis.
this.entity.worldTransform.rotation.x = MathUtils.degToRad(45);

// Rotate to a target rotation
const targetRotation = new Euler(0, MathUtils.degToRad(180), 0);
this.entity.worldTransform.rotation.copy(targetRotation);

Example 3 - Rotating with Quaternions

Quaternions offer much more functionality than Eulers, but you need to convert from Euler to Quaternion and then back again to apply the rotation.

Note: These examples don't cache any values for simplicity, but if you are doing operations like this every frame you should reuse class scoped objects instead of creating new ones each time.

// Rotate to a target rotation - Quaternion
const targetEuler = new Euler(0, MathUtils.degToRad(180), 0);
const targetQuaternion = new Quaternion().fromEuler(targetEuler);
this.entity.worldTransform.rotation.setFromQuaternion(targetQuaternion);

// Add a rotation.
const rotateAmountEuler = new Euler(0, MathUtils.degToRad(180), 0);
const rotateAmountQuaternion = new Quaternion().setFromEuler(rotateAmountEuler);
const currentQuaternion = new Quaternion().setFromEuler(
  this.entity.worldTransform.rotation,
);
this.entity.worldTransform.rotation.setFromQuaternion(
  currentQuaternion.multiply(rotateAmountQuaternion),
);

Other Ways To Rotate

There are several additional ways to rotate objects. Some of these ways work really well for things like turrets tracking a target, or wheels turning on a specific axis. We'll go over these in some more detail below.

Example 4 - Looking at a target object

Here we make an Entity always face a different Entity along it's positive Z axis. Something like this is great for turrets or following pets.

targetEntity = new EntityRef();

tick() {
    // Only the owner can transform this object.
    if (!this.entity.isOwnedLocally) {
        return;
    }

    // Ensure there is a target.
    if (!this.targetEntity.exists()) {
        return;
    }

    // Get the target position.
    const targetPosition = this.targetEntity.get()!.worldTransform.position;

    // Rotate to face the target.
    this.entity.worldTransform.lookAt(targetPosition);
}

Example 5 - Rotating around a specific axis

Sometimes you need to rotate around a specific axis. This may not be the x, y or z axis, but around an imaginary line through an Entity.

start() {
    // An angled axis through the Entity. This needs to be normalized to use it in setFromAxisAngle().
    const axis = new Vector3(MathUtils.degToRad(45), 0, MathUtils.degToRad(90)).normalize();

    // Rotate 90 degrees around the axis.
    const targetRotation = new Quaternion().setFromAxisAngle(axis, MathUtils.degToRad(90));

    // Set the rotation.
    this.entity.worldTransform.rotation.setFromQuaternion(targetRotation);
}

Example 6 - Rotating from one direction to another

You may have a situation where you want to get the rotation from one direction to a different direction. Here we will rotate an Entity 90 degrees to the left.

start() {
    const initialDirection = this.entity.worldTransform.getForward();
    const targetDirection = this.entity.worldTransform.getLeft();
    const targetRotation = new Quaternion().setFromUnitVectors(initialDirection, targetDirection);

    this.entity.worldTransform.rotation.setFromQuaternion(targetRotation);
}

Basic Scaling

An Entities scale is contained in a Vector3 like the position. Knowing this, you can alter it in exactly the same ways.

Unlike position, scale defaults to <1, 1, 1> and is applied like a multiplier. An object with a scale of <2, 2, 2> will be twice as large as it would be by default, while an object at <0.5, 0.5, 0.5> would be half as big.

Note: Non-uniform scale, like <1, 0.5, 1> is supported but could result in unexpected behavior during runtime.

Example 7 - A simple scale change

Here we'll go over a few examples of changing the scale of an Entity.

// Scale to a specific size
this.entity.worldTransform.scale.set(1.25, 0.5, 1.25);

// Scale one axis.
this.entity.worldTransform.scale.x += 1;

// Set a target scale.
const targetScale = new Vector3(2, 2, 2);
this.entity.worldTransform.scale.copy(targetScale);

// Scale by a specific amount.
const scaleAmount = new Vector3(1, 1, 1);
this.entity.worldTransform.scale.add(scaleAmount);

Transforming Over Time

All of the above examples apply a transform change immediately. In most cases you will want to apply changes smoothly over a period of time. We'll go over some techniques to do this below.

Example 8 - Moving to a target over time

Here we have a simple setup that allows you to tell an Entity to move to a target location over a set amount of time. Once the target is reached it will move back to where it started and repeat.

// A property to allow for changing the distance.
distance = 50;

// Some variables to help time the motion.
private _time = 0;
private _origin = new Vector3();
private _movementVector = new Vector3();

start() {
    // Store the initial position so we can return to it later.
    this._origin.copy(this.entity.worldTransform.position);
}

tick() {
    // If the Entity is networked, only the owner can move it.
    if (!this.entity.isOwnedLocally) {
        return;
    }

    // Do some math to determine a scale value.
    const scale = Math.cos(this._time + Math.PI) * this.distance + this.distance;

    // Move the Entity along it's forward direction by the scaled amount.
    this._movementVector.copy(this.entity.worldTransform.getForward()).normalize().multiplyScalar(scale);
    this.entity.worldTransform.position.x = this._origin.x + this._movementVector.x;
    this.entity.worldTransform.position.z = this._origin.z + this._movementVector.z;

    // Increment time to use in the scale calculation.
    this._time += this.game.frameDeltaTime;
}

Example 9 - Moving to a target over time

We'll do the exact same thing here but use an interpolation function called Vector3.lerpVectors(). This function will pick a position between two Vector3 positions based on the 0-1 value passed.

// A property to allow for changing the distance.
distance = 50;

// Some variables to help time the motion.
private _time = 0;
private _origin = new Vector3();
private _movementVector = new Vector3();

start() {
    // Store the initial position so we can return to it later.
    this._origin.copy(this.entity.worldTransform.position);

    // Calculate the target position by adding an offset to the origin.
    this._movementVector.copy(this._origin).add(this.entity.worldTransform.getForward().setLength(this.distance));
}

tick() {
    // If the Entity is networked, only the owner can move it.
    if (!this.entity.isOwnedLocally) {
        return;
    }

    // Do some math to determine a scale value.
    const scale = Math.cos(this._time + Math.PI);

    // Move the Entity along it's forward direction by the scaled amount.
    this.entity.worldTransform.position.lerpVectors(this._origin, this._movementVector, scale);

    // Increment time to use in the scale calculation.
    this._time += this.game.frameDeltaTime;
}

Easing Motion

Applying Easing curves to your motion can add a huge amount of polish and it's super easy to do! An Easing curve is a mathematical formula applied to a value to change it to fit a certain point on the curve.

Easing is commonly used to add anticipation and follow through to motion without manually animating it. There are also Easing curves that add bounciness or elasticity to movements. The ease can optionally be applied to the start, end or both parts of a motion letting you really customize it.

Example 10 - A bouncing Entity

This script will make an Entity jump in place with some bouncy easing applied to polish the movement.

// The height the Entity will jump above it's starting position.
bounceHeight = 20;

// The time it takes to reach the jump height.
jumpSeconds = 0.2;

// The time it takes to fall back down.
fallSeconds = 0.75;

private _originPosition = new Vector3();
private _jumpPosition = new Vector3();
private _jumping = false;
private _timeRemaining = 0;

start() {
    // Store the initial position so we can return to it later.
    this._originPosition.copy(this.entity.worldTransform.position);

    // Calculate the position at the top of the jump.
    this._jumpPosition.copy(this._originPosition).add(this.entity.worldTransform.getUp().setLength(this.bounceHeight));

    // Jump immediately.
    this.jump();
}

tick() {
    // Decrement time remaining.
    this._timeRemaining -= this.game.frameDeltaTime;

    // Get a ratio of time remaining between 0 - 1. Make sure we can't divide by 0.
    const ratio = 1 - (this._timeRemaining / Math.max(this._jumping ? this.jumpSeconds : this.fallSeconds, 0.001));

    // Handle jumping.
    if (this._jumping) {
        // Continue moving up if there is time left.
        if (this._timeRemaining > 0) {
            // Apply the Easing functions to the lerp values.
            this.entity.worldTransform.position.lerpVectors(this._originPosition, this._jumpPosition, Easing.Quadratic.out(ratio));
        }
        else {
            // Start falling.
            this.fall();
        }
    }
    // Handle falling.
    else if (!this._jumping) {
        // Continue moving down if there is time left.
        if (this._timeRemaining > 0) {
            // Apply the Easing functions to the lerp values.
            this.entity.worldTransform.position.lerpVectors(this._jumpPosition, this._originPosition, Easing.Bounce.out(ratio));
        }
        else {
            // Jump.
            this.jump();
        }
    }
}

private jump() {
    // Set the time remaining and jumping flag.
    this._timeRemaining = this.jumpSeconds;
    this._jumping = true;
}

private fall() {
    // Set the time remaining and jumping flag.
    this._timeRemaining = this.fallSeconds;
    this._jumping = false;
}