The following table contains available Typescript decorators that Needle Engine provides.
You can think of them as Attributes on steroids (if you are familiar with C#) - they can be added to classes, fields or methods in Typescript to provide additional functionality.
Field & Property Decorators | |
@serializable() | Add to exposed / serialized fields. Is used when loading glTF files that have been exported with components from Unity or Blender. |
@syncField() | Add to a field to network the value when it changes. You can pass in a method to be called when the field changes |
@validate() | Add to receive callbacks in the component event method onValidate whenever the value changes. This behaves similar to Unity's onValidate. |
Method Decorators | |
@prefix(<type>) (experimental) | Can be used to easily inject custom code into other components. Optionally return false to prevent the original method from being executed. See the example below |
Class Decorators | |
@registerType | No argument. Can be added to a custom component class to be registered to the Needle Engine types and to enable hot reloading support. |
Examples
Serializable
import { class Behaviour
Needle Engine component base class. Component's are the main building blocks of the Needle Engine.
Derive from
Behaviour
to implement your own using the provided lifecycle methods.
Components can be added to threejs objects using [addComponent](addComponent)
or [GameObject.addComponent](GameObject.addComponent)
The most common lifecycle methods are awake
, start
, onEnable
, onDisable
update
and onDestroy
.
XR specific callbacks include onEnterXR
, onLeaveXR
, onUpdateXR
, onControllerAdded
and onControllerRemoved
.
To receive pointer events implement onPointerDown
, onPointerUp
, onPointerEnter
, onPointerExit
and onPointerMove
.
Behaviour, const serializable: <T>(type?: Constructor<T> | TypeResolver<T> | (TypeResolver<T> | Constructor<any>)[] | null | undefined) => (_target: any, _propertyKey: string | {
name: string;
}) => void
The serializable attribute should be used to annotate all serialized fields / fields and members that should be serialized and exposed in an editor
serializable, class EventList
The EventList is a class that can be used to create a list of event listeners that can be invoked
EventList } from "@needle-tools/engine";
import { class Object3D<TEventMap extends Object3DEventMap = Object3DEventMap>
interface Object3D<TEventMap extends Object3DEventMap = Object3DEventMap>
This is the base class for most objects in three.js and provides a set of properties and methods for manipulating objects in 3D space.
Object3D } from "three";
export class class SomeComponentType
SomeComponentType extends class Behaviour
Needle Engine component base class. Component's are the main building blocks of the Needle Engine.
Derive from
Behaviour
to implement your own using the provided lifecycle methods.
Components can be added to threejs objects using [addComponent](addComponent)
or [GameObject.addComponent](GameObject.addComponent)
The most common lifecycle methods are awake
, start
, onEnable
, onDisable
update
and onDestroy
.
XR specific callbacks include onEnterXR
, onLeaveXR
, onUpdateXR
, onControllerAdded
and onControllerRemoved
.
To receive pointer events implement onPointerDown
, onPointerUp
, onPointerEnter
, onPointerExit
and onPointerMove
.
Behaviour {}
export class class ButtonObject
ButtonObject extends class Behaviour
Needle Engine component base class. Component's are the main building blocks of the Needle Engine.
Derive from
Behaviour
to implement your own using the provided lifecycle methods.
Components can be added to threejs objects using [addComponent](addComponent)
or [GameObject.addComponent](GameObject.addComponent)
The most common lifecycle methods are awake
, start
, onEnable
, onDisable
update
and onDestroy
.
XR specific callbacks include onEnterXR
, onLeaveXR
, onUpdateXR
, onControllerAdded
and onControllerRemoved
.
To receive pointer events implement onPointerDown
, onPointerUp
, onPointerEnter
, onPointerExit
and onPointerMove
.
Behaviour {
// you can omit the type if it's a primitive
// e.g. Number, String or Bool
@serializable<unknown>(type?: Constructor<unknown> | TypeResolver<unknown> | (Constructor<any> | TypeResolver<unknown>)[] | null | undefined): (_target: any, _propertyKey: string | {
...;
}) => void
The serializable attribute should be used to annotate all serialized fields / fields and members that should be serialized and exposed in an editor
serializable()
ButtonObject.myNumber: number
myNumber: number = 42;
// otherwise add the concrete type that you want to serialize to
@serializable<EventList>(type?: Constructor<EventList> | TypeResolver<EventList> | (Constructor<any> | TypeResolver<EventList>)[] | null | undefined): (_target: any, _propertyKey: string | {
...;
}) => void
The serializable attribute should be used to annotate all serialized fields / fields and members that should be serialized and exposed in an editor
serializable(class EventList
The EventList is a class that can be used to create a list of event listeners that can be invoked
EventList)
ButtonObject.onClick?: EventList | undefined
onClick?: class EventList
The EventList is a class that can be used to create a list of event listeners that can be invoked
EventList;
@serializable<SomeComponentType>(type?: Constructor<SomeComponentType> | TypeResolver<SomeComponentType> | (Constructor<...> | TypeResolver<...>)[] | null | undefined): (_target: any, _propertyKey: string | {
...;
}) => void
The serializable attribute should be used to annotate all serialized fields / fields and members that should be serialized and exposed in an editor
serializable(class SomeComponentType
SomeComponentType)
ButtonObject.myComponent?: SomeComponentType | undefined
myComponent?: class SomeComponentType
SomeComponentType;
// Note that for arrays you still add the concrete type (not the array)
@serializable<Object3D<Object3DEventMap>>(type?: Constructor<Object3D<Object3DEventMap>> | TypeResolver<Object3D<Object3DEventMap>> | (Constructor<...> | TypeResolver<...>)[] | null | undefined): (_target: any, _propertyKey: string | {
...;
}) => void
The serializable attribute should be used to annotate all serialized fields / fields and members that should be serialized and exposed in an editor
serializable(class Object3D<TEventMap extends Object3DEventMap = Object3DEventMap>
This is the base class for most objects in three.js and provides a set of properties and methods for manipulating objects in 3D space.
Object3D)
ButtonObject.myObjects?: Object3D<Object3DEventMap>[] | undefined
myObjects?: class Object3D<TEventMap extends Object3DEventMap = Object3DEventMap>
interface Object3D<TEventMap extends Object3DEventMap = Object3DEventMap>
This is the base class for most objects in three.js and provides a set of properties and methods for manipulating objects in 3D space.
Object3D[];
}
SyncField
The @syncField
decorator can be used to automatically network properties of your components for all users (visitors of your website) connected to the same networking room. It can optionally take a callback function that will be invoked whenever the value changes.
- To notify the system that a reference value (like an object or an array) has changed you need to re-assign the field. E.g. like this:
myField = myField
- The callback function can not be an arrow function (e.g.
MyScript.prototype.onNumberChanged
works foronNumberChanged() { ... }
but it does not formyNumberChanged = () => { ... }
)
import { class Behaviour
Needle Engine component base class. Component's are the main building blocks of the Needle Engine.
Derive from
Behaviour
to implement your own using the provided lifecycle methods.
Components can be added to threejs objects using [addComponent](addComponent)
or [GameObject.addComponent](GameObject.addComponent)
The most common lifecycle methods are awake
, start
, onEnable
, onDisable
update
and onDestroy
.
XR specific callbacks include onEnterXR
, onLeaveXR
, onUpdateXR
, onControllerAdded
and onControllerRemoved
.
To receive pointer events implement onPointerDown
, onPointerUp
, onPointerEnter
, onPointerExit
and onPointerMove
.
Behaviour, const serializable: <T>(type?: Constructor<T> | TypeResolver<T> | (TypeResolver<T> | Constructor<any>)[] | null | undefined) => (_target: any, _propertyKey: string | {
name: string;
}) => void
The serializable attribute should be used to annotate all serialized fields / fields and members that should be serialized and exposed in an editor
serializable, const syncField: (onFieldChanged?: string | FieldChangedCallbackFn) => (target: any, _propertyKey: string | {
name: string;
}) => void
Decorate a field to be automatically networked synced
Primitive values are all automatically synced (like string, boolean, number).
For arrays or objects make sure to re-assign them (e.g. this.mySyncField = this.mySyncField
) to trigger an update
syncField } from "@needle-tools/engine";
export class class MyScript
MyScript extends class Behaviour
Needle Engine component base class. Component's are the main building blocks of the Needle Engine.
Derive from
Behaviour
to implement your own using the provided lifecycle methods.
Components can be added to threejs objects using [addComponent](addComponent)
or [GameObject.addComponent](GameObject.addComponent)
The most common lifecycle methods are awake
, start
, onEnable
, onDisable
update
and onDestroy
.
XR specific callbacks include onEnterXR
, onLeaveXR
, onUpdateXR
, onControllerAdded
and onControllerRemoved
.
To receive pointer events implement onPointerDown
, onPointerUp
, onPointerEnter
, onPointerExit
and onPointerMove
.
Behaviour {
@function syncField(onFieldChanged?: string | FieldChangedCallbackFn | undefined): (target: any, _propertyKey: string | {
name: string;
}) => void
Decorate a field to be automatically networked synced
Primitive values are all automatically synced (like string, boolean, number).
For arrays or objects make sure to re-assign them (e.g. this.mySyncField = this.mySyncField
) to trigger an update
syncField(class MyScript
MyScript.MyScript.prototype: MyScript
prototype.MyScript.onNumberChanged(newValue: number, oldValue: number): void
onNumberChanged)
@serializable<unknown>(type?: Constructor<unknown> | TypeResolver<unknown> | (Constructor<any> | TypeResolver<unknown>)[] | null | undefined): (_target: any, _propertyKey: string | {
...;
}) => void
The serializable attribute should be used to annotate all serialized fields / fields and members that should be serialized and exposed in an editor
serializable()
MyScript.myNumber: number
myNumber: number = 42;
private MyScript.onNumberChanged(newValue: number, oldValue: number): void
onNumberChanged(newValue: number
newValue: number, oldValue: number
oldValue: number){
var console: Console
The console
module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
- A
Console
class with methods such as console.log()
, console.error()
and console.warn()
that can be used to write to any Node.js stream.
- A global
console
instance configured to write to process.stdout
and
process.stderr
. The global console
can be used without calling require('console')
.
Warning: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the note on process I/O
for
more information.
Example using the global console
:
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console
class:
const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err
console.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to stdout
with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to printf(3)
(the arguments are all passed to util.format()
).
const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout
See util.format()
for more information.
log("Number changed from ", oldValue: number
oldValue, "to", newValue: number
newValue)
}
}
Validate
import { class Behaviour
Needle Engine component base class. Component's are the main building blocks of the Needle Engine.
Derive from
Behaviour
to implement your own using the provided lifecycle methods.
Components can be added to threejs objects using [addComponent](addComponent)
or [GameObject.addComponent](GameObject.addComponent)
The most common lifecycle methods are awake
, start
, onEnable
, onDisable
update
and onDestroy
.
XR specific callbacks include onEnterXR
, onLeaveXR
, onUpdateXR
, onControllerAdded
and onControllerRemoved
.
To receive pointer events implement onPointerDown
, onPointerUp
, onPointerEnter
, onPointerExit
and onPointerMove
.
Behaviour, const serializable: <T>(type?: Constructor<T> | TypeResolver<T> | (TypeResolver<T> | Constructor<any>)[] | null | undefined) => (_target: any, _propertyKey: string | {
name: string;
}) => void
The serializable attribute should be used to annotate all serialized fields / fields and members that should be serialized and exposed in an editor
serializable, const validate: (set?: setter, get?: getter) => (target: IComponent | any, propertyKey: string, descriptor?: undefined) => void
create accessor callbacks for a field
validate } from "@needle-tools/engine";
export class class MyScript
MyScript extends class Behaviour
Needle Engine component base class. Component's are the main building blocks of the Needle Engine.
Derive from
Behaviour
to implement your own using the provided lifecycle methods.
Components can be added to threejs objects using [addComponent](addComponent)
or [GameObject.addComponent](GameObject.addComponent)
The most common lifecycle methods are awake
, start
, onEnable
, onDisable
update
and onDestroy
.
XR specific callbacks include onEnterXR
, onLeaveXR
, onUpdateXR
, onControllerAdded
and onControllerRemoved
.
To receive pointer events implement onPointerDown
, onPointerUp
, onPointerEnter
, onPointerExit
and onPointerMove
.
Behaviour {
@function validate(set?: setter | undefined, get?: getter | undefined): (target: any, propertyKey: string, descriptor?: undefined) => void
create accessor callbacks for a field
validate()
@serializable<unknown>(type?: Constructor<unknown> | TypeResolver<unknown> | (Constructor<any> | TypeResolver<unknown>)[] | null | undefined): (_target: any, _propertyKey: string | {
...;
}) => void
The serializable attribute should be used to annotate all serialized fields / fields and members that should be serialized and exposed in an editor
serializable()
MyScript.myNumber?: number | undefined
myNumber?: number;
MyScript.start(): void
called at the beginning of a frame (once per component)
start() { function setInterval<[]>(callback: () => void, ms?: number | undefined): NodeJS.Timeout (+2 overloads)
Schedules repeated execution of callback
every delay
milliseconds.
When delay
is larger than 2147483647
or less than 1
, the delay
will be
set to 1
. Non-integer delays are truncated to an integer.
If callback
is not a function, a TypeError
will be thrown.
This method has a custom variant for promises that is available using timersPromises.setInterval()
.
setInterval(() => this.MyScript.myNumber?: number | undefined
myNumber = var Math: Math
An intrinsic object that provides basic mathematics functionality and constants.
Math.Math.random(): number
Returns a pseudorandom number between 0 and 1.
random(), 1000) }
MyScript.onValidate(fieldName: string): void
called when you decorate fields with the
onValidate(fieldName: string
fieldName: string) {
var console: Console
The console
module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
- A
Console
class with methods such as console.log()
, console.error()
and console.warn()
that can be used to write to any Node.js stream.
- A global
console
instance configured to write to process.stdout
and
process.stderr
. The global console
can be used without calling require('console')
.
Warning: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the note on process I/O
for
more information.
Example using the global console
:
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console
class:
const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err
console.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to stdout
with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to printf(3)
(the arguments are all passed to util.format()
).
const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout
See util.format()
for more information.
log("Validate", fieldName: string
fieldName, this.MyScript.myNumber?: number | undefined
myNumber);
}
}
Prefix
import { class Camera
Camera, const prefix: <T>(type: Constructor<T>) => (target: IComponent | any, _propertyKey: string | {
name: string;
}, _PropertyDescriptor: PropertyDescriptor) => void
Experimental attribute
Use to hook into another type's methods and run before the other methods run (similar to Harmony prefixes).
Return false to prevent the original method from running.
prefix } from "@needle-tools/engine";
class class YourClass
YourClass {
@prefix<Camera>(type: Constructor<Camera>): (target: any, _propertyKey: string | {
name: string;
}, _PropertyDescriptor: PropertyDescriptor) => void
Experimental attribute
Use to hook into another type's methods and run before the other methods run (similar to Harmony prefixes).
Return false to prevent the original method from running.
prefix(class Camera
Camera) // < this is type that has the method you want to change
YourClass.awake(): void
awake() { // < this is the method name you want to change
// this is now called before the Camera.awake method runs
// NOTE: `this` does now refer to the Camera instance and NOT `YourClass` anymore. This allows you to access internal state of the component as well
var console: Console
The console
module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
- A
Console
class with methods such as console.log()
, console.error()
and console.warn()
that can be used to write to any Node.js stream.
- A global
console
instance configured to write to process.stdout
and
process.stderr
. The global console
can be used without calling require('console')
.
Warning: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the note on process I/O
for
more information.
Example using the global console
:
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console
class:
const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err
console.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to stdout
with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to printf(3)
(the arguments are all passed to util.format()
).
const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout
See util.format()
for more information.
log("Hello camera:", this)
// optionally return false if you want to prevent the default behaviour
}
}