diff --git a/docs/documentation/godot-js-scripts/bindings.md b/docs/documentation/godot-js-scripts/bindings.md index dca0d52..009a00e 100644 --- a/docs/documentation/godot-js-scripts/bindings.md +++ b/docs/documentation/godot-js-scripts/bindings.md @@ -69,6 +69,113 @@ And, avoid coding property assignments like this: `node.position.x = 0;`, althou **It's not an error in javascript (which is more DANGEROUS)**, the actually modifed value is just a copy of `node.position`. +## Godot Data Structures + +Godot has its own types that represent arrays and dictionaries. Godot engine methods are passed and return these types rather than JavaScript arrays/objects. + +GodotJS provides convenience APIs to make working with these data structures easier. + +### Creating Godot Arrays + +You can convert JavaScript arrays to Godot arrays using `GArray.create`. + +```ts +const js_arr = [1, 2, 3]; +const godot_arr = GArray.create(js_arr); +``` + +Importantly, type information is retained i.e. the following will compile: + +```ts +const godot_arr = GArray.create([1, 2, 3]); +const a_number: number = godot_arr.get(0); +``` + +but this will not: + +```ts +const godot_arr = GArray.create([1, 2, 3]); +const a_string: string = godot_arr.get(0); +``` + +`GArray.create` works recursively: + +```ts +const godot_arr = GArray.create([["a", "b", "c"], ["x", "y", "z"]]); +const a_string: string = godot_arr.get(0).get(0); +``` + +### Creating Godot Dictionaries + +Similar to arrays, you can create Godot dictionaries from JS objects: + + +```ts +const godot_dict = GDictionary.create({ foo: "bar" }); +``` + +As with arrays, type information is retained when creating GDictionary. + +```ts +const godot_dict = GDictionary.create({ foo: "bar" }); +const foo: string = godot_dict.get("foo"); +``` + +### Accessing Godot Data Structures + +GodotJS provides a convenience API that allows you to treat `GArray`/`GDictionary` similar to their JavaScript equivalents. + +```ts +const godot_arr = GArray.create([1, 2, 3]); +const godot_arr_proxy = godot_arr.proxy(); +const a_number: number = godot_arr_proxy[0]; +``` + +Note the use of the indexer rather than needing to call `.get(0)`. + +This works equally well for assignment: + +```ts +godot_arr_proxy[0] = 42; +``` + +You can also access arbitrarily nested data too: + +```ts +const godot_dict = GDictionary.create({ + nested: [ + [{ foo: 'bar' }], + [{ foo: 'bar' }], + ], +}); +const godot_dict_proxy = godot_dict.proxy(); +const a_string: string = godot_dict_proxy.nested[0]![0]!.foo; +``` + +Accessing data this way is particularly useful if you want to pluck out a few values from a large data structure. However, when you access nested data this way JavaScript `Proxy` objects are created on the fly; which can cause overhead with frequent nested access. + +If you're performing frequent access, it'll likely be more efficient to convert the data structure to JavaScript objects just the once. + +You can use the spread operator to create a JavaScript object from a proxy: + +```ts +const godot_arr = GArray.create([ + ["a", "b", "c"], + ["x", "y", "z"], +]); +const js_shallow_arr = [...godot_arr.proxy()]; + +const godot_dict = GDictionary.create({ + nested: [ + [{ foo: 'bar' }], + [{ foo: 'bar' }], + ], +}); +const js_shallow_dict = { ...godot_dict.proxy() }; +``` + +Keep in mind that these are _shallow_ copies i.e. only the top level collection has been converted to a JavaScript object/array. Nested Godot collections will remain (as proxies). + ### StringName `StringName` optimization is completely transparent in javascript. When passing `StringName` parameters, a mapping between `StringName` and `v8::String` will be automatically cached to avoid repeatedly allocation of new `v8::String` objects and Godot `Variant` objects. diff --git a/docs/documentation/godot-js-scripts/signals.md b/docs/documentation/godot-js-scripts/signals.md index 146be5e..229aedf 100644 --- a/docs/documentation/godot-js-scripts/signals.md +++ b/docs/documentation/godot-js-scripts/signals.md @@ -29,20 +29,14 @@ Incorrect Example: this.some_signal.emit({ key: "value" }); // ❌ Raw JS object ``` -Correct Example: +You can use `GDictionary.create`/`GArray.create` to convert JS objects to their Godot equivalent data structure. ```ts import { GDictionary } from "godot"; -const data = new GDictionary(); -data.set("key", "value"); -this.some_signal.emit(data); // ✅ Godot dictionary +this.some_signal.emit(GDictionary.create({ key: "value" })); // ✅ Godot dictionary ``` -If raw JavaScript objects must be passed, consider converting them into `GDictionary` or `GArray` before emitting them as arguments. -If you use [godot-ts](https://github.com/godotjs/godot-ts) you can use -the functions `toGDictionary` and `fromGDictionary` from `generated/utils.ts`. - ## Connect and disconnect to a signal programmatically ```ts