@ringozz/react-godot 4.7.2-583 → 4.7.2-585
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 +10 -9
- package/package.json +2 -2
- package/src/react-hooks.ts +8 -10
- package/src/react-types.ts +14 -16
package/README.md
CHANGED
|
@@ -123,9 +123,9 @@ Note for physics bodies: `attach="shape"` goes on the shape **resource** element
|
|
|
123
123
|
|
|
124
124
|
Props map directly to Godot node setters and properties. Helper arrays are automatically cast to Godot types:
|
|
125
125
|
|
|
126
|
-
- **Vectors**: `position={[0, 1, 0]}`, `
|
|
127
|
-
- **Colors**: `albedoColor={[1, 0.2, 0.2]}`
|
|
128
|
-
- **Packed arrays**: `Packed*Array`-typed props (e.g. `Gradient.colors`/`offsets`) accept plain JS arrays — elements convert with the array's element type, so `colors={[[0.5, 0, 0, 1], [1, 1, 1, 1]]}` works; `new PackedVector2Array([[1, 2], [3, 4]])` works too.
|
|
126
|
+
- **Vectors / value types**: value types are **named tuples** — flat (`Vector3`, `Color`, `Rect2`, …) are scalar-labeled tuples (`Vector3 = [x, y, z]`, `Color = [r, g, b, a?]`); nested (`AABB`/`Transform2D`/`Basis`/`Transform3D`/`Projection`) are row-labeled tuples of the flat element alias (`Transform3D = [x, y, z, origin]`, each row a `Vector3`), so `position={[0, 1, 0]}`, `transform={[[1, 0, 0], [0, 1, 0], [0, 0, 1], [10, 20, 30]]}`. Getters return the same labeled shape (`node.position` is `[x, y, z]`); direct setters accept them too (`node.position = [1, 2, 3]`). **Wrong-arity arrays fail typecheck** (`position={[1, 2]}` errors) — except `Color`'s trailing `a`, which is optional (`[r, g, b, a?]`, alpha defaults 1; it's the only value type with a partial scalar ctor). The math API lives as named functions per type module — `import * as v3 from '@ringozz/godot/Vector3'`, then `v3.normalized(v)`, `v3.dot(a, b)`; construction uses `fromXxx` factories (`basis.fromAxisAngle(axis, angle)`, `transform3d.fromBasisOrigin(basis, origin)`).
|
|
127
|
+
- **Colors**: `albedoColor={[1, 0.2, 0.2]}` (a `Color` is just `[r, g, b, a?]`).
|
|
128
|
+
- **Packed arrays**: `Packed*Array`-typed props (e.g. `Gradient.colors`/`offsets`) accept plain JS arrays — elements convert with the array's element type, so `colors={[[0.5, 0, 0, 1], [1, 1, 1, 1]]}` works; `new PackedVector2Array([[1, 2], [3, 4]])` works too. The same element-aware conversion applies to **direct setters and method args**: `gradient.colors = [[1, 0, 0, 1], [0, 1, 0, 1]]`, `addPoint(0.5, [1, 0, 0, 1])`, and `PhysicsRayQueryParameters3D.create([1, 2, 3], [4, 5, 6])` all accept tuples.
|
|
129
129
|
- **Array-typed props** (`GodotArray`, e.g. `CodeEdit.lineLengthGuidelines`, `Font.fallbacks`) accept plain JS arrays directly — they become a Godot `Array`.
|
|
130
130
|
- **Sub-properties / Dash props**: Set individual vector/color components directly using dash syntax:
|
|
131
131
|
```tsx
|
|
@@ -176,7 +176,8 @@ Signal handlers are connected **before** value props are applied within a commit
|
|
|
176
176
|
Below is a complete interactive 3D physics scene with custom camera controls and physics bodies:
|
|
177
177
|
|
|
178
178
|
```tsx
|
|
179
|
-
import {
|
|
179
|
+
import { Engine, Key, MouseButton } from '@ringozz/godot';
|
|
180
|
+
import type { Color } from '@ringozz/godot';
|
|
180
181
|
import { BoxMesh } from '@ringozz/godot/BoxMesh';
|
|
181
182
|
import { BoxShape3D } from '@ringozz/godot/BoxShape3D';
|
|
182
183
|
import { Camera3D } from '@ringozz/godot/Camera3D';
|
|
@@ -201,7 +202,7 @@ const tree = Engine.getMainLoop() as SceneTree;
|
|
|
201
202
|
const root = tree.root;
|
|
202
203
|
|
|
203
204
|
function PhysBody({ color, shape, children, ...rest }: {
|
|
204
|
-
color: Color
|
|
205
|
+
color: Color;
|
|
205
206
|
shape: ReactElement;
|
|
206
207
|
} & ComponentProps<typeof RigidBody3D>) {
|
|
207
208
|
return (
|
|
@@ -224,8 +225,8 @@ export function App() {
|
|
|
224
225
|
|
|
225
226
|
useSignal(root.windowInput, (event) => {
|
|
226
227
|
if (event instanceof InputEventMouseMotion && isDragging.current) {
|
|
227
|
-
yaw.current -= event.relative
|
|
228
|
-
pitch.current = Math.max(-1.4, Math.min(1.4, pitch.current + event.relative
|
|
228
|
+
yaw.current -= event.relative[0] * 0.005;
|
|
229
|
+
pitch.current = Math.max(-1.4, Math.min(1.4, pitch.current + event.relative[1] * 0.005));
|
|
229
230
|
} else if (event instanceof InputEventMouseButton) {
|
|
230
231
|
if (event.buttonIndex === MouseButton.MOUSE_BUTTON_LEFT) {
|
|
231
232
|
isDragging.current = event.pressed;
|
|
@@ -241,8 +242,8 @@ export function App() {
|
|
|
241
242
|
const cx = zoom.current * Math.cos(pitch.current) * Math.sin(yaw.current);
|
|
242
243
|
const cy = zoom.current * Math.sin(pitch.current) + 1;
|
|
243
244
|
const cz = zoom.current * Math.cos(pitch.current) * Math.cos(yaw.current);
|
|
244
|
-
camera.position =
|
|
245
|
-
camera.lookAt(
|
|
245
|
+
camera.position = [cx, cy, cz];
|
|
246
|
+
camera.lookAt([0, 1, 0], [0, 1, 0]);
|
|
246
247
|
});
|
|
247
248
|
|
|
248
249
|
return (
|
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-585",
|
|
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-585",
|
|
27
27
|
"@types/react-reconciler": "^0.33.0",
|
|
28
28
|
"react-reconciler": "^0.33.0"
|
|
29
29
|
},
|
package/src/react-hooks.ts
CHANGED
|
@@ -6,10 +6,10 @@ import type { Signal } from '@ringozz/godot';
|
|
|
6
6
|
import type { Node } from '@ringozz/godot/Node';
|
|
7
7
|
import type { Object as Instance } from '@ringozz/godot/Object';
|
|
8
8
|
import { PropertyTweener } from '@ringozz/godot/PropertyTweener';
|
|
9
|
-
import type { GodotVar } from '@ringozz/godot/runtime';
|
|
10
9
|
import { Tween, type EaseType, type TransitionType } from '@ringozz/godot/Tween';
|
|
11
10
|
import type React from 'react';
|
|
12
11
|
import { useEffect, useMemo, useRef } from 'react';
|
|
12
|
+
import { toValueType, type GodotVar } from '@ringozz/godot/runtime';
|
|
13
13
|
|
|
14
14
|
// Tween/PropertyTweener are only type-used here; value-reference them so their
|
|
15
15
|
// `_R` class registration isn't tree-shaken (otherwise createTween's wrapper
|
|
@@ -47,7 +47,9 @@ export function useSignal<T extends (...args: any[]) => any>(signal: Signal<T>,
|
|
|
47
47
|
// identity diff drops them, so nothing restarts); changing deps creates a new
|
|
48
48
|
// tween (the previous one is killed).
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
// Value-type goals are named tuples on the JS side — flat ones are `number[]`,
|
|
51
|
+
// nested ones (Transform2D/Basis/Transform3D/Projection/AABB) are `number[][]`.
|
|
52
|
+
export type TweenValue = number | number[] | number[][] | GodotVar;
|
|
51
53
|
|
|
52
54
|
export interface TweenConfig {
|
|
53
55
|
/** Seconds (default 1). */
|
|
@@ -81,14 +83,10 @@ export interface TweenProps<T extends Record<string, TweenValue> = Record<string
|
|
|
81
83
|
|
|
82
84
|
export type Spring<T extends Record<string, TweenValue>> = { [K in keyof T]: T[K] };
|
|
83
85
|
|
|
84
|
-
// tweenProperty/set reject JS arrays, so array goals are reconstructed
|
|
85
|
-
// property's value type (
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const Ctor = (node.get(native) as any)?.constructor;
|
|
89
|
-
if (typeof Ctor === 'function') return new Ctor(...value);
|
|
90
|
-
return value;
|
|
91
|
-
}
|
|
86
|
+
// tweenProperty/set reject generic JS arrays, so array goals are reconstructed
|
|
87
|
+
// as the property's value type by `toValueType` (resolves the node's
|
|
88
|
+
// property type and returns a GodotVar carrier the `_C` tween path unwraps;
|
|
89
|
+
// non-array goals pass through unchanged).
|
|
92
90
|
|
|
93
91
|
function makeSpring<T extends Record<string, TweenValue>>(
|
|
94
92
|
tweenRef: { current: Tween | null },
|
package/src/react-types.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import type React from 'react';
|
|
6
6
|
import type { Object } from '@ringozz/godot/Object';
|
|
7
|
-
import type { Signal,
|
|
7
|
+
import type { Signal, GodotDictionary } from '@ringozz/godot';
|
|
8
8
|
import type { PackedScene } from '@ringozz/godot/PackedScene';
|
|
9
9
|
|
|
10
10
|
// Identity check sensitive to readonly (deferred-conditional trick; the
|
|
@@ -39,29 +39,27 @@ type SettableKey<T, K extends keyof T> =
|
|
|
39
39
|
|
|
40
40
|
// Container props accept plain JS forms: JSX values flow through
|
|
41
41
|
// `Instance.assign` (`napi_to_variant_typed`), which builds packed arrays,
|
|
42
|
-
// Godot `Array`s, and `Dictionary`s from JS arrays/objects.
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
// -> `(E | number[])[]` (instances or value-type tuples);
|
|
48
|
-
// `(E | number[])[]`; `GodotDictionary<K,V>` ->
|
|
42
|
+
// Godot `Array`s, and `Dictionary`s from JS arrays/objects. Every gen heap
|
|
43
|
+
// container implements `Iterable` and has `get(index: number)`; `get` recovers
|
|
44
|
+
// the concrete element type (packed class getters return it even though the
|
|
45
|
+
// class generic defaults to `any`), and `GodotDictionary` routes to
|
|
46
|
+
// `Record<string, V>` first. Scalar-packed -> `number[]`/`string[]`;
|
|
47
|
+
// element-packed -> `(E | number[])[]` (instances or value-type tuples);
|
|
48
|
+
// `GodotArray<E>` -> `(E | number[])[]`; `GodotDictionary<K,V>` ->
|
|
49
|
+
// `Record<string, V>`.
|
|
49
50
|
type ContainerForm<T> =
|
|
50
51
|
T extends GodotDictionary<any, infer V> ? Record<string, V>
|
|
51
52
|
: T extends { get(index: number): infer E } ? (E extends number | string ? E[] : (E | number[])[])
|
|
52
|
-
: T extends Iterable<infer E> ? E[]
|
|
53
53
|
: never;
|
|
54
54
|
|
|
55
55
|
// JSX props are settable instance members, typed as the reconciler's
|
|
56
|
-
// `Instance.assign` accepts them:
|
|
57
|
-
// (getter `Signal<CB>`, setter `CB | null`)
|
|
58
|
-
// containers accept plain JS arrays/objects.
|
|
59
|
-
// (homomorphic) so optional/readonly modifiers survive.
|
|
56
|
+
// `Instance.assign` accepts them: value types are plain tuples (the getter
|
|
57
|
+
// type IS the array form), signals (getter `Signal<CB>`, setter `CB | null`)
|
|
58
|
+
// take the callback directly, and containers accept plain JS arrays/objects.
|
|
59
|
+
// Maps over `keyof T` (homomorphic) so optional/readonly modifiers survive.
|
|
60
60
|
type Properties<T> = {
|
|
61
61
|
[K in keyof T as SettableKey<T, K>]:
|
|
62
|
-
T[K] extends
|
|
63
|
-
: T[K] extends Signal<infer CB> ? CB | null
|
|
64
|
-
: T[K] extends GodotDictionary<any, any> ? T[K] | ContainerForm<T[K]>
|
|
62
|
+
T[K] extends Signal<infer CB> ? CB | null
|
|
65
63
|
: T[K] extends Iterable<unknown> ? T[K] | ContainerForm<T[K]>
|
|
66
64
|
: T[K];
|
|
67
65
|
};
|