ECMAScript
ECMAScript copied to clipboard
@onready raise Error
The code is:
@onready("..")
parent: godot.Node;
and some errors were printed in console:
ERROR: object_method: Parameter "bind" is null. At: modules/ECMAScript/quickjs/quickjs_binder.cpp:146
ERROR: object_method: Parameter "bind" is null. At: modules/ECMAScript/quickjs/quickjs_binder.cpp:146
ERROR: object_method: Parameter "bind" is null. At: modules/ECMAScript/quickjs/quickjs_binder.cpp:146
What's wrong?
I get the same error.
As a minimal example, replace paintings.jsx with compiled form of this file:
https://gist.github.com/averagehat/55a96c33149b4702dac17a2c9acd2161
this.get_node works but @onready doesn't.
I think only default class and classes with '@gdclass' decorator can using this kind of decorators
要怎么使用这个@onready呢?会爆bind错误。现在暂时如下使用。
import { onready, signal, tool } from "../decorators";
@tool
export default class Hud extends godot.CanvasLayer {
@signal
static readonly OnStartGame: string;
// @onready("scoreLabel")
scoreLabel: godot.Label;
// @onready("message")
message: godot.Label;
// @onready("startButton")
startButton: godot.Button;
// @onready("messageTimer")
messageTimer: godot.Timer;
_ready() {
this.scoreLabel = this.$("scoreLabel") as godot.Label;
this.message = this.$("message") as godot.Label;
this.startButton = this.$("startButton") as godot.Button;
this.messageTimer = this.$("messageTimer") as godot.Timer;
this.showGameOver();
}
showMessage(text: string) {
this.message.text = text;
this.message.show();
this.messageTimer.start();
}
async showGameOver() {
this.showMessage("Game Over");
await godot.yield(this.messageTimer, "timeout");
this.message.text = "Dodge the\nCreeps!";
this.message.show();
godot.yield(this.get_tree().create_timer(1), "timeout");
this.startButton.show();
}
}
1
按照Typescript官方文档的说法,propertyDecorator只接收两个参数,忽略了返回值,建议只用于观察属性
NOTE A Property Descriptor is not provided as an argument to a property decorator due to how property decorators are initialized in TypeScript. This is because there is currently no mechanism to describe an instance property when defining members of a prototype, and no way to observe or modify the initializer for a property. The return value is ignored too. As such, a property decorator can only be used to observe that a property of a specific name has been declared for a class.
目前版本@onready的实现,返回一个descriptor,不是太符合文档中定义。
/**
* Return the node with `path` if the `_onready` is called
* @param path The path or the type to get the node
*/
export function onready<T extends godot.Node>(path: string | (new()=>godot.Node)) {
return function (target: T, property: string, descriptor?: any) {
const key = `$onready:${property}`;
descriptor = descriptor || {};
descriptor.set = function(v) { this[key] = v; };
descriptor.get = function() {
let v = this[key];
if (!v) {
v = (this as godot.Node).get_node(path as (new()=>godot.Node));
this[key] = v;
}
return v;
};
return descriptor;
}
}
2
造成 Parameter "bind" is null
错误的代码位置可能在https://github.com/GodotExplorer/ECMAScript/blob/master/ecmascript.cpp#L112。Editor会在脚本加载时,通过owner->get(*p)
获取对象属性值,调用相应的getter方法。此时,this对象似乎没有bind to native data,导致在getter中this.get_node
失败,报错。
3
一个workaround,修改@onready装饰器行为,使其接受一个factory函数,用于在_ready
中初始化属性值。这样的行为也符合GDScript官方文档的定义 https://docs.godotengine.org/en/latest/tutorials/scripting/gdscript/gdscript_basics.html#doc-gdscript-onready-annotation
@onready 将factory函数添加到队列中,然后统一在对象的_ready
方法执行
export function onready<T extends godot.Node, ValueType>(
factory: (node: T) => ValueType
) {
return function (target: T, property: string) {
const handlersKey = '$onready:handlers'
let handlers = Reflect.get(target, handlersKey) as {
property: string
factory: (node: godot.Node) => unknown
}[]
// set onready callbacks
if (!handlers) {
handlers = []
Reflect.set(target, handlersKey, handlers)
// get _ready
const oldReady: () => void = Reflect.get(target, '_ready') || (() => {})
// modify _ready
Reflect.defineProperty(target, '_ready', {
value: function () {
for (const { property, factory } of handlers) {
// init value
this[property] = factory(this)
}
oldReady.call(this)
},
})
}
// add handler
handlers.push({ factory, property })
}
}
之后能够在代码中使用@onready,且没有bind错误
4 Example
export default class Player extends godot.Area2D {
@onready((n) => n.$('AnimatedSprite'))
private animatedSprite: godot.AnimatedSprite
@onready((n: Player) => n.get_viewport_rect().size)
private screenSize: godot.Vector2
@onready((n) => n.$('CollisionShape2D'))
private collisionShape2D: godot.CollisionShape2D
_ready() {
console.log(`ready`)
}
}
Hi! Any fix regarding this ? It works but still error messages, thanks a lot!
Are you missing @gdclass decorator on your class?
Tried it but still displays the error, but the error doesn't prevent things from working, so I guess I will just ignore it.