Saltar al contenido principal

Custom Components

Introduction

Custom Components might define visual appearance, physical properties, input handling, or custom game logic.

Creating a Custom Component

To create a custom Component, follow these steps:

  1. In the File browser, click the plus button (+).
  2. Click "New file" → "New Component file", and give it a name (File extension optional).
    1. A new Component file will be generated, TypeScript by default, in your project.
    2. The new Component file will include the boilerplate code required to register the custom Component.

Registering a Custom Component

The following code is an example of how a newly created Custom Component will appear in the Code Editor:

Example

peligro

Avoid using ‘debug-’ as a prefix for component names. Component names starting with 'debug-' are reserved for internal debugging purposes and will not function correctly if used in your code.

// This is a Component file. You can use this file to define a custom Component for your project.
// This Component will appear as a custom Component in the editor.

import * as ecs from '@8thwall/ecs' // This is how you access the ecs library.

ecs.registerComponent({
name: 'custom-component',
schema: {
// Add data that can be configured on the Component.
},
schemaDefaults: {
// Add defaults for the schema fields.
},
data: {
// Add data that cannot be configured outside of the Component.
},
add: (world, component) => {
// Runs when the Component is added to the world.
},
tick: (world, component) => {
// Runs every frame.
},
remove: (world, component) => {
// Runs when the Component is removed from the world.
},
})

Adding Custom Components to an Entity

The following example shows how to add a custom Component to an existing entity.

Example

consejo

Make sure you export the Component before attempting to import or use it anywhere.

import * as ecs from '@8thwall/ecs'

const CustomComponent = ecs.registerComponent({
name: 'custom-component',
schema: {
foo: ecs.string
},
schemaDefaults: {
},
data: {
},
add: (world, component) => {
},
tick: (world, component) => {
},
remove: (world, component) => {
},
})

export {CustomComponent}
import CustomComponent from './custom-component' // The name of the file without the file type.

const demo = world.createEntity()

CustomComponent.set(world, demo, {
foo: 'bar'
})

Functions

Component functions allow you to perform different actions on a component and its data in relation to an entity.

Get

Returns a read-only reference.

Example

ecs.CylinderGeometry.get(world, component.eid)

Set

Ensures the component exists on the entity, then assigns the (optional) data to the component.

Example

ecs.CylinderGeometry.set(world, component.eid, {
radius: 1,
height: 1
})

Mutate

Perform an update to the component within a callback function. Return true to indicate no changes made.

Example

ecs.CylinderGeometry.mutate(world, component.eid, (cursor) => {
cursor.radius += 1;
cursor.height *= 2;
return false;
})

Remove

Removes the component from the entity.

Example

ecs.CylinderGeometry.remove(world, component.eid)

Has

Returns true if the component is present on the entity.

Example

ecs.CylinderGeometry.has(world, component.eid)

Reset

Adds, or resets the component to its default state.

Example

ecs.CylinderGeometry.reset(world, component.eid)

Advanced Functions

Cursor

Returns a mutable reference. Cursors are reused so only one cursor for each component can exist at a time.

Example
ecs.CylinderGeometry.cursor(world, component.eid)

Acquire

Same behavior as cursor, but commit must be called after the cursor is done being used.

Example
ecs.CylinderGeometry.acquire(world, component.eid)

Commit

Called after acquire. An optional third argument determines whether the cursor was mutated or not.

Example
ecs.CylinderGeometry.commit(world, component.eid)
ecs.CylinderGeometry.commit(world, component.eid, false)

Dirty

Mark the entity as having been mutated. Only needed in a specific case where systems are mutating data.

Example
ecs.CylinderGeometry.dirty(world, component.eid)

Schema

A schema is a list of key-value pairs that defines the structure of a Component. The key is the name of the property, and the value is an ECS type that specifies the kind of data that property will hold.

info

Currently, storing dynamically sized objects or lists isn’t supported. We’re actively exploring this feature and would love to hear about your specific use cases.

The following data types are useful for creating Schema properties on a Custom Component or references to a specific type.

TypeDescription
ecs.eidEntity Reference
ecs.f3232-bit floating-point number
ecs.f6464-bit floating-point number
ecs.i3232-bit integer
ecs.ui88-bit unsigned integer
ecs.ui3232-bit unsigned integer
ecs.stringString
ecs.booleanBoolean

Example

The following example shows a Custom Component's schema.

schema: {
target: ecs.eid, // Unique entity reference for the NPC (Entity ID)
speed: ecs.f32, // Movement speed of the NPC (32-bit float)
strength: ecs.f64, // Strength level for the NPC (64-bit float)
level: ecs.i32, // Character level of the NPC (32-bit integer)
armor: ecs.ui8, // Armor rating of the NPC (0-255, 8-bit unsigned integer)
experience: ecs.ui32, // Experience points of the NPC (32-bit unsigned integer)
guildName: ecs.string, // Name of the Guild NPC belongs to. (String)
isHostile: ecs.boolean // Boolean indicating if the NPC is hostile to the player (Boolean)
}
schemaDefaults: {
speed: 3.14,
strength: 5.8,
level: 10,
armor: 255,
experience: 12,
guildName: 'Niantic Crew'
isHostile: false
}

Custom Editor Fields

Display and functionality of your components in the entity editor can be customized in various ways. This is all done using comments inside the schema where fields are marked // @.

Labels

Sometimes labels in the editor need to be more descriptive than their names in code.

schema: {
// @label Foo
bar: ecs.eid,
},

Asset References

If you need to reference an asset rather than an entity.

schema: {
// @asset
yeti: ecs.eid,
}

Conditions

Properties can be set to only show depending on the values of other properties.

schema: {
// 'from' will only show if autoFrom set false:
autoFrom: ecs.boolean,
// @condition autoFrom=false
from: ecs.f32,

// 'easingFunction' will show if either easeIn or easeOut set:
easeIn: ecs.boolean,
easeOut: ecs.boolean,
// @condition easeIn=true|easeOut=true
easingFunction: ecs.string,

// 'targetX' only shows if no target set:
target: ecs.eid,
// @condition target=null
targetX: ecs.f32,
}

Enumerations

String properties can be limited to a set list:

schema: {
// @enum Quadratic, Cubic, Quartic, Quintic, Sinusoidal, Exponential
easingFunction: ecs.string,
}

Groups

Certain groups of properties can be instructed to be treated specially in the editor. Groups are configured as follows:

  • The start and end of the group is marked with // @group start … and // @group end
  • Conditions can be applied to the whole group with // @group condition
  • Two kinds of group currently supported: vector3 and color
Labels

Custom labels can still be used for individual fields:

schema: {
// @group start orient:vector3
// @label Pitch
orientPitch: ecs.f32,
// @label Yaw
orientYaw: ecs.f32,
// @label Roll
orientRoll: ecs.f32,
// @group end
}
Vector3

Groups of properties that represent 3D vectors can be indicated as follows:

schema: {
autoFrom: ecs.boolean,
// @group start from:vector3
// @group condition autoFrom=false
fromX: ecs.f32,
fromY: ecs.f32,
fromZ: ecs.f32,
// @group end
}
Color

Colors can be indicated as in the following example:

schema: {
// @group start background:color
bgRed: ecs.f32,
bgGreen: ecs.f32,
bgBlue: ecs.f32,
// @group end
}

Data

Data is similar to Schema, however there are two notable differences.

  1. Data can not be read or written outside the Component it is defined in.
  2. Data does not have default values, however they can be set in the 'add' lifecycle method for similar functionality.

Lifecycle Methods

MethodDescription
addCalled once when the Component is initialized. Used to set up initial state and instantiate variables
removeCalled when the Component is removed from the entity or when the entity is detached from the scene. Used to undo all previous modifications to the entity.
tickCalled on each render loop or tick of the scene. Used for continuous changes or checks.

Parameters

PropertyTypeDescription
worldWorldReference to the World.
componentComponentObjectReference to the current Component.

ComponentObject

aviso

Use schemaAttribute or dataAttribute instead of eid, schema, or data properties in asynchronous contexts like timers or event handlers.

PropertyTypeDescription
eideidThe Entity ID of the current Component
schemaCursorReference to the current Entity's schema
schemaAttributeComponentObjectReference to the current Component's schema in World Scope.
dataCursorReference to the current Entity's data
dataAttributeComponentObjectReference to the current Component's data in World Scope.