@needle-tools/engine 4.9.0-next.ce04b9f → 4.9.0
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/CHANGELOG.md +14 -0
- package/README.md +1 -1
- package/components.needle.json +1 -1
- package/dist/{needle-engine.bundle-a5_4RZjH.min.js → needle-engine.bundle-B1gr_nQ0.min.js} +131 -131
- package/dist/{needle-engine.bundle-qHhlH-u4.js → needle-engine.bundle-BikYBC35.js} +5931 -5863
- package/dist/{needle-engine.bundle-DHAUN6T_.umd.cjs → needle-engine.bundle-DrlDKOar.umd.cjs} +130 -130
- package/dist/needle-engine.js +2 -2
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/lib/engine-components/Component.d.ts +8 -5
- package/lib/engine-components/Component.js +8 -5
- package/lib/engine-components/Component.js.map +1 -1
- package/lib/engine-components/physics/Attractor.d.ts +12 -0
- package/lib/engine-components/physics/Attractor.js +14 -2
- package/lib/engine-components/physics/Attractor.js.map +1 -1
- package/lib/engine-components/web/Clickthrough.d.ts +1 -0
- package/lib/engine-components/web/Clickthrough.js +4 -2
- package/lib/engine-components/web/Clickthrough.js.map +1 -1
- package/lib/engine-components/web/CursorFollow.d.ts +9 -0
- package/lib/engine-components/web/CursorFollow.js +17 -0
- package/lib/engine-components/web/CursorFollow.js.map +1 -1
- package/lib/engine-components/web/ScrollFollow.d.ts +47 -3
- package/lib/engine-components/web/ScrollFollow.js +135 -17
- package/lib/engine-components/web/ScrollFollow.js.map +1 -1
- package/package.json +3 -3
- package/src/engine-components/Component.ts +8 -5
- package/src/engine-components/physics/Attractor.ts +14 -4
- package/src/engine-components/web/Clickthrough.ts +6 -2
- package/src/engine-components/web/CursorFollow.ts +17 -2
- package/src/engine-components/web/ScrollFollow.ts +154 -25
|
@@ -7,14 +7,28 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
import { serializable } from "../../engine/engine_serialization_decorator.js";
|
|
8
8
|
import { getTempVector } from "../../engine/engine_three_utils.js";
|
|
9
9
|
import { Behaviour } from "../Component.js";
|
|
10
|
+
/**
|
|
11
|
+
* The CursorFollow component makes the object follow the cursor (or touch) position on screen.
|
|
12
|
+
*/
|
|
10
13
|
export class CursorFollow extends Behaviour {
|
|
11
14
|
/**
|
|
12
15
|
* Damping for the movement, set to 0 for instant movement
|
|
13
16
|
* @default 0
|
|
14
17
|
*/
|
|
15
18
|
damping = 0;
|
|
19
|
+
/**
|
|
20
|
+
* If true, the initial distance to the camera is maintained when following the cursor.
|
|
21
|
+
* @default true
|
|
22
|
+
*/
|
|
23
|
+
keepDistance = true;
|
|
24
|
+
awake() {
|
|
25
|
+
this._distance = -1;
|
|
26
|
+
}
|
|
16
27
|
_distance = -1;
|
|
17
28
|
updateDistance() {
|
|
29
|
+
if (this.keepDistance && this._distance !== -1) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
18
32
|
this._distance = this.gameObject.worldPosition.distanceTo(this.context.mainCamera.worldPosition);
|
|
19
33
|
}
|
|
20
34
|
/** @internal */
|
|
@@ -43,4 +57,7 @@ export class CursorFollow extends Behaviour {
|
|
|
43
57
|
__decorate([
|
|
44
58
|
serializable()
|
|
45
59
|
], CursorFollow.prototype, "damping", void 0);
|
|
60
|
+
__decorate([
|
|
61
|
+
serializable()
|
|
62
|
+
], CursorFollow.prototype, "keepDistance", void 0);
|
|
46
63
|
//# sourceMappingURL=CursorFollow.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CursorFollow.js","sourceRoot":"","sources":["../../../src/engine-components/web/CursorFollow.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gDAAgD,CAAC;AAC9E,OAAO,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"CursorFollow.js","sourceRoot":"","sources":["../../../src/engine-components/web/CursorFollow.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gDAAgD,CAAC;AAC9E,OAAO,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;GAEG;AACH,MAAM,OAAO,YAAa,SAAQ,SAAS;IAEvC;;;OAGG;IAEH,OAAO,GAAW,CAAC,CAAC;IAEpB;;;OAGG;IAEH,YAAY,GAAY,IAAI,CAAC;IAE7B,KAAK;QACD,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;IACxB,CAAC;IAEO,SAAS,GAAW,CAAC,CAAC,CAAC;IAE/B,cAAc;QACV,IAAI,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,SAAS,KAAK,CAAC,CAAC,EAAE;YAC5C,OAAO;SACV;QACD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;IACrG,CAAC;IAED,gBAAgB;IAChB,MAAM;QACF,8DAA8D;QAC9D,IAAI,CAAC,cAAc,EAAE,CAAC;QAEtB,yEAAyE;QACzE,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC;QAClD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC;QACvC,MAAM,cAAc,GAAG,MAAM,CAAC,aAAa,CAAC;QAE5C,iDAAiD;QACjD,MAAM,YAAY,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAC5E,YAAY,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,SAAS,EAAE,CAAC;QAE7C,oDAAoD;QACpD,MAAM,WAAW,GAAG,YAAY,CAAC,cAAc,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QACpF,IAAI,IAAI,CAAC,OAAO,GAAG,CAAC,EAAE;YAClB,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC;YAC1C,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;YAClE,IAAI,CAAC,UAAU,CAAC,aAAa,GAAG,GAAG,CAAC;SACvC;aACI;YACD,IAAI,CAAC,UAAU,CAAC,aAAa,GAAG,WAAW,CAAC;SAC/C;IAEL,CAAC;CAEJ;AAjDG;IADC,YAAY,EAAE;6CACK;AAOpB;IADC,YAAY,EAAE;kDACc"}
|
|
@@ -1,7 +1,32 @@
|
|
|
1
1
|
import { Behaviour } from "../Component.js";
|
|
2
|
+
import { EventList } from "../EventList.js";
|
|
3
|
+
type ScrollFollowEvent = {
|
|
4
|
+
/** Event type */
|
|
5
|
+
type: "change";
|
|
6
|
+
/** Current scroll value */
|
|
7
|
+
value: number;
|
|
8
|
+
/** ScrollFollow component that raised the event */
|
|
9
|
+
component: ScrollFollow;
|
|
10
|
+
/** Call to prevent invocation of default (e.g. updating targets) */
|
|
11
|
+
preventDefault: () => void;
|
|
12
|
+
defaultPrevented: boolean;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* The ScrollFollow component allows you to link the scroll position of the page (or a specific element) to one or more target objects.
|
|
16
|
+
* This can be used to create scroll-based animations, audio playback, or other effects. For example you can link the scroll position to a timeline (PlayableDirector) to create scroll-based storytelling effects or to an Animator component to change the animation state based on scroll.
|
|
17
|
+
*
|
|
18
|
+
* @link Example at https://scrollytelling-2-z23hmxby7c6x-u30ld.needle.run/
|
|
19
|
+
*/
|
|
2
20
|
export declare class ScrollFollow extends Behaviour {
|
|
3
21
|
/**
|
|
4
22
|
* Target object(s) to follow the scroll position of the page. If null, the main camera is used.
|
|
23
|
+
*
|
|
24
|
+
* Supported target types:
|
|
25
|
+
* - PlayableDirector (timeline), the scroll position will be mapped to the timeline time
|
|
26
|
+
* - Animator, the scroll position will be set to a float parameter named "scroll"
|
|
27
|
+
* - Animation, the scroll position will be mapped to the animation time
|
|
28
|
+
* - AudioSource, the scroll position will be mapped to the audio time
|
|
29
|
+
* - Any object with a `scroll` property (number or function)
|
|
5
30
|
*/
|
|
6
31
|
target: object[] | object | null;
|
|
7
32
|
/**
|
|
@@ -9,19 +34,38 @@ export declare class ScrollFollow extends Behaviour {
|
|
|
9
34
|
* @default 0
|
|
10
35
|
*/
|
|
11
36
|
damping: number;
|
|
12
|
-
|
|
37
|
+
/**
|
|
38
|
+
* If true, the scroll value will be inverted (e.g. scrolling down will result in a value of 0)
|
|
39
|
+
* @default false
|
|
40
|
+
*/
|
|
41
|
+
invert: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* If set, the scroll position will be read from the specified element instead of the window.
|
|
44
|
+
* Use a CSS selector to specify the element, e.g. `#my-scrollable-div` or `.scroll-container`.
|
|
45
|
+
* @default null
|
|
46
|
+
*/
|
|
47
|
+
htmlSelector: string | null;
|
|
48
|
+
private mode;
|
|
49
|
+
/**
|
|
50
|
+
* Event fired when the scroll position changes
|
|
51
|
+
*/
|
|
52
|
+
changed: EventList<ScrollFollowEvent>;
|
|
13
53
|
/**
|
|
14
54
|
* Current scroll value in "pages" (0 = top of page, 1 = bottom of page)
|
|
15
55
|
*/
|
|
16
56
|
get currentValue(): number;
|
|
17
57
|
private current_value;
|
|
18
58
|
private target_value;
|
|
59
|
+
private applied_value;
|
|
19
60
|
/** @internal */
|
|
20
61
|
onEnable(): void;
|
|
21
62
|
/** @internal */
|
|
22
63
|
onDisable(): void;
|
|
23
64
|
/** @internal */
|
|
24
65
|
lateUpdate(): void;
|
|
25
|
-
private
|
|
26
|
-
private
|
|
66
|
+
private _lastSelectorValue;
|
|
67
|
+
private _lastSelectorElement;
|
|
68
|
+
private updateCurrentScrollValue;
|
|
69
|
+
private static applyScroll;
|
|
27
70
|
}
|
|
71
|
+
export {};
|
|
@@ -5,13 +5,33 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
7
|
import { Object3D } from "three";
|
|
8
|
-
import { serializable } from "../../engine/engine_serialization.js";
|
|
9
8
|
import { Mathf } from "../../engine/engine_math.js";
|
|
9
|
+
import { serializable } from "../../engine/engine_serialization.js";
|
|
10
|
+
import { getBoundingBox } from "../../engine/engine_three_utils.js";
|
|
11
|
+
import { Animation } from "../Animation.js";
|
|
12
|
+
import { Animator } from "../Animator.js";
|
|
13
|
+
import { AudioSource } from "../AudioSource.js";
|
|
10
14
|
import { Behaviour } from "../Component.js";
|
|
11
|
-
import {
|
|
15
|
+
import { EventList } from "../EventList.js";
|
|
16
|
+
import { Light } from "../Light.js";
|
|
17
|
+
import { SplineWalker } from "../splines/SplineWalker.js";
|
|
18
|
+
import { PlayableDirector } from "../timeline/PlayableDirector.js";
|
|
19
|
+
/**
|
|
20
|
+
* The ScrollFollow component allows you to link the scroll position of the page (or a specific element) to one or more target objects.
|
|
21
|
+
* This can be used to create scroll-based animations, audio playback, or other effects. For example you can link the scroll position to a timeline (PlayableDirector) to create scroll-based storytelling effects or to an Animator component to change the animation state based on scroll.
|
|
22
|
+
*
|
|
23
|
+
* @link Example at https://scrollytelling-2-z23hmxby7c6x-u30ld.needle.run/
|
|
24
|
+
*/
|
|
12
25
|
export class ScrollFollow extends Behaviour {
|
|
13
26
|
/**
|
|
14
27
|
* Target object(s) to follow the scroll position of the page. If null, the main camera is used.
|
|
28
|
+
*
|
|
29
|
+
* Supported target types:
|
|
30
|
+
* - PlayableDirector (timeline), the scroll position will be mapped to the timeline time
|
|
31
|
+
* - Animator, the scroll position will be set to a float parameter named "scroll"
|
|
32
|
+
* - Animation, the scroll position will be mapped to the animation time
|
|
33
|
+
* - AudioSource, the scroll position will be mapped to the audio time
|
|
34
|
+
* - Any object with a `scroll` property (number or function)
|
|
15
35
|
*/
|
|
16
36
|
target = null;
|
|
17
37
|
/**
|
|
@@ -19,7 +39,22 @@ export class ScrollFollow extends Behaviour {
|
|
|
19
39
|
* @default 0
|
|
20
40
|
*/
|
|
21
41
|
damping = 0;
|
|
42
|
+
/**
|
|
43
|
+
* If true, the scroll value will be inverted (e.g. scrolling down will result in a value of 0)
|
|
44
|
+
* @default false
|
|
45
|
+
*/
|
|
46
|
+
invert = false;
|
|
47
|
+
/**
|
|
48
|
+
* If set, the scroll position will be read from the specified element instead of the window.
|
|
49
|
+
* Use a CSS selector to specify the element, e.g. `#my-scrollable-div` or `.scroll-container`.
|
|
50
|
+
* @default null
|
|
51
|
+
*/
|
|
52
|
+
htmlSelector = null;
|
|
22
53
|
mode = "window";
|
|
54
|
+
/**
|
|
55
|
+
* Event fired when the scroll position changes
|
|
56
|
+
*/
|
|
57
|
+
changed = new EventList();
|
|
23
58
|
/**
|
|
24
59
|
* Current scroll value in "pages" (0 = top of page, 1 = bottom of page)
|
|
25
60
|
*/
|
|
@@ -28,48 +63,122 @@ export class ScrollFollow extends Behaviour {
|
|
|
28
63
|
}
|
|
29
64
|
current_value = 0;
|
|
30
65
|
target_value = 0;
|
|
66
|
+
applied_value = -1;
|
|
31
67
|
/** @internal */
|
|
32
68
|
onEnable() {
|
|
33
|
-
window.addEventListener("wheel", this.
|
|
69
|
+
window.addEventListener("wheel", this.updateCurrentScrollValue, { passive: true });
|
|
70
|
+
this.applied_value = -1;
|
|
34
71
|
}
|
|
35
72
|
/** @internal */
|
|
36
73
|
onDisable() {
|
|
37
|
-
window.removeEventListener("wheel", this.
|
|
74
|
+
window.removeEventListener("wheel", this.updateCurrentScrollValue);
|
|
38
75
|
}
|
|
39
76
|
/** @internal */
|
|
40
77
|
lateUpdate() {
|
|
78
|
+
this.updateCurrentScrollValue();
|
|
41
79
|
// apply damping if any
|
|
42
80
|
if (this.damping > 0) {
|
|
43
81
|
this.current_value = Mathf.lerp(this.current_value, this.target_value, this.context.time.deltaTime / this.damping);
|
|
44
82
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
this.target.forEach(t => t && this.applyScroll(t));
|
|
83
|
+
else {
|
|
84
|
+
this.current_value = this.target_value;
|
|
48
85
|
}
|
|
49
|
-
|
|
50
|
-
this.
|
|
86
|
+
if (this.current_value !== this.applied_value) {
|
|
87
|
+
this.applied_value = this.current_value;
|
|
88
|
+
let defaultPrevented = false;
|
|
89
|
+
if (this.changed.listenerCount > 0) {
|
|
90
|
+
// fire change event
|
|
91
|
+
const event = {
|
|
92
|
+
type: "change",
|
|
93
|
+
value: this.current_value,
|
|
94
|
+
component: this,
|
|
95
|
+
preventDefault: () => { event.defaultPrevented = true; },
|
|
96
|
+
defaultPrevented: false,
|
|
97
|
+
};
|
|
98
|
+
this.changed.invoke(event);
|
|
99
|
+
defaultPrevented = event.defaultPrevented;
|
|
100
|
+
}
|
|
101
|
+
// if not prevented apply scroll
|
|
102
|
+
if (!defaultPrevented) {
|
|
103
|
+
const value = this.invert ? 1 - this.current_value : this.current_value;
|
|
104
|
+
// apply scroll to target(s)
|
|
105
|
+
if (Array.isArray(this.target)) {
|
|
106
|
+
this.target.forEach(t => t && ScrollFollow.applyScroll(t, value));
|
|
107
|
+
}
|
|
108
|
+
else if (this.target) {
|
|
109
|
+
ScrollFollow.applyScroll(this.target, value);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
51
112
|
}
|
|
52
113
|
}
|
|
53
|
-
|
|
114
|
+
_lastSelectorValue = null;
|
|
115
|
+
_lastSelectorElement = null;
|
|
116
|
+
updateCurrentScrollValue = () => {
|
|
54
117
|
switch (this.mode) {
|
|
55
118
|
case "window":
|
|
56
|
-
|
|
119
|
+
if (this.htmlSelector?.length) {
|
|
120
|
+
if (this.htmlSelector !== this._lastSelectorValue) {
|
|
121
|
+
this._lastSelectorElement = document.querySelector(this.htmlSelector);
|
|
122
|
+
this._lastSelectorValue = this.htmlSelector;
|
|
123
|
+
}
|
|
124
|
+
if (this._lastSelectorElement) {
|
|
125
|
+
const rect = this._lastSelectorElement.getBoundingClientRect();
|
|
126
|
+
this.target_value = -rect.top / (rect.height - window.innerHeight);
|
|
127
|
+
if (isNaN(this.target_value) || !isFinite(this.target_value))
|
|
128
|
+
this.target_value = 0;
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
this.target_value = window.scrollY / (document.body.scrollHeight - window.innerHeight);
|
|
134
|
+
}
|
|
57
135
|
if (isNaN(this.target_value) || !isFinite(this.target_value))
|
|
58
136
|
this.target_value = 0;
|
|
59
137
|
break;
|
|
60
138
|
}
|
|
61
|
-
if (this.damping <= 0) {
|
|
62
|
-
this.current_value = this.target_value;
|
|
63
|
-
}
|
|
64
139
|
};
|
|
65
|
-
applyScroll(target) {
|
|
140
|
+
static applyScroll(target, value) {
|
|
141
|
+
if (!target)
|
|
142
|
+
return;
|
|
66
143
|
if (target instanceof PlayableDirector) {
|
|
67
|
-
target.time =
|
|
144
|
+
target.time = value * target.duration;
|
|
68
145
|
if (!target.isPlaying)
|
|
69
146
|
target.evaluate();
|
|
70
147
|
}
|
|
148
|
+
else if (target instanceof Animator) {
|
|
149
|
+
target.setFloat("scroll", value);
|
|
150
|
+
}
|
|
71
151
|
else if (target instanceof Animation) {
|
|
72
|
-
target.time =
|
|
152
|
+
target.time = value * target.duration;
|
|
153
|
+
}
|
|
154
|
+
else if (target instanceof AudioSource) {
|
|
155
|
+
if (!target.duration)
|
|
156
|
+
return;
|
|
157
|
+
target.time = value * target.duration;
|
|
158
|
+
}
|
|
159
|
+
else if (target instanceof SplineWalker) {
|
|
160
|
+
target.position01 = value;
|
|
161
|
+
}
|
|
162
|
+
else if (target instanceof Light) {
|
|
163
|
+
target.intensity = value;
|
|
164
|
+
}
|
|
165
|
+
else if (target instanceof Object3D) {
|
|
166
|
+
// When objects are assigned they're expected to move vertically based on scroll
|
|
167
|
+
if (target["needle:scrollbounds"] === undefined) {
|
|
168
|
+
target["needle:scrollbounds"] = getBoundingBox(target) || null;
|
|
169
|
+
}
|
|
170
|
+
const bounds = target["needle:scrollbounds"];
|
|
171
|
+
if (bounds) {
|
|
172
|
+
target.position.y = -bounds.min.y - value * (bounds.max.y - bounds.min.y);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
else if ("scroll" in target) {
|
|
176
|
+
if (typeof target.scroll === "number") {
|
|
177
|
+
target.scroll = value;
|
|
178
|
+
}
|
|
179
|
+
else if (typeof target.scroll === "function") {
|
|
180
|
+
target.scroll(value);
|
|
181
|
+
}
|
|
73
182
|
}
|
|
74
183
|
}
|
|
75
184
|
}
|
|
@@ -79,7 +188,16 @@ __decorate([
|
|
|
79
188
|
__decorate([
|
|
80
189
|
serializable()
|
|
81
190
|
], ScrollFollow.prototype, "damping", void 0);
|
|
191
|
+
__decorate([
|
|
192
|
+
serializable()
|
|
193
|
+
], ScrollFollow.prototype, "invert", void 0);
|
|
194
|
+
__decorate([
|
|
195
|
+
serializable()
|
|
196
|
+
], ScrollFollow.prototype, "htmlSelector", void 0);
|
|
82
197
|
__decorate([
|
|
83
198
|
serializable()
|
|
84
199
|
], ScrollFollow.prototype, "mode", void 0);
|
|
200
|
+
__decorate([
|
|
201
|
+
serializable(EventList)
|
|
202
|
+
], ScrollFollow.prototype, "changed", void 0);
|
|
85
203
|
//# sourceMappingURL=ScrollFollow.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScrollFollow.js","sourceRoot":"","sources":["../../../src/engine-components/web/ScrollFollow.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"ScrollFollow.js","sourceRoot":"","sources":["../../../src/engine-components/web/ScrollFollow.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@needle-tools/engine",
|
|
3
|
-
"version": "4.9.0
|
|
3
|
+
"version": "4.9.0",
|
|
4
4
|
"description": "Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.",
|
|
5
5
|
"main": "dist/needle-engine.min.js",
|
|
6
6
|
"exports": {
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
],
|
|
94
94
|
"dependencies": {
|
|
95
95
|
"@dimforge/rapier3d-compat": "^0.19.0",
|
|
96
|
-
"@needle-tools/gltf-progressive": "3.3.
|
|
96
|
+
"@needle-tools/gltf-progressive": "3.3.4",
|
|
97
97
|
"@needle-tools/three-animation-pointer": "1.0.2",
|
|
98
98
|
"@webxr-input-profiles/motion-controllers": "^1.0.0",
|
|
99
99
|
"flatbuffers": "2.0.4",
|
|
@@ -168,4 +168,4 @@
|
|
|
168
168
|
"module": "lib/needle-engine.js",
|
|
169
169
|
"typings": "lib/needle-engine.d.ts",
|
|
170
170
|
"types": "lib/needle-engine.d.ts"
|
|
171
|
-
}
|
|
171
|
+
}
|
|
@@ -543,22 +543,25 @@ export abstract class GameObject extends Object3D implements Object3D, IGameObje
|
|
|
543
543
|
|
|
544
544
|
|
|
545
545
|
/**
|
|
546
|
-
* Needle Engine component
|
|
546
|
+
* Needle Engine component's are the main building blocks of the Needle Engine.
|
|
547
547
|
* Derive from {@link Behaviour} to implement your own using the provided lifecycle methods.
|
|
548
548
|
* Components can be added to any {@link Object3D} using {@link addComponent} or {@link GameObject.addComponent}.
|
|
549
549
|
*
|
|
550
|
-
*
|
|
550
|
+
* **Component lifecycle event methods:**
|
|
551
|
+
* {@link awake}, {@link start}, {@link onEnable}, {@link onDisable}, {@link onDestroy}, {@link earlyUpdate}, {@link update}, {@link lateUpdate}, {@link onBeforeRender}, {@link onAfterRender}.
|
|
551
552
|
*
|
|
552
|
-
* XR
|
|
553
|
+
* **XR event methods:**
|
|
554
|
+
* {@link onEnterXR}, {@link onLeaveXR}, {@link onUpdateXR}, {@link onXRControllerAdded} and {@link onXRControllerRemoved}.
|
|
553
555
|
*
|
|
554
|
-
*
|
|
556
|
+
* **Input event methods:**
|
|
557
|
+
* {@link onPointerDown}, {@link onPointerUp}, {@link onPointerEnter}, {@link onPointerExit} and {@link onPointerMove}.
|
|
555
558
|
*
|
|
556
559
|
* @example
|
|
557
560
|
* ```typescript
|
|
558
561
|
* import { Behaviour } from "@needle-tools/engine";
|
|
559
562
|
* export class MyComponent extends Behaviour {
|
|
560
563
|
* start() {
|
|
561
|
-
* console.log("Hello World");
|
|
564
|
+
* console.log("Hello World", this.gameObject.name);
|
|
562
565
|
* }
|
|
563
566
|
* update() {
|
|
564
567
|
* console.log("Frame", this.context.time.frame);
|
|
@@ -1,9 +1,20 @@
|
|
|
1
|
+
import { serializable } from "../../engine/engine_serialization_decorator.js";
|
|
1
2
|
import { Behaviour } from "../Component.js";
|
|
2
3
|
import { Rigidbody } from "../RigidBody.js";
|
|
3
|
-
import { serializable } from "../../engine/engine_serialization_decorator.js";
|
|
4
|
-
|
|
5
4
|
|
|
6
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Used to attract Rigidbodies towards the position of this component.
|
|
8
|
+
* Add Rigidbodies to the `targets` array to have them be attracted.
|
|
9
|
+
* You can use negative strength values to create a repulsion effect.
|
|
10
|
+
*
|
|
11
|
+
* @example Attractor component attracting a Rigidbody
|
|
12
|
+
* ```ts
|
|
13
|
+
* const attractor = object.addComponent(Attractor);
|
|
14
|
+
* attractor.strength = 5; // positive value to attract
|
|
15
|
+
* attractor.radius = 10; // only attract within 10 units
|
|
16
|
+
* attractor.targets.push(rigidbody); // add the Rigidbody to be attracted
|
|
17
|
+
*/
|
|
7
18
|
export class Attractor extends Behaviour {
|
|
8
19
|
|
|
9
20
|
@serializable()
|
|
@@ -15,11 +26,10 @@ export class Attractor extends Behaviour {
|
|
|
15
26
|
@serializable(Rigidbody)
|
|
16
27
|
targets: Rigidbody[] = [];
|
|
17
28
|
|
|
18
|
-
|
|
19
29
|
update() {
|
|
20
30
|
const wp = this.gameObject.worldPosition;
|
|
21
31
|
const factor = -this.strength * this.context.time.deltaTime;
|
|
22
|
-
this.targets
|
|
32
|
+
this.targets?.forEach(t => {
|
|
23
33
|
if(!t) return;
|
|
24
34
|
const dir = t.gameObject.worldPosition.sub(wp);
|
|
25
35
|
const length = dir.length();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { NEPointerEvent } from "../../engine/engine_input.js";
|
|
2
|
-
import { Behaviour } from "../Component.js";
|
|
3
2
|
import { onStart } from "../../engine/engine_lifecycle_api.js";
|
|
3
|
+
import { Behaviour } from "../Component.js";
|
|
4
4
|
|
|
5
5
|
// Automatically add ClickThrough component if "clickthrough" attribute is present on the needle-engine element
|
|
6
6
|
onStart(ctx => {
|
|
@@ -23,6 +23,9 @@ onStart(ctx => {
|
|
|
23
23
|
* @link Example https://stackblitz.com/~/github.com/needle-engine/sample-3d-over-html
|
|
24
24
|
*/
|
|
25
25
|
export class ClickThrough extends Behaviour {
|
|
26
|
+
|
|
27
|
+
private _previousPointerEvents: string = 'all';
|
|
28
|
+
|
|
26
29
|
onEnable() {
|
|
27
30
|
// Register for pointer down and pointer move event
|
|
28
31
|
this.context.input.addEventListener('pointerdown', this.onPointerEvent);
|
|
@@ -30,12 +33,13 @@ export class ClickThrough extends Behaviour {
|
|
|
30
33
|
queue: 100,
|
|
31
34
|
});
|
|
32
35
|
window.addEventListener("touchend", this.onTouchEnd, { passive: true });
|
|
36
|
+
this._previousPointerEvents = this.context.domElement.style.pointerEvents;
|
|
33
37
|
}
|
|
34
38
|
onDisable() {
|
|
35
39
|
this.context.input.removeEventListener('pointerdown', this.onPointerEvent);
|
|
36
40
|
this.context.input.removeEventListener('pointermove', this.onPointerEvent);
|
|
37
41
|
window.removeEventListener("touchend", this.onTouchEnd);
|
|
38
|
-
this.context.domElement.style.pointerEvents =
|
|
42
|
+
this.context.domElement.style.pointerEvents = this._previousPointerEvents;
|
|
39
43
|
}
|
|
40
44
|
onPointerEnter() {
|
|
41
45
|
/** do nothing, necessary to raycast children */
|
|
@@ -2,7 +2,9 @@ import { serializable } from "../../engine/engine_serialization_decorator.js";
|
|
|
2
2
|
import { getTempVector } from "../../engine/engine_three_utils.js";
|
|
3
3
|
import { Behaviour } from "../Component.js";
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* The CursorFollow component makes the object follow the cursor (or touch) position on screen.
|
|
7
|
+
*/
|
|
6
8
|
export class CursorFollow extends Behaviour {
|
|
7
9
|
|
|
8
10
|
/**
|
|
@@ -12,13 +14,26 @@ export class CursorFollow extends Behaviour {
|
|
|
12
14
|
@serializable()
|
|
13
15
|
damping: number = 0;
|
|
14
16
|
|
|
17
|
+
/**
|
|
18
|
+
* If true, the initial distance to the camera is maintained when following the cursor.
|
|
19
|
+
* @default true
|
|
20
|
+
*/
|
|
21
|
+
@serializable()
|
|
22
|
+
keepDistance: boolean = true;
|
|
23
|
+
|
|
24
|
+
awake() {
|
|
25
|
+
this._distance = -1;
|
|
26
|
+
}
|
|
15
27
|
|
|
16
28
|
private _distance: number = -1;
|
|
29
|
+
|
|
17
30
|
updateDistance() {
|
|
31
|
+
if (this.keepDistance && this._distance !== -1) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
18
34
|
this._distance = this.gameObject.worldPosition.distanceTo(this.context.mainCamera.worldPosition);
|
|
19
35
|
}
|
|
20
36
|
|
|
21
|
-
|
|
22
37
|
/** @internal */
|
|
23
38
|
update() {
|
|
24
39
|
// continuously update distance in case camera or object moves
|