Needle Engine

Changes between version 3.47.5-beta.1 and 3.47.6-beta
Files changed (11) hide show
  1. plugins/vite/dependency-watcher.js +1 -1
  2. src/engine-components/AudioSource.ts +8 -0
  3. src/engine-components/Camera.ts +4 -0
  4. src/engine-components/DragControls.ts +53 -20
  5. src/engine/engine_create_objects.ts +1 -1
  6. src/engine/engine_math.ts +11 -3
  7. src/engine/engine_scenetools.ts +2 -2
  8. src/engine/engine_three_utils.ts +15 -6
  9. src/engine/engine_utils_screenshot.ts +5 -0
  10. src/engine-components/OrbitControls.ts +9 -2
  11. src/engine-components/SpriteRenderer.ts +119 -12
plugins/vite/dependency-watcher.js CHANGED
@@ -217,7 +217,7 @@
217
217
  // }
218
218
 
219
219
  // check if server is running
220
- if (server.httpServer.listening) {
220
+ if (server.httpServer?.listening) {
221
221
  server.restart();
222
222
  }
223
223
  isRunningRestart = false;
src/engine-components/AudioSource.ts CHANGED
@@ -182,6 +182,14 @@
182
182
  private _volume: number = 1;
183
183
 
184
184
  @serializable()
185
+ set pitch(val: number) {
186
+ if (this.sound) this.sound.setPlaybackRate(val);
187
+ }
188
+ get pitch(): number {
189
+ return this.sound ? this.sound.getPlaybackRate() : 1;
190
+ }
191
+
192
+ @serializable()
185
193
  rollOffMode: AudioRolloffMode = 0;
186
194
 
187
195
 
src/engine-components/Camera.ts CHANGED
@@ -15,6 +15,7 @@
15
15
 
16
16
  /** The ClearFlags enum is used to determine how the camera clears the background */
17
17
  export enum ClearFlags {
18
+ None = 0,
18
19
  /** Clear the background with a skybox */
19
20
  Skybox = 1,
20
21
  /** Clear the background with a solid color. The alpha channel of the color determines the transparency */
@@ -429,6 +430,9 @@
429
430
  console.debug(msg);
430
431
  }
431
432
  switch (this._clearFlags) {
433
+ case ClearFlags.None:
434
+ return;
435
+
432
436
  case ClearFlags.Skybox:
433
437
  if (Camera.backgroundShouldBeTransparent(this.context)) {
434
438
  if (!this.ARBackgroundAlpha || this.ARBackgroundAlpha < 0.001) {
src/engine-components/DragControls.ts CHANGED
@@ -98,18 +98,47 @@
98
98
  /** When enabled, draws a line from the dragged object downwards to the next raycast hit. */
99
99
  @serializable()
100
100
  public showGizmo: boolean = false;
101
-
101
+
102
102
  /** The currently dragged object (if any) */
103
103
  get draggedObject() {
104
104
  return this._targetObject;
105
105
  }
106
106
 
107
+ /**
108
+ * Use to update the object that is being dragged by the DragControls
109
+ */
110
+ setTargetObject(obj: Object3D | null) {
111
+ this._targetObject = obj;
112
+ for (const handler of this._dragHandlers.values()) {
113
+ handler.setTargetObject(obj);
114
+ }
115
+
116
+ // If the object was kinematic we want to reset it
117
+ const wasKinematicKey = "_rigidbody-was-kinematic";
118
+ if (this._rigidbody?.[wasKinematicKey] === false) {
119
+ this._rigidbody.isKinematic = false;
120
+ }
121
+
122
+ this._rigidbody = null;
123
+ // If we have a object that is being dragged we want to get the Rigidbody component
124
+ // and we set kinematic to false while it's being dragged
125
+ if (obj) {
126
+ this._rigidbody = GameObject.getComponentInChildren(obj, Rigidbody);
127
+ if (this._rigidbody?.isKinematic === false) {
128
+ this._rigidbody.isKinematic = true;
129
+ this._rigidbody[wasKinematicKey] = false;
130
+ }
131
+ }
132
+
133
+ }
134
+ private _rigidbody: Rigidbody | null = null;
135
+
107
136
  // future:
108
137
  // constraints?
109
138
 
110
139
 
111
140
  /** The object to be dragged – we pass this to handlers when they are created */
112
- private _targetObject: GameObject | null = null;
141
+ private _targetObject: Object3D | null = null;
113
142
  private _dragHelper: LegacyDragVisualsHelper | null = null;
114
143
  private static lastHovered: Object3D;
115
144
  private _draggingRigidbodies: Rigidbody[] = [];
@@ -121,13 +150,6 @@
121
150
  private _isDragging: boolean = false;
122
151
  private _didDrag: boolean = false;
123
152
 
124
- setTargetObject(obj: Object3D | null) {
125
- this._targetObject = obj as GameObject;
126
- for (const handler of this._dragHandlers.values()) {
127
- handler.setTargetObject(obj);
128
- }
129
- }
130
-
131
153
  /** @internal */
132
154
  awake() {
133
155
  // initialize all data that may be cloned incorrectly otherwise
@@ -202,10 +224,13 @@
202
224
  this._totalMovement.set(0, 0, 0);
203
225
  this._potentialDragStartEvt = args;
204
226
  }
227
+ if (!this._targetObject) {
228
+ this.setTargetObject(this.gameObject);
229
+ }
205
230
 
206
231
  DragControls._active += 1;
207
232
 
208
- const newDragHandler = new DragPointerHandler(this, this._targetObject || this.gameObject);
233
+ const newDragHandler = new DragPointerHandler(this, this._targetObject!);
209
234
  this._dragHandlers.set(args.event.space, newDragHandler);
210
235
 
211
236
  newDragHandler.onDragStart(args);
@@ -214,9 +239,8 @@
214
239
  const iterator = this._dragHandlers.values();
215
240
  const a = iterator.next().value;
216
241
  const b = iterator.next().value;
217
- const mtHandler = new MultiTouchDragHandler(this, this._targetObject || this.gameObject, a, b);
242
+ const mtHandler = new MultiTouchDragHandler(this, this._targetObject!, a, b);
218
243
  this._dragHandlers.set(this.gameObject, mtHandler);
219
-
220
244
  mtHandler.onDragStart(args);
221
245
  }
222
246
 
@@ -231,9 +255,7 @@
231
255
 
232
256
  /** @internal */
233
257
  onPointerUp(args: PointerEventData) {
234
-
235
258
  if (debug) Gizmos.DrawLabel(args.point ?? this.gameObject.worldPosition, "POINTERUP:" + args.pointerId + ", " + args.button, .03, 3);
236
-
237
259
  if (!this.allowEdit(this.gameObject)) return;
238
260
  if (args.button !== 0) return;
239
261
  this._potentialDragStartEvt = null;
@@ -250,6 +272,8 @@
250
272
  if (DragControls._active > 0)
251
273
  DragControls._active -= 1;
252
274
 
275
+ this.setTargetObject(null);
276
+
253
277
  if (handler.onDragEnd) handler.onDragEnd(args);
254
278
  this._dragHandlers.delete(args.event.space);
255
279
 
@@ -388,7 +412,7 @@
388
412
 
389
413
  private context: Context;
390
414
  private settings: DragControls;
391
- private gameObject: GameObject;
415
+ private gameObject: Object3D;
392
416
  private _handlerAAttachmentPoint: Vector3 = new Vector3();
393
417
  private _handlerBAttachmentPoint: Vector3 = new Vector3();
394
418
 
@@ -397,7 +421,7 @@
397
421
  private _deviceMode!: XRTargetRayMode | "transient-pointer";
398
422
  private _followObjectStartWorldQuaternion: Quaternion = new Quaternion();
399
423
 
400
- constructor(dragControls: DragControls, gameObject: GameObject, pointerA: DragPointerHandler, pointerB: DragPointerHandler) {
424
+ constructor(dragControls: DragControls, gameObject: Object3D, pointerA: DragPointerHandler, pointerB: DragPointerHandler) {
401
425
  this.context = dragControls.context;
402
426
  this.settings = dragControls;
403
427
  this.gameObject = gameObject;
@@ -633,7 +657,7 @@
633
657
  get hitPointInLocalSpace(): Vector3 { return this._hitPointInLocalSpace; }
634
658
 
635
659
  private context: Context;
636
- private gameObject: GameObject;
660
+ private gameObject: Object3D | null;
637
661
  private settings: DragControls;
638
662
  private _lastRig: IGameObject | undefined = undefined;
639
663
 
@@ -667,10 +691,10 @@
667
691
 
668
692
  /** Allows overriding which object is dragged while a drag is already ongoing. Used for example by Duplicatable */
669
693
  setTargetObject(obj: Object3D | null) {
670
- this.gameObject = obj as GameObject;
694
+ this.gameObject = obj;
671
695
  }
672
696
 
673
- constructor(dragControls: DragControls, gameObject: GameObject) {
697
+ constructor(dragControls: DragControls, gameObject: Object3D) {
674
698
  this.settings = dragControls;
675
699
  this.context = dragControls.context;
676
700
  this.gameObject = gameObject;
@@ -682,6 +706,10 @@
682
706
  console.warn("Error: space follow object doesn't have parent but recenter() is called. This is likely a bug");
683
707
  return;
684
708
  }
709
+ if (!this.gameObject) {
710
+ console.warn("Error: space follow object doesn't have a gameObject");
711
+ return;
712
+ }
685
713
 
686
714
  const p = this._followObject.parent as GameObject;
687
715
 
@@ -721,6 +749,10 @@
721
749
  }
722
750
 
723
751
  onDragStart(args: PointerEventData) {
752
+ if (!this.gameObject) {
753
+ console.warn("Error: space follow object doesn't have a gameObject");
754
+ return;
755
+ }
724
756
 
725
757
  args.event.space.add(this._followObject);
726
758
 
@@ -1037,7 +1069,8 @@
1037
1069
  }
1038
1070
  }
1039
1071
  else if (didHaveSurfaceHitPointLastFrame) {
1040
- this.setPlaneViewAligned(this.gameObject.worldPosition, false)
1072
+ if (this.gameObject)
1073
+ this.setPlaneViewAligned(this.gameObject.worldPosition, false)
1041
1074
  }
1042
1075
  }
1043
1076
 
src/engine/engine_create_objects.ts CHANGED
@@ -148,7 +148,7 @@
148
148
  * @returns The created object
149
149
  */
150
150
  static createOccluder(type: PrimitiveTypeNames): Mesh {
151
- const occluderMaterial = new MeshStandardMaterial({ colorWrite: false, depthWrite: true, side: DoubleSide });
151
+ const occluderMaterial = new MeshBasicMaterial({ colorWrite: false, depthWrite: true, side: DoubleSide });
152
152
  return this.createPrimitive(type, { material: occluderMaterial });
153
153
  }
154
154
 
src/engine/engine_math.ts CHANGED
@@ -6,10 +6,18 @@
6
6
 
7
7
  class MathHelper {
8
8
 
9
- random(min?: number, max?: number): number {
10
- if (min !== undefined && max !== undefined) {
11
- return Math.random() * (max - min) + min;
9
+ random<T>(arr: Array<T>): T | null;
10
+ random(min?: number, max?: number): number;
11
+ random<T>(arrayOrMin?: number | Array<T>, max?: number): number | T | null {
12
+ if (Array.isArray(arrayOrMin)) {
13
+ if(arrayOrMin.length <= 0) return null;
14
+ return arrayOrMin[Math.floor(Math.random() * arrayOrMin.length)];
12
15
  }
16
+ else {
17
+ if (arrayOrMin !== undefined && max !== undefined) {
18
+ return Math.random() * (max - arrayOrMin) + arrayOrMin;
19
+ }
20
+ }
13
21
  return Math.random();
14
22
  }
15
23
 
src/engine/engine_scenetools.ts CHANGED
@@ -200,7 +200,7 @@
200
200
  }
201
201
 
202
202
  }, err => {
203
- console.error("failed loading " + path, err);
203
+ console.error("Loading asset at \"" + path + "\" failed\n", err);
204
204
  resolve(undefined);
205
205
  });
206
206
  }
@@ -262,7 +262,7 @@
262
262
  }, evt => {
263
263
  prog?.call(loader, evt);
264
264
  }, err => {
265
- console.error("failed loading " + url, err);
265
+ console.error("Loading asset at \"" + url + "\" failed\n", err);
266
266
  resolve(undefined);
267
267
  });
268
268
  }
src/engine/engine_three_utils.ts CHANGED
@@ -73,7 +73,7 @@
73
73
 
74
74
  /** Gets a temporary vector. If a vector is passed in it will be copied to the temporary vector
75
75
  * Temporary vectors are cached and reused internally. Don't store them!
76
- * @param vecOrX the vector to copy or the x value
76
+ * @param vec3 the vector to copy or the x value
77
77
  * @param y the y value
78
78
  * @param z the z value
79
79
  * @returns a temporary vector
@@ -87,15 +87,24 @@
87
87
  * const vec5 = getTempVector();
88
88
  * ```
89
89
  */
90
- export function getTempVector(vecOrX?: Vector3 | number | DOMPointReadOnly, y?: number, z?: number) {
90
+ export function getTempVector(): Vector3;
91
+ export function getTempVector(vec3: Vector3): Vector3;
92
+ export function getTempVector(vec3: [number, number, number]): Vector3;
93
+ export function getTempVector(dom: DOMPointReadOnly): Vector3;
94
+ export function getTempVector(x: number): Vector3;
95
+ export function getTempVector(x: number, y: number, z: number): Vector3;
96
+ export function getTempVector(vecOrX?: Vector3 | [number, number, number] | DOMPointReadOnly | number, y?: number, z?: number): Vector3 {
91
97
  const vec = _tempVecs.get();
92
98
  vec.set(0, 0, 0); // initialize with default values
93
99
  if (vecOrX instanceof Vector3) vec.copy(vecOrX);
100
+ else if (Array.isArray(vecOrX)) vec.set(vecOrX[0], vecOrX[1], vecOrX[2]);
94
101
  else if (vecOrX instanceof DOMPointReadOnly) vec.set(vecOrX.x, vecOrX.y, vecOrX.z);
95
102
  else {
96
- if (typeof vecOrX === "number") vec.x = vecOrX;
97
- if (typeof y === "number") vec.y = y;
98
- if (typeof z === "number") vec.z = z;
103
+ if (typeof vecOrX === "number") {
104
+ vec.x = vecOrX;
105
+ vec.y = y !== undefined ? y : vec.x;
106
+ vec.z = z !== undefined ? z : vec.x;
107
+ }
99
108
  }
100
109
  return vec;
101
110
  }
@@ -523,7 +532,7 @@
523
532
  if (obj["needle:rendercustomshadow"] === true) {
524
533
  return true;
525
534
  }
526
- else if(obj["needle:rendercustomshadow"] == undefined) {
535
+ else if (obj["needle:rendercustomshadow"] == undefined) {
527
536
  return true;
528
537
  }
529
538
  }
src/engine/engine_utils_screenshot.ts CHANGED
@@ -141,6 +141,10 @@
141
141
  width /= zoomLevel;
142
142
  height /= zoomLevel;
143
143
 
144
+ // save XR state and reset it for screenshot
145
+ const previousXRState = context.renderer.xr.enabled;
146
+ context.renderer.xr.enabled = false;
147
+
144
148
  // reset style during screenshot
145
149
  context.renderer.domElement.style.width = width + "px";
146
150
  context.renderer.domElement.style.height = height + "px";
@@ -247,6 +251,7 @@
247
251
  camera.aspect = previousAspect;
248
252
  camera.updateProjectionMatrix();
249
253
  }
254
+ context.renderer.xr.enabled = previousXRState;
250
255
  }
251
256
 
252
257
  return null;
src/engine-components/OrbitControls.ts CHANGED
@@ -311,6 +311,7 @@
311
311
  this._activePointerEvents = [];
312
312
  this.context.input.addEventListener("pointerdown", this._onPointerDown, { queue: InputEventQueue.Early });
313
313
  this.context.input.addEventListener("pointerup", this._onPointerUp, { queue: InputEventQueue.Early });
314
+ this.context.input.addEventListener("pointerup", this._onPointerUpLate, { queue: InputEventQueue.Late });
314
315
  }
315
316
 
316
317
  /** @internal */
@@ -327,6 +328,7 @@
327
328
  this._activePointerEvents.length = 0;
328
329
  this.context.input.removeEventListener("pointerdown", this._onPointerDown);
329
330
  this.context.input.removeEventListener("pointerup", this._onPointerUp);
331
+ this.context.input.removeEventListener("pointerup", this._onPointerUpLate);
330
332
  }
331
333
 
332
334
  private _activePointerEvents!: NEPointerEvent[];
@@ -373,6 +375,12 @@
373
375
  }
374
376
  };
375
377
 
378
+ private _onPointerUpLate = (evt: NEPointerEvent) => {
379
+ if(this.doubleClickToFocus && evt.isDoubleClick && !evt.used){
380
+ this.setTargetFromRaycast();
381
+ }
382
+ };
383
+
376
384
  private onControlsChangeStarted = () => {
377
385
  if (this._syncedTransform) {
378
386
  this._syncedTransform.requestOwnership();
@@ -447,8 +455,7 @@
447
455
  }
448
456
  }
449
457
 
450
- let focusAtPointer = (this.middleClickToFocus && this.context.input.getPointerClicked(1));
451
- focusAtPointer ||= (this.doubleClickToFocus && this.context.input.getPointerDoubleClicked(0) && this.context.time.time - this._enableTime > .3);
458
+ const focusAtPointer = (this.middleClickToFocus && this.context.input.getPointerClicked(1));
452
459
  if (focusAtPointer) {
453
460
  this.setTargetFromRaycast();
454
461
  }
src/engine-components/SpriteRenderer.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { BufferAttribute, BufferGeometry, Color, DoubleSide, Material, Mesh, MeshBasicMaterial, NearestFilter, SRGBColorSpace, Texture } from "three";
2
2
 
3
- import { Context } from "../engine/engine_context.js";
4
3
  import { serializable, serializeable } from "../engine/engine_serialization_decorator.js";
5
4
  import { getParam } from "../engine/engine_utils.js";
6
5
  import { NEEDLE_progressive } from "../engine/extensions/NEEDLE_progressive.js";
@@ -75,6 +74,16 @@
75
74
  */
76
75
  export class Sprite {
77
76
 
77
+ constructor(texture?: Texture) {
78
+ if (texture) {
79
+ this.texture = texture;
80
+ this.triangles = [0, 1, 2, 0, 2, 3];
81
+ this.uv = [{ x: 0, y: 0 }, { x: 1, y: 0 }, { x: 1, y: 1 }, { x: 0, y: 1 }];
82
+ this.vertices = [{ x: -.5, y: -.5 }, { x: .5, y: -.5 }, { x: .5, y: .5 }, { x: -.5, y: .5 }];
83
+ }
84
+ }
85
+
86
+
78
87
  @serializable()
79
88
  guid?: string;
80
89
  @serializable(Texture)
@@ -140,9 +149,49 @@
140
149
 
141
150
  export class SpriteData {
142
151
 
152
+ static create() {
153
+ const i = new SpriteData();
154
+ i.spriteSheet = new SpriteSheet();
155
+ return i;
156
+ }
157
+
158
+ // we don't assign anything here because it's used by the serialization system.
159
+ // there's currently a limitation in the serializer when e.g. spriteSheet is already assigned it will not be overriden by the serializer
160
+ // hence the spriteSheet field is undefined by default
161
+ constructor() { }
162
+
163
+ /**
164
+ * Set the sprite to be rendered in the currently assigned sprite sheet at the currently active index {@link index}
165
+ */
166
+ set sprite(sprite: Sprite | undefined) {
167
+ if (!sprite) {
168
+ return;
169
+ }
170
+ if (!this.spriteSheet) {
171
+ this.spriteSheet = new SpriteSheet();
172
+ this.spriteSheet.sprites = [sprite];
173
+ this.index = 0;
174
+ }
175
+ else {
176
+ if (this.index === null || this.index === undefined) this.index = 0;
177
+ this.spriteSheet.sprites[this.index] = sprite;
178
+ }
179
+ }
180
+ /** The currently active sprite */
181
+ get sprite(): Sprite | undefined {
182
+ if (!this.spriteSheet) return undefined;
183
+ return this.spriteSheet.sprites[this.index];
184
+ }
185
+
186
+ /**
187
+ * The spritesheet holds all sprites that can be rendered by the sprite renderer
188
+ */
143
189
  @serializable(SpriteSheet)
144
190
  spriteSheet?: SpriteSheet;
145
191
 
192
+ /**
193
+ * The index of the sprite to be rendered in the currently assigned sprite sheet
194
+ */
146
195
  @serializable()
147
196
  index: number = 0;
148
197
 
@@ -178,7 +227,7 @@
178
227
  }
179
228
 
180
229
  /**
181
- * The sprite renderer renders a sprite on a GameObject using an assigned spritesheet ({@link SpriteData})
230
+ * The sprite renderer renders a sprite on a GameObject using an assigned spritesheet ({@link SpriteData}).
182
231
  */
183
232
  export class SpriteRenderer extends Behaviour {
184
233
 
@@ -211,20 +260,67 @@
211
260
  @serializable()
212
261
  toneMapped: boolean = true;
213
262
 
263
+ /**
264
+ * Assign a new texture to the currently active sprite
265
+ */
266
+ set texture(value: Texture | undefined) {
267
+ if (!this._spriteSheet) return;
268
+ const currentSprite = this._spriteSheet.spriteSheet?.sprites[this.spriteIndex];
269
+ if (!currentSprite) return;
270
+ currentSprite.texture = value;
271
+ this.updateSprite();
272
+ }
273
+
274
+ /**
275
+ * Add a new sprite to the currently assigned sprite sheet. The sprite will be added to the end of the sprite sheet.
276
+ * Note that the sprite will not be rendered by default - set the `spriteIndex` to the index of the sprite to be rendered.
277
+ * @param sprite The sprite to be added
278
+ * @returns The index of the sprite in the sprite sheet
279
+ * @example
280
+ * ```typescript
281
+ * const spriteRenderer = gameObject.addComponent(SpriteRenderer);
282
+ * const index = spriteRenderer.addSprite(mySprite);
283
+ * if(index >= 0)
284
+ * spriteRenderer.spriteIndex = index;
285
+ * ```
286
+ */
287
+ addSprite(sprite: Sprite, setActive: boolean = false): number {
288
+ if (!this._spriteSheet) {
289
+ this._spriteSheet = SpriteData.create();
290
+ }
291
+ if (!this._spriteSheet.spriteSheet) return -1;
292
+ this._spriteSheet.spriteSheet?.sprites.push(sprite);
293
+ const index = this._spriteSheet.spriteSheet?.sprites.length - 1;
294
+ if (setActive) {
295
+ this.spriteIndex = index;
296
+ }
297
+ return index;
298
+ }
299
+
300
+ /**
301
+ * Get the currently active sprite
302
+ */
214
303
  @serializable(SpriteData)
215
304
  get sprite(): SpriteData | undefined {
216
305
  return this._spriteSheet;
217
306
  }
218
307
  /**
219
- * Set a new sprite sheetsheet or update the index of the sprite to be rendered in the currently assigned sprite sheet
308
+ * Set the sprite to be rendered in the currently assigned sprite sheet at the currently active index {@link spriteIndex}
220
309
  */
221
- set sprite(value: SpriteData | undefined | number) {
310
+ set sprite(value: Sprite | SpriteData | undefined | number) {
222
311
  if (value === this._spriteSheet) return;
223
312
  if (typeof value === "number") {
224
313
  const index = Math.floor(value);
225
314
  this.spriteIndex = index;
226
315
  return;
227
316
  }
317
+ else if (value instanceof Sprite) {
318
+ if (!this._spriteSheet) {
319
+ this._spriteSheet = SpriteData.create();
320
+ }
321
+ this._spriteSheet.sprite = value;
322
+ this.updateSprite();
323
+ }
228
324
  else {
229
325
  this._spriteSheet = value;
230
326
  this.updateSprite();
@@ -236,6 +332,7 @@
236
332
  */
237
333
  set spriteIndex(value: number) {
238
334
  if (!this._spriteSheet) return;
335
+ if (value === this.spriteIndex) return;
239
336
  this._spriteSheet.index = value;
240
337
  this.updateSprite();
241
338
  }
@@ -255,6 +352,10 @@
255
352
  /** @internal */
256
353
  awake(): void {
257
354
  this._currentSprite = undefined;
355
+ if (!this._spriteSheet) {
356
+ this._spriteSheet = new SpriteData();
357
+ this._spriteSheet.spriteSheet = new SpriteSheet();
358
+ }
258
359
  if (debug) {
259
360
  console.log("Awake", this.name, this, this.sprite);
260
361
  }
@@ -270,19 +371,24 @@
270
371
 
271
372
  /**
272
373
  * Update the sprite. Modified properties will be applied to the sprite mesh. This method is called automatically when the sprite is changed.
374
+ * @param force If true, the sprite will be forced to update.
375
+ * @returns True if the sprite was updated successfully
273
376
  */
274
- updateSprite(force: boolean = false) {
275
- if (!this.__didAwake && !force) return;
276
- if (!this.sprite?.spriteSheet?.sprites) return;
277
- const sprite = this.sprite.spriteSheet.sprites[this.spriteIndex];
377
+ updateSprite(force: boolean = false): boolean {
378
+ if (!this.__didAwake && !force) return false;
379
+ const data = this._spriteSheet;
380
+ if (!data?.spriteSheet?.sprites) {
381
+ console.warn("SpriteRenderer has no data or spritesheet assigned...");
382
+ return false;
383
+ }
384
+ const sprite = data.spriteSheet.sprites[this.spriteIndex];
278
385
  if (!sprite) {
279
386
  if (debug)
280
- console.warn("Sprite not found", this.spriteIndex, this.sprite.spriteSheet.sprites);
281
- return;
387
+ console.warn("Sprite not found", this.spriteIndex, data.spriteSheet.sprites);
388
+ return false;
282
389
  }
283
390
  if (!this._currentSprite) {
284
391
  const mat = new MeshBasicMaterial({ color: 0xffffff, side: DoubleSide });
285
- if (!mat) return;
286
392
  if (showWireframe)
287
393
  mat.wireframe = true;
288
394
  if (this.color) {
@@ -330,6 +436,7 @@
330
436
  this.sharedMaterial.transparent = this.transparent;
331
437
  }
332
438
  this._currentSprite.castShadow = this.castShadows;
333
- this._spriteSheet?.update( this.sharedMaterial);
439
+ data?.update(this.sharedMaterial);
440
+ return true;
334
441
  }
335
442
  }