A javascript class can extend a Godot Object class:
This example is written in TypeScript.
import { Node, Signal0, Callable } from "godot";
import { signal, seconds } from "godot.annotations";
export default class MyJSNode extends Node {
@signal()
test!: Signal0; // it's technically identical to Signal, but with type restriction for parameters
private _on_test() {
}
_ready() {
console.log("MyJSNode _ready");
this.test.connect(Callable.create(this, this._on_test), 0);
this.test.emit();
this.test.disconnect(Callable.create(this, this._on_test));
}
// it's also possible to use async functions in scripts (even the `_ready` call)
async call_me() {
let ticks = 5;
for (let i = ticks; i > 0; i--) {
(<Label>this.get_node("Control/Label")).text = `wait for ${i} seconds with await`;
await seconds(1);
}
(<Label>this.get_node("Control/Label")).text = `${ticks} seconds passed`;
}
}
Warning
Explicitly defined constructor
in script classes inherited from Godot Object are not recommended, because GodotJS constructs the script classes for special uses (such as CDO and cross-binding).
If it can't be avoided, always define it with an explicit argument identifier? any
, and don't forget to call super(identifier)
.
For instance:
export default class MyExampleNode extends Node {
constructor(identifier?: any) {
super(identifier);
// do other things you want
//...
}
}
You can instantiate a script class directly with new
in scripts:
// do not pass any arguments to the constructor
let node = new MyExampleNode();
Note
A class must be exported as default
, otherwise the script will not recognized as a valid script class.
Compile the typescript source into javascript, and attach the compiled script to a Node:
Currently, GodotJS
doesn't provide sufficient support for using code from npm packages. Because many factors are involved in it, such as:
- Scripts depend on functionalities of node.js which is not supported
- How the typescript/javascript project is packaged (archive into a single script file or not)
- Javascript modular standard variants may be involved
If a npm package just works, there is no guarantee that it does also work after packaging, GodotJS
tries to export all dependant javascript sources into the targeting package.
Note
See read_xlsx.ts about using a npm package.
In GodotJS
, class member properties/variables can be exported. This means their value gets saved along with the resource (such as the scene) they're attached to. They will also be available for editing in the property editor. Exporting is done by using the @export_
annotation.
export default class Shooter extends Sprite2D {
// type must be explicitly provided as the first parameter of @export_
// cuz static type is actually a phantom in typescript
@export_(Variant.Type.TYPE_FLOAT)
speed: number = 0;
// ...
}
In this example the value 0
will be saved and visible in the property editor.
The retrieval of default value is implemented through Class Default Object (CDO)
. GodotJS
will instantiate a pure javascript instance of the script class (Shooter
in this example) as CDO
, then the property value is read from CDO
as default value
in the property editor.
Note
Be cautious when coding within constructor
, as it is probably called for initializing CDO
.
@export_(Variant.Type.TYPE_STRING)
address: string = "somewhere"; // `:string` can be omitted here
@export_(Variant.Type.TYPE_INT)
age: number = 0; // `:number` can be omitted here
If there's no default value, default value
of the give type will be used (0
in this case).
@export_(Variant.Type.TYPE_INT)
age: number;
Enum value properties can be exported with the built-in support in the property editor.
Note
So far, only int
is supported as enum value.
@export_enum(MyColor)
color: MyColor = MyColor.White;
The value can be easily chosen from a dropdown list in the editor.
NOT IMPLEMENTED FOR NOW
An icon can be used as node icon in the editor scene hierarchy with the annotation @icon
.
@icon("res://icon/affiliate.svg")
export default class MySprite extends Sprite2D {
}
Cyclic imports are allowed in GodotJS
with some limits.
// file: cyclic_import_1.ts
import { CyclicClass2 } from "./cyclic_import_2";
// NOT OK: The behaviour is undefined if anything from cyclic imported modules is referenced in the script compile-run scope
// let a_name = CyclicClass2.something;
// NOT OK: extends a class from cyclic imported modules
// class BehaviorUndefined extends CyclicClass2 {}
// OK: references at runtime
export class CyclicClass1 {
static call1() {
console.log("call1");
CyclicClass2.call2();
}
static call3() {
console.log("call3");
}
}
// file: cyclic_import_2.ts
import { CyclicClass1 } from "./cyclic_import_1";
export class CyclicClass2 {
static call2() {
console.log("call2");
CyclicClass1.call3();
}
}
WRITE SOMETHING HERE
It's recommended to use external editor to write typescripts. Change the settings in Editor > Edtior Settings > Text Editor > Use External Editor
, and replace the Exec Path
with the code editor installed locally: