Skip to content

Entity

What is an Entity?

Entity is the fundamental building block in Mage. Everything you see in a scene—meshes, lights, cameras, sounds, particles—is an Entity or extends from Entity.

Think of Entity as a container that:

  • Wraps a Three.js object (the "body")
  • Provides a unified API for transforms (position, rotation, scale)
  • Enables smooth animations via tweening
  • Supports parent-child hierarchies
  • Can have behaviors attached via Scripts
  • Emits events you can listen to

The Entity Hierarchy

Entity (base class)
├── Element (meshes, primitives)
├── Model (loaded 3D models)
├── Light (AmbientLight, PointLight, etc.)
├── Camera
├── Sprite
├── Audio (sound sources)
└── Particle (particle systems)

You'll rarely create Entity directly—instead you'll create specific types like Cube, Model, or PointLight that inherit Entity's capabilities.


Common Workflows

Moving and Rotating

Every Entity has position, rotation, and scale:

javascript
// Direct positioning
player.setPosition({ x: 10, y: 0, z: -5 });

// Move relative to current position
player.translate({ x: 1, y: 0, z: 0 });

// Rotate (in radians)
player.setRotation({ y: Math.PI / 2 }); // 90 degrees

// Scale
player.setScale({ x: 2, y: 2, z: 2 }); // Double size

TIP

All transform methods accept partial objects. setPosition({ y: 5 }) only changes Y, keeping X and Z unchanged.

Smooth Animations with Tweening

Move entities smoothly over time:

javascript
// Move to position over 2 seconds
await player.goTo({ x: 100, y: 0, z: 0 }, 2000);

// Rotate to angle over 1 second
await enemy.rotateTo({ y: Math.PI }, 1000);

// Scale up over 500ms
await powerup.scaleTo({ x: 2, y: 2, z: 2 }, 500);

// Chain animations
await door.rotateTo({ y: Math.PI / 2 }, 1000);
await door.goTo({ x: -2 }, 500);

TIP

All tween methods return Promises, making it easy to sequence animations with await.

Parent-Child Relationships

Build complex objects from simpler parts:

javascript
// Create a character with attached weapon
const character = Models.get('character');
const sword = Models.get('sword');

character.add(sword);
sword.setPosition({ x: 0.5, y: 0, z: 0 }); // Position relative to character

// Now when character moves, sword moves with it
character.setPosition({ x: 10, y: 0, z: 0 });

Adding Behaviors with Scripts

javascript
import { Scripts } from 'mage-engine';

// Attach behavior to an entity
enemy.addScript('patrol', { 
    waypoints: [point1, point2, point3],
    speed: 5
});

player.addScript('playerControls');

Listening to Events

javascript
import { ENTITY_EVENTS } from 'mage-engine';

// When entity is disposed
entity.addEventListener(ENTITY_EVENTS.DISPOSE, () => {
    console.log('Entity was removed');
});

// Animation events
character.addEventListener(ENTITY_EVENTS.ANIMATION.FINISHED, () => {
    console.log('Animation completed');
});

Transform Methods

Position

MethodDescription
getPosition()Returns current position as Vector3
setPosition({ x?, y?, z? })Sets position (partial updates ok)
translate({ x, y, z })Moves relative to current position
goTo(target, time, easing)Smoothly animates to position

Rotation

MethodDescription
getRotation()Returns current rotation as Vector3
setRotation({ x?, y?, z? })Sets rotation in radians
getQuaternion()Returns rotation as Quaternion
setQuaternion({ x?, y?, z?, w? })Sets quaternion rotation
rotateTo(target, time, easing)Smoothly animates rotation

Scale

MethodDescription
getScale()Returns current scale as Vector3
setScale({ x?, y?, z? })Sets scale (partial updates ok)
scaleTo(target, time, easing)Smoothly animates scale

World vs Local Transforms

When an Entity is a child of another Entity, its transforms are relative to the parent:

javascript
parent.setPosition({ x: 10, y: 0, z: 0 });
child.setPosition({ x: 5, y: 0, z: 0 });  // World position is 15, 0, 0

// Get world (absolute) transform
const worldTransform = child.getWorldTransform();
console.log(worldTransform.position); // { x: 15, y: 0, z: 0 }

Hierarchy Methods

MethodDescription
add(child)Add Entity as a child
remove(child)Remove child from hierarchy
hasParent()Check if this Entity has a parent
getParent()Get parent Entity
hasChildren()Check if Entity has children
isParentOf(entity)Check if this is parent of entity
getHierarchy()Get tree structure of children

Tags

Tags help you organize and query entities:

javascript
// Tag entities
enemy.addTag('hostile');
enemy.addTags(['spawnable', 'boss']);

// Query by tag later
const allEnemies = Scene.getByTag('hostile');

// Check tags
if (entity.hasTag('hostile')) {
    player.takeDamage(10);
}
MethodDescription
addTag(tag)Add a single tag
addTags(tags)Add multiple tags
removeTag(tag)Remove a tag
removeAllTags()Remove all tags (keeps 'all')
hasTag(tag)Check if entity has tag
getTags()Get all tags

TIP

All entities automatically have the 'all' tag, which cannot be removed.


Scripts

Attach reusable behaviors to entities:

javascript
// Add scripts
player.addScript('movement', { speed: 10 });
player.addScript('shooting', { fireRate: 0.5 });

// Check for scripts
if (player.hasScripts()) {
    const movement = player.getScript('movement');
    movement.setSpeed(15);
}

// Enable/disable
player.disableScripts();  // Pause all behaviors
player.enableScripts();   // Resume
MethodDescription
addScript(name, options)Attach a script
getScript(name)Get attached script by name
hasScripts()Check if entity has scripts
enableScripts()Enable all scripts
disableScripts()Disable all scripts

TIP

See Scripts documentation for creating custom scripts.


Body Access

The "body" is the underlying Three.js object:

javascript
// Get the Three.js object directly
const mesh = entity.getBody();

// Access materials, geometry, etc.
mesh.material.color.set(0xff0000);

// Find nested objects by name
const wheel = car.getBodyByName('front_wheel_left');
MethodDescription
getBody()Get the Three.js object
setBody({ body })Set the Three.js object
hasBody()Check if body exists
getBodyByName(name)Find child in body hierarchy

Data Storage

Store arbitrary data on entities:

javascript
// Store data
enemy.setData('health', 100);
enemy.setData('lootTable', ['gold', 'potion', 'sword']);

// Retrieve later
const health = enemy.getData('health');
enemy.setData('health', health - 25);

Entity Events

EventWhen Fired
ENTITY_EVENTS.DISPOSEEntity is removed from scene
ENTITY_EVENTS.ANIMATION.LOOPAnimation loops
ENTITY_EVENTS.ANIMATION.FINISHEDAnimation completes
ENTITY_EVENTS.STATE_MACHINE.CHANGEState machine changes state

Lifecycle

Created → Added to Scene → Active → Disposed
              │                      │
              ├── Scripts start      ├── Scripts disposed
              ├── Update loop runs   ├── Children disposed
              └── Events dispatched  └── DISPOSE event fired

Cleaning up:

javascript
// Remove entity from scene
entity.dispose();

// This automatically:
// 1. Disposes all children
// 2. Disposes all scripts
// 3. Removes from parent
// 4. Fires DISPOSE event