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:
- In the File browser, click the plus button (+).
- Click "New file" → "New Component file", and give it a name (File extension optional).
- A new Component file will be generated, TypeScript by default, in your project.
- 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​
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​
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.
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.
Type | Description |
---|---|
ecs.eid | Entity Reference |
ecs.f32 | 32-bit floating-point number |
ecs.f64 | 64-bit floating-point number |
ecs.i32 | 32-bit integer |
ecs.ui8 | 8-bit unsigned integer |
ecs.ui32 | 32-bit unsigned integer |
ecs.string | String |
ecs.boolean | Boolean |
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.
- Data can not be read or written outside the Component it is defined in.
- Data does not have default values, however they can be set in the 'add' lifecycle method for similar functionality.
Lifecycle Methods​
Method | Description |
---|---|
add | Called once when the Component is initialized. Used to set up initial state and instantiate variables |
remove | Called 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. |
tick | Called on each render loop or tick of the scene. Used for continuous changes or checks. |
Parameters​
Property | Type | Description |
---|---|---|
world | World | Reference to the World. |
component | ComponentObject | Reference to the current Component. |
ComponentObject​
Use schemaAttribute or dataAttribute instead of eid, schema, or data properties in asynchronous contexts like timers or event handlers.
Property | Type | Description |
---|---|---|
eid | eid | The Entity ID of the current Component |
schema | Cursor | Reference to the current Entity's schema |
schemaAttribute | ComponentObject | Reference to the current Component's schema in World Scope. |
data | Cursor | Reference to the current Entity's data |
dataAttribute | ComponentObject | Reference to the current Component's data in World Scope. |