createComponent to define component types with typed schemas.| Type | Description |
|---|---|
Types.Int8, Types.Int16 | Integer numbers |
Types.Float32, Types.Float64 | Floating point numbers |
Types.Boolean | true/false values |
Types.String | Text strings |
Types.Vec2 | 2D vectors [x, y] |
Types.Vec3 | 3D vectors [x, y, z] |
Types.Vec4 | 4D vectors [x, y, z, w] |
Types.Color | RGBA colors [r, g, b, a] |
Types.Entity | References to other entities |
Types.Object | Any JavaScript object |
Types.Enum | String values from a defined set |
import { createComponent } from '@iwsdk/core';
export const Robot = createComponent('Robot', {});
import { createComponent, Types } from '@iwsdk/core';
export const Health = createComponent('Health', {
current: { type: Types.Float32, default: 100 },
max: { type: Types.Float32, default: 100 },
regenerating: { type: Types.Boolean, default: false },
});
export const Position = createComponent('Position', {
velocity: { type: Types.Vec3, default: [0, 0, 0] },
target: { type: Types.Vec3, default: [0, 0, 0] },
});
import { createSystem, eq, Types } from '@iwsdk/core';
export class MySystem extends createSystem(
{
// Regular query - entities with specific components
myQuery: { required: [ComponentA, ComponentB] },
// Query with exclusion - has ComponentA but NOT ComponentC
specialQuery: { required: [ComponentA], excluded: [ComponentC] },
// Query with value predicate - matches specific component values
configQuery: {
required: [PanelUI, PanelDocument],
where: [eq(PanelUI, 'config', '/ui/welcome.json')],
},
},
{
// Optional config schema - system-level configuration
speed: { type: Types.Float32, default: 1.0 },
enabled: { type: Types.Boolean, default: true },
},
) {
// System implementation
}
this.queries.queryName.entities (which returns a Set) and support reactive subscriptions:// Access entities in update() or init()
this.queries.myQuery.entities.forEach((entity) => {
// Process each entity
});
// React to entities entering/leaving queries
this.queries.welcomePanel.subscribe('qualify', (entity) => {
// Called when entity newly matches query
});
this.queries.welcomePanel.subscribe('disqualify', (entity) => {
// Called when entity stops matching query
});
this.config.propertyName.value:// Access config values
const currentSpeed = this.config.speed.value;
const isEnabled = this.config.enabled.value;
// React to config changes
this.config.speed.subscribe((value) => {
// React to speed changes
console.log('Speed changed to:', value);
});
init(): Called once when the system is registered with the world.init() {
// Initialize reusable objects for performance
this.tempVector = new Vector3();
// Set up reactive subscriptions
this.queries.myQuery.subscribe('qualify', (entity) => {
// Called when an entity newly matches the query
});
this.queries.myQuery.subscribe('disqualify', (entity) => {
// Called when an entity stops matching the query
});
}
update(delta: number, time: number): Called every frame.update(delta, time) {
// delta: Time since last frame (in seconds) - use for frame-rate independent movement
// time: Total elapsed time since start (in seconds) - use for animations and timing
this.queries.myQuery.entities.forEach((entity) => {
// Run logic on each matching entity
const position = entity.getComponent(Position);
position.velocity[0] *= delta; // Frame-rate independent movement
});
}
destroy(): Called when the system is unregistered.destroy() {
// Clean up resources, remove event listeners, etc.
this.tempVector = null;
}
this.queries: Access to defined queries and their entities.this.config: Access to system configuration values.this.world: Reference to the ECS world.this.player: Reference to XR player/camera rig (see XR Origin for more details).this.camera: Reference to the camera.isPaused: Whether the system is currently paused.// Register components first
world
.registerComponent(Robot)
.registerComponent(Health)
.registerComponent(Position);
// Then register systems
world.registerSystem(RobotSystem).registerSystem(HealthSystem, {
priority: -1, // Higher priority systems run first (negative = higher priority)
configData: { speed: 2.0 }, // Override default config values
});
import {
AudioUtils,
createComponent,
createSystem,
Pressed,
Vector3,
} from '@iwsdk/core';
// 1. Creating a tag component - no data, just tags entities as robots
export const Robot = createComponent('Robot', {});
// 2. Creating a system with two queries
export class RobotSystem extends createSystem({
robot: { required: [Robot] }, // All robot entities
robotClicked: { required: [Robot, Pressed] }, // Only clicked robots
}) {
private lookAtTarget;
private vec3;
// 3. init() - called when system is registered
init() {
// Performance: Create reusable objects once
this.lookAtTarget = new Vector3();
this.vec3 = new Vector3();
// Audio integration: Subscribe to click events
this.queries.robotClicked.subscribe('qualify', (entity) => {
AudioUtils.play(entity);
});
}
// 4. update() - called every frame
update() {
// Process all robot entities
this.queries.robot.entities.forEach((entity) => {
// Get player head position
this.player.head.getWorldPosition(this.lookAtTarget);
// Get robot's Three.js object and position
const spinnerObject = entity.object3D;
spinnerObject.getWorldPosition(this.vec3);
// Keep robots level (don't tilt up/down)
this.lookAtTarget.y = this.vec3.y;
// Make robot face player
spinnerObject.lookAt(this.lookAtTarget);
});
}
}
Robot is a tag component (empty schema) that tags entities.index.ts with world.registerSystem(RobotSystem).init() sets up resources and subscriptions, update() runs the behavior.AudioUtils.play(entity) with the entity’s AudioSource component.init().entity.object3D bridges ECS data with rendering.subscribe('qualify', ...) for reactive audio.lookAt() sets absolute rotation.