@ringozz/react-godot 4.7.2-612 → 4.7.2-614
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -0
- package/package.json +2 -2
- package/src/react-fiber.ts +8 -3
- package/src/react-types.ts +9 -0
package/README.md
CHANGED
|
@@ -90,6 +90,19 @@ If you already have a pre-existing Godot `Node`, `Resource`, or `PackedScene` in
|
|
|
90
90
|
- **A `PackedScene` `object` is instantiated once per host mount** — each element gets a fresh node tree; the result is a plain `Node` (not `.reference()`'d) freed on unmount. The same `PackedScene` can feed multiple elements; give same-type siblings stable `key`s.
|
|
91
91
|
- **Dynamic same-type sibling lists need stable `key`s.** Godot host nodes are identity, not content: React reconciles keyless siblings by position, so deleting the *first* of two same-type siblings reuses its Godot node for the survivor. A plain state re-render does this too, and Bun's web dev server makes it especially likely — its React Fast Refresh re-renders the edited component **in place** (no app re-run), reusing the same fibers and Godot nodes, so a deleted sibling's node can survive on its neighbor and the neighbor's node is freed. Add `key="..."` to each dynamically toggled same-type sibling to keep nodes pinned to the right elements.
|
|
92
92
|
|
|
93
|
+
### 1b. Attaching a Script with `script`
|
|
94
|
+
|
|
95
|
+
Use the `script` prop to attach a `Script` resource (e.g. an imported GDScript `.gd` module) to a node on creation. The script is attached **before the node enters the tree**, so Godot fires `_ready` and auto-enables the script's `_process`/`_input` — no manual `set_process(true)`/`set_process_input(true)` calls needed:
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
import controller from './camera_controller.gd';
|
|
99
|
+
|
|
100
|
+
<Camera3D script={use(controller)} current={true} fov={55} />
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
- **Attach before tree entry means `_ready`/`_process`/`_input` work.** Attaching to an already-in-tree node (e.g. in a `useEffect`) never fires `_ready` and leaves `_process`/`_input` disabled — prefer `script` over an effect for scripted nodes.
|
|
104
|
+
- **`script` is immutable on a mounted instance.** A re-render that changes `script` on an existing element throws — remount with a new `key` to swap the compiled script.
|
|
105
|
+
|
|
93
106
|
### 2. Resource Nesting with `attach`
|
|
94
107
|
|
|
95
108
|
In Godot, nodes often hold references to `Resource` objects (such as shapes, meshes, materials, or environments). Use the `attach` prop to automatically assign a child resource to a specific property on its parent:
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ringozz/react-godot",
|
|
3
3
|
"author": "Vladimir Davidovich",
|
|
4
|
-
"version": "4.7.2-
|
|
4
|
+
"version": "4.7.2-614",
|
|
5
5
|
"description": "A React renderer for Godot Engine via @ringozz/godot",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public"
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"*.md"
|
|
24
24
|
],
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@ringozz/godot": "^4.7.2-
|
|
26
|
+
"@ringozz/godot": "^4.7.2-614",
|
|
27
27
|
"@types/react-reconciler": "^0.33.0",
|
|
28
28
|
"react-reconciler": "^0.33.0"
|
|
29
29
|
},
|
package/src/react-fiber.ts
CHANGED
|
@@ -33,7 +33,7 @@ const EMPTY = Object.freeze({});
|
|
|
33
33
|
const attachments = new WeakMap<Instance, string>();
|
|
34
34
|
|
|
35
35
|
export function createInstance<T extends Instance>(type: Type, props: Props, root: Container, hostContext: HostContext, internalHandle: Reconciler.OpaqueHandle): T {
|
|
36
|
-
let { attach, object, ...rest } = props;
|
|
36
|
+
let { attach, object, script, ...rest } = props;
|
|
37
37
|
if (object instanceof PackedScene)
|
|
38
38
|
object = object.instantiate();
|
|
39
39
|
else if (object instanceof RefCounted)
|
|
@@ -43,6 +43,9 @@ export function createInstance<T extends Instance>(type: Type, props: Props, roo
|
|
|
43
43
|
if (attach)
|
|
44
44
|
attachments.set(instance, attach);
|
|
45
45
|
|
|
46
|
+
if (script)
|
|
47
|
+
instance.setScript(script);
|
|
48
|
+
|
|
46
49
|
commitUpdate(instance, type, EMPTY, rest, internalHandle);
|
|
47
50
|
return instance;
|
|
48
51
|
}
|
|
@@ -271,12 +274,14 @@ function isEqual(a: unknown, b: unknown) {
|
|
|
271
274
|
}
|
|
272
275
|
|
|
273
276
|
export function commitUpdate(instance: Instance, type: Type, prevProps: Props, nextProps: Props, internalHandle: Reconciler.OpaqueHandle): void {
|
|
274
|
-
const { attach: attachOld, object: objectOld, ref: refOld, children: childrenOld, ...restOld } = prevProps;
|
|
275
|
-
const { attach: attachNew, object: objectNew, ref: refNew, children: childrenNew, ...restNew } = nextProps;
|
|
277
|
+
const { attach: attachOld, object: objectOld, script: scriptOld, ref: refOld, children: childrenOld, ...restOld } = prevProps;
|
|
278
|
+
const { attach: attachNew, object: objectNew, script: scriptNew, ref: refNew, children: childrenNew, ...restNew } = nextProps;
|
|
276
279
|
if (attachOld !== attachNew)
|
|
277
280
|
throw new Error(`Cannot change attachment ${attachOld} to ${attachNew}`);
|
|
278
281
|
if (objectOld !== objectNew)
|
|
279
282
|
throw new Error(`Cannot change the \`object\` of a mounted instance (${objectOld} -> ${objectNew}); remount with a new key`);
|
|
283
|
+
if (scriptOld !== scriptNew)
|
|
284
|
+
throw new Error(`Cannot change the \`script\` of a mounted instance (${scriptOld} -> ${scriptNew}); remount with a new key`);
|
|
280
285
|
|
|
281
286
|
const toText = (c: unknown) => Array.isArray(c) ? c.join('') : c as string;
|
|
282
287
|
if (shouldSetTextContent(type, prevProps)) restOld['text'] = toText(childrenOld);
|
package/src/react-types.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type React from 'react';
|
|
|
6
6
|
import type { Object } from '@ringozz/godot/Object';
|
|
7
7
|
import type { Signal, GodotDictionary } from '@ringozz/godot';
|
|
8
8
|
import type { PackedScene } from '@ringozz/godot/PackedScene';
|
|
9
|
+
import type { Script } from '@ringozz/godot/Script';
|
|
9
10
|
|
|
10
11
|
// Identity check sensitive to readonly (deferred-conditional trick; the
|
|
11
12
|
// comparison lives in return-type position, so it is independent of
|
|
@@ -24,6 +25,14 @@ export type InstanceProps<T extends Instance = Instance> = {
|
|
|
24
25
|
object?: T | PackedScene;
|
|
25
26
|
/** Attaches the element to a named property of the parent instead of adding it as a child. */
|
|
26
27
|
attach?: string;
|
|
28
|
+
/** Attaches a Script (e.g. a GDScript) to the instance on creation, via
|
|
29
|
+
* `Object.setScript` *before* the node enters the tree. Attaching pre-tree —
|
|
30
|
+
* as opposed to in a mount effect on an already-in-tree node — lets Godot's
|
|
31
|
+
* NOTIFICATION_READY auto-enable the script's `_process`/`_input` (an
|
|
32
|
+
* already-in-tree node never fires `_ready`, so those callbacks stay
|
|
33
|
+
* disabled and must be enabled manually). Script is a method-only member of
|
|
34
|
+
* `Object` (not a settable property), so it gets dedicated handling here. */
|
|
35
|
+
script?: Script;
|
|
27
36
|
};
|
|
28
37
|
|
|
29
38
|
type ReactProps<P> = React.PropsWithChildren<React.RefAttributes<P>>;
|