@needle-tools/engine 3.32.21-alpha → 3.32.22-alpha

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.
Files changed (30) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/needle-engine.js +2856 -2833
  3. package/dist/needle-engine.light.js +2725 -2702
  4. package/dist/needle-engine.light.min.js +159 -159
  5. package/dist/needle-engine.light.umd.cjs +119 -119
  6. package/dist/needle-engine.min.js +159 -159
  7. package/dist/needle-engine.umd.cjs +119 -119
  8. package/lib/engine/extensions/NEEDLE_progressive.d.ts +11 -5
  9. package/lib/engine/extensions/NEEDLE_progressive.js +51 -34
  10. package/lib/engine/extensions/NEEDLE_progressive.js.map +1 -1
  11. package/lib/engine-components/SpriteRenderer.d.ts +4 -1
  12. package/lib/engine-components/SpriteRenderer.js +24 -4
  13. package/lib/engine-components/SpriteRenderer.js.map +1 -1
  14. package/lib/engine-components/ui/Graphic.d.ts +2 -1
  15. package/lib/engine-components/ui/Graphic.js +8 -2
  16. package/lib/engine-components/ui/Graphic.js.map +1 -1
  17. package/lib/engine-components/ui/Image.js +3 -2
  18. package/lib/engine-components/ui/Image.js.map +1 -1
  19. package/lib/engine-components-experimental/networking/PlayerSync.js +1 -1
  20. package/lib/engine-components-experimental/networking/PlayerSync.js.map +1 -1
  21. package/package.json +1 -1
  22. package/plugins/types/needleConfig.d.ts +1 -0
  23. package/plugins/types/userconfig.d.ts +3 -0
  24. package/plugins/vite/build-pipeline.js +97 -0
  25. package/plugins/vite/index.js +2 -0
  26. package/src/engine/extensions/NEEDLE_progressive.ts +61 -43
  27. package/src/engine-components/SpriteRenderer.ts +28 -7
  28. package/src/engine-components/ui/Graphic.ts +9 -2
  29. package/src/engine-components/ui/Image.ts +3 -2
  30. package/src/engine-components-experimental/networking/PlayerSync.ts +1 -1
@@ -0,0 +1,97 @@
1
+ import { exec, spawn } from 'child_process';
2
+ import { getOutputDirectory, loadConfig, tryLoadProjectConfig } from './config.js';
3
+ import { existsSync, readdirSync } from 'fs';
4
+
5
+ // see https://linear.app/needle/issue/NE-3798
6
+
7
+ /**
8
+ * Runs the needle build pipeline as part of the vite build process
9
+ * @param {import('../types').userSettings} userSettings
10
+ * @returns {import('vite').Plugin}
11
+ */
12
+ export const needleBuildPipeline = async (command, config, userSettings) => {
13
+ // we only want to run compression here if this is a distribution build
14
+ if (command !== "build") return;
15
+ if (userSettings.noBuildPipeline) return;
16
+
17
+ const meta = await loadConfig();
18
+ let shouldRun = false;
19
+ if (meta && meta.developmentBuild === false) {
20
+ shouldRun = true;
21
+ }
22
+ if (!shouldRun) {
23
+ log("Skipping build pipeline because this is a development build");
24
+ return;
25
+ }
26
+
27
+ /** @type {Promise<any>|null} */
28
+ let task = null;
29
+ let taskFinished = false;
30
+ let taskSucceeded = false;
31
+ return {
32
+ name: 'needle-buildpipeline',
33
+ enforce: "post",
34
+ apply: 'build',
35
+ buildEnd() {
36
+ // start the compression process once vite is done copying the files
37
+ task = invokeBuildPipeline().then((res) => {
38
+ taskFinished = true;
39
+ taskSucceeded = res;
40
+ });
41
+ },
42
+ closeBundle() {
43
+ // this is the last hook that is called, so we can wait for the task to finish here
44
+ if (taskFinished) return;
45
+ // delay the final log slightly to give other plugins a chance to log their stuff
46
+ wait(100).then(() => {
47
+ if (!taskFinished) log("Waiting for postprocessing to finish...")
48
+ });
49
+ return task.then(_ => {
50
+ log("finished", taskSucceeded ? "successfully" : "with errors");
51
+ });
52
+ },
53
+ }
54
+ }
55
+
56
+ function log(...args) {
57
+ console.log("[needle-buildpipeline]", ...args);
58
+ }
59
+
60
+ /**
61
+ * @param {*} opts
62
+ * @returns {Promise<boolean>}
63
+ */
64
+ async function invokeBuildPipeline(opts) {
65
+ await wait(1000);
66
+ const outputDirectory = getOutputDirectory() + "/assets";
67
+ if (!existsSync(outputDirectory)) {
68
+ log("No output directory found at ", outputDirectory);
69
+ return;
70
+ }
71
+ const files = readdirSync(outputDirectory).filter(f => f.endsWith(".glb") || f.endsWith(".gltf"));
72
+ log(files.length + " file(s) to process");
73
+
74
+ const cmd = "npm run transform --prefix node_modules/@needle-tools/gltf-build-pipeline";
75
+ log("Running command \"" + cmd + "\" at " + process.cwd() + "...");
76
+ const sub = exec(cmd);
77
+ sub.stdout.on('data', data => {
78
+ if (data.length <= 0) return;
79
+ // ensure that it doesnt end with a newline
80
+ if (data.endsWith("\n")) data = data.slice(0, -1);
81
+ console.log(data);
82
+ });
83
+ sub.stderr.on('data', console.error);
84
+ return new Promise((resolve, reject) => {
85
+ sub.on('exit', (code) => {
86
+ resolve(code === 0);
87
+ });
88
+ });
89
+ }
90
+
91
+ function wait(ms) {
92
+ return new Promise((resolve, reject) => {
93
+ setTimeout(() => {
94
+ resolve();
95
+ }, ms);
96
+ });
97
+ }
@@ -43,6 +43,7 @@ export { needleFacebookInstantGames } from "./facebook-instant-games.js";
43
43
  import { vite_4_4_hack } from "./vite-4.4-hack.js";
44
44
  import { needleImportsLogger } from "./imports-logger.js";
45
45
  import { needleBuildInfo } from "./buildinfo.js";
46
+ import { needleBuildPipeline } from "./build-pipeline.js";
46
47
 
47
48
 
48
49
  export * from "./gzip.js";
@@ -79,6 +80,7 @@ export const needlePlugins = async (command, config, userSettings) => {
79
80
  vite_4_4_hack(command, config, userSettings),
80
81
  needleFacebookInstantGames(command, config, userSettings),
81
82
  needleImportsLogger(command, config, userSettings),
83
+ needleBuildPipeline(command, config, userSettings),
82
84
  ];
83
85
  array.push(await editorConnection(command, config, userSettings, array));
84
86
  return array;
@@ -22,6 +22,7 @@ let show_lod0 = false;
22
22
  if (debug) {
23
23
  window.addEventListener("keyup", evt => {
24
24
  if (evt.key === "p") {
25
+ console.log("Toggle progressive textures", debug_toggle_maps);
25
26
  debug_toggle_maps.forEach((map, material) => {
26
27
  Object.entries(map).forEach(([key, value]) => {
27
28
  if (show_lod0) {
@@ -43,51 +44,68 @@ export class NEEDLE_progressive implements GLTFLoaderPlugin {
43
44
  return EXTENSION_NAME;
44
45
  }
45
46
 
46
- static assignTextureLOD(context: Context, source: SourceIdentifier | undefined, material: Material, level: number = 0) : Promise<any> {
47
- if (!material) return Promise.resolve(null);
48
-
49
- const promises: Array<Promise<Texture | null>> = [];
50
-
51
-
52
- if (material instanceof RawShaderMaterial) {
53
- // iterate uniforms of custom shaders
54
- for (const slot of Object.keys(material.uniforms)) {
55
- const val = material.uniforms[slot].value;
56
- if (val instanceof Texture) {
57
- const task = this.assignTextureLODForSlot(context, source, material, level, slot, val);
58
- promises.push(task);
47
+ /** Load a different resolution of a texture (if available)
48
+ * @param context the context
49
+ * @param source the sourceid of the file from which the texture is loaded (this is usually the component's sourceId)
50
+ * @param materialOrTexture the material or texture to load the LOD for (if passing in a material all textures in the material will be loaded)
51
+ * @param level the level of detail to load (0 is the highest resolution) - currently only 0 is supported
52
+ */
53
+ static assignTextureLOD(context: Context, source: SourceIdentifier | undefined, materialOrTexture: Material | Texture, level: number = 0): Promise<any> {
54
+ if (!materialOrTexture) return Promise.resolve(null);
55
+
56
+ if (materialOrTexture instanceof Material) {
57
+ const material = materialOrTexture;
58
+ const promises: Array<Promise<Texture | null>> = [];
59
+
60
+ if (material instanceof RawShaderMaterial) {
61
+ // iterate uniforms of custom shaders
62
+ for (const slot of Object.keys(material.uniforms)) {
63
+ const val = material.uniforms[slot].value;
64
+ if (val instanceof Texture) {
65
+ const task = this.assignTextureLODForSlot(context, source, val, level, material, slot);
66
+ promises.push(task);
67
+ }
59
68
  }
60
69
  }
61
- }
62
- else {
63
- for (const slot of Object.keys(material)) {
64
- const val = material[slot];
65
- if (val instanceof Texture) {
66
- const task = this.assignTextureLODForSlot(context, source, material, level, slot, val);
67
- promises.push(task);
70
+ else {
71
+ for (const slot of Object.keys(material)) {
72
+ const val = material[slot];
73
+ if (val instanceof Texture) {
74
+ const task = this.assignTextureLODForSlot(context, source, val, level, material, slot);
75
+ promises.push(task);
76
+ }
68
77
  }
69
78
  }
79
+ return PromiseAllWithErrors(promises);
80
+ }
81
+
82
+ if (materialOrTexture instanceof Texture) {
83
+ const texture = materialOrTexture;
84
+ return this.assignTextureLODForSlot(context, source, texture, level, null, null);
70
85
  }
71
86
 
72
- return PromiseAllWithErrors(promises);
87
+ return Promise.resolve(null);
73
88
  }
74
89
 
75
- private static assignTextureLODForSlot(context: Context, source: SourceIdentifier | undefined, material: Material, level: number, slot: string, val: any): Promise<Texture | null> {
90
+ private static assignTextureLODForSlot(context: Context, source: SourceIdentifier | undefined, val: any, level: number, material: Material | null, slot: string | null): Promise<Texture | null> {
76
91
  if (val?.isTexture !== true) return Promise.resolve(null);
77
92
 
78
- if (debug) console.log("-----------\n", "FIND", material.name, slot, val?.name, val?.userData, val, material);
93
+ if (debug) console.log("-----------\n", "FIND", material?.name, slot, val?.name, val?.userData, val, material);
79
94
 
80
- return NEEDLE_progressive.getOrLoadTexture(context, source, material, slot, val, level).then(t => {
95
+ return NEEDLE_progressive.getOrLoadTexture(context, source, val, level, material, slot).then(t => {
81
96
 
82
97
  if (t?.isTexture === true) {
83
98
 
84
- if (debug) console.warn("Assign LOD", material.name, slot, t.name, t["guid"], material, "Prev:", val, "Now:", t, "\n--------------");
99
+ if (debug) console.warn("Assign LOD", material?.name, slot, t.name, t["guid"], material, "Prev:", val, "Now:", t, "\n--------------");
85
100
 
86
- material[slot] = t;
87
101
  t.needsUpdate = true;
88
- material.needsUpdate = true;
89
102
 
90
- if (debug) {
103
+ if (material && slot) {
104
+ material[slot] = t;
105
+ material.needsUpdate = true;
106
+ }
107
+
108
+ if (debug && material && slot) {
91
109
  let debug_map = debug_toggle_maps.get(material);
92
110
  if (!debug_map) {
93
111
  debug_map = {};
@@ -146,16 +164,16 @@ export class NEEDLE_progressive implements GLTFLoaderPlugin {
146
164
  private static resolved: { [key: string]: Texture } = {};
147
165
  private static currentlyLoading: { [key: string]: Promise<Texture | null> } = {};
148
166
 
149
- private static async getOrLoadTexture(context: Context, source: SourceIdentifier | undefined, material: Material, slot: string, current: Texture, _level: number): Promise<Texture | null> {
167
+ private static async getOrLoadTexture(context: Context, source: SourceIdentifier | undefined, current: Texture, _level: number, material: Material | null, slot: string | null): Promise<Texture | null> {
150
168
 
151
169
  const key = current.uuid;
152
170
 
153
171
  let progressiveInfo: ProgressiveTextureSchema | undefined;
154
172
 
155
173
  // See https://github.com/needle-tools/needle-engine-support/issues/133
156
- if(current.source && current.source[$progressiveTextureExtension])
174
+ if (current.source && current.source[$progressiveTextureExtension])
157
175
  progressiveInfo = current.source[$progressiveTextureExtension];
158
- if(!progressiveInfo) progressiveInfo = NEEDLE_progressive.cache.get(key);
176
+ if (!progressiveInfo) progressiveInfo = NEEDLE_progressive.cache.get(key);
159
177
 
160
178
  if (progressiveInfo) {
161
179
  if (debug)
@@ -171,12 +189,12 @@ export class NEEDLE_progressive implements GLTFLoaderPlugin {
171
189
  let res = this.resolved[resolveKey];
172
190
  // check if the texture has been disposed or not
173
191
  if (res.image?.data || res.source?.data) {
174
- if (debug) console.log("Texture has already been loaded: " + resolveKey, material.name, slot, current.name, res);
192
+ if (debug) console.log("Texture has already been loaded: " + resolveKey, material?.name, slot, current.name, res);
175
193
  res = this.copySettings(current, res);
176
194
  return res;
177
195
  }
178
196
  else if (res) {
179
- if(debug) console.warn("Texture has been disposed, will load again: " + resolveKey, material.name, slot, current.name, res.source.data);
197
+ if (debug) console.warn("Texture has been disposed, will load again: " + resolveKey, material?.name, slot, current.name, res.source.data);
180
198
  }
181
199
  }
182
200
 
@@ -184,7 +202,7 @@ export class NEEDLE_progressive implements GLTFLoaderPlugin {
184
202
  try {
185
203
  if (this.currentlyLoading[resolveKey] !== undefined) {
186
204
  if (debug)
187
- console.log("Already loading:", material.name + "." + slot, resolveKey);
205
+ console.log("Already loading:", material?.name + "." + slot, resolveKey);
188
206
  let res = await this.currentlyLoading[resolveKey];
189
207
  if (res)
190
208
  res = this.copySettings(current, res);
@@ -196,19 +214,19 @@ export class NEEDLE_progressive implements GLTFLoaderPlugin {
196
214
  addDracoAndKTX2Loaders(loader, context);
197
215
 
198
216
 
199
- if (debug) console.warn("Start loading " + uri, material.name, slot, ext.guid);
217
+ if (debug) console.warn("Start loading " + uri, material?.name, slot, ext.guid);
200
218
  if (debug) {
201
219
  await delay(Math.random() * 1000);
202
220
  }
203
221
 
204
222
  const gltf = await loader.loadAsync(uri);
205
223
  const parser = gltf.parser;
206
- if (debug) console.log("Loading finished " + uri, material.name, slot, ext.guid);
224
+ if (debug) console.log("Loading finished " + uri, material?.name, slot, ext.guid);
207
225
  let index = -1;
208
226
  let found = false;
209
-
227
+
210
228
  if (!gltf.parser.json?.textures) {
211
- if (debug) console.warn("No textures in glTF " + uri + " - may be a bug", material.name, slot, ext.guid);
229
+ if (debug) console.warn("No textures in glTF " + uri + " - may be a bug", material?.name, slot, ext.guid);
212
230
  return resolve(null);
213
231
  }
214
232
 
@@ -234,7 +252,7 @@ export class NEEDLE_progressive implements GLTFLoaderPlugin {
234
252
  }
235
253
  this.resolved[resolveKey] = tex as Texture;
236
254
  if (debug)
237
- console.log(material.name, slot, "change \"" + current.name + "\" → \"" + tex.name + "\"", uri, index, tex, material, resolveKey);
255
+ console.log(material?.name, slot, "change \"" + current.name + "\" → \"" + tex.name + "\"", uri, index, tex, material, resolveKey);
238
256
  resolve(tex);
239
257
  });
240
258
  this.currentlyLoading[resolveKey] = request;
@@ -343,7 +361,7 @@ export class NEEDLE_progressive implements GLTFLoaderPlugin {
343
361
  private static _currentProgressiveLoadingInfo: Map<Context, ProgressiveLoadingInfo[]> = new Map();
344
362
 
345
363
  // called whenever a progressive loading event starts
346
- private static onProgressiveLoadStart(context: Context, source: SourceIdentifier | undefined, uri: string, material: Material, slot: string): ProgressiveLoadingInfo {
364
+ private static onProgressiveLoadStart(context: Context, source: SourceIdentifier | undefined, uri: string, material: Material | null, slot: string | null): ProgressiveLoadingInfo {
347
365
  if (!this._currentProgressiveLoadingInfo.has(context)) {
348
366
  this._currentProgressiveLoadingInfo.set(context, []);
349
367
  }
@@ -379,11 +397,11 @@ export class ProgressiveLoadingInfo {
379
397
  readonly context: Context;
380
398
  readonly source: SourceIdentifier | undefined;
381
399
  readonly uri: string;
382
- readonly material?: Material;
383
- readonly slot?: string;
400
+ readonly material?: Material | null;
401
+ readonly slot?: string | null;
384
402
  // TODO: can contain information if the event is a background process / preloading or if the object is currently visible
385
403
 
386
- constructor(context: Context, source: SourceIdentifier | undefined, uri: string, material?: Material, slot?: string) {
404
+ constructor(context: Context, source: SourceIdentifier | undefined, uri: string, material?: Material | null, slot?: string | null) {
387
405
  this.context = context;
388
406
  this.source = source;
389
407
  this.uri = uri;
@@ -1,8 +1,10 @@
1
1
  import * as THREE from "three";
2
2
  import { Material, NearestFilter, Texture } from "three";
3
3
 
4
+ import { Context } from "../engine/engine_context.js";
4
5
  import { serializable, serializeable } from "../engine/engine_serialization_decorator.js";
5
6
  import { getParam } from "../engine/engine_utils.js";
7
+ import { NEEDLE_progressive } from "../engine/extensions/NEEDLE_progressive.js";
6
8
  import { Behaviour } from "./Component.js";
7
9
  import { RGBAColor } from "./js-extensions/RGBAColor.js";
8
10
 
@@ -70,7 +72,6 @@ class Vec2 {
70
72
  }
71
73
 
72
74
  export class Sprite {
73
-
74
75
  @serializable()
75
76
  guid?: string;
76
77
  @serializable(Texture)
@@ -83,6 +84,7 @@ export class Sprite {
83
84
  vertices!: Array<Vec2>;
84
85
 
85
86
  _geometry?: THREE.BufferGeometry;
87
+ _hasLoadedProgressive: boolean = false;
86
88
  }
87
89
 
88
90
  const $spriteTexOwner = Symbol("spriteOwner");
@@ -101,7 +103,7 @@ export class SpriteData {
101
103
  @serializable()
102
104
  index: number = 0;
103
105
 
104
- update() {
106
+ update(context: Context, sourceId: string | undefined, material: Material | undefined) {
105
107
  if (!this.spriteSheet) return;
106
108
  const index = this.index;
107
109
  if (index < 0 || index >= this.spriteSheet.sprites.length)
@@ -113,6 +115,21 @@ export class SpriteData {
113
115
  if (tex.minFilter == NearestFilter && tex.magFilter == NearestFilter)
114
116
  tex.anisotropy = 1;
115
117
  tex.needsUpdate = true;
118
+
119
+ if (!slice._hasLoadedProgressive) {
120
+ slice._hasLoadedProgressive = true;
121
+ const previousTexture = tex;
122
+ NEEDLE_progressive.assignTextureLOD(context, sourceId, tex, 0).then(res => {
123
+ if (res instanceof Texture) {
124
+ slice.texture = res;
125
+ const shouldUpdateInMaterial = material?.["map"] === previousTexture;
126
+ if (shouldUpdateInMaterial) {
127
+ material["map"] = res;
128
+ material.needsUpdate = true;
129
+ }
130
+ }
131
+ });
132
+ }
116
133
  }
117
134
  }
118
135
 
@@ -137,8 +154,11 @@ export class SpriteRenderer extends Behaviour {
137
154
  if (value === this._spriteSheet) return;
138
155
  if (typeof value === "number") {
139
156
  const index = Math.floor(value);
140
- if (index === value)
141
- this.spriteIndex = index;
157
+ // if (value === index)
158
+ this.spriteIndex = index;
159
+ // else if (debug) {
160
+ // console.log("Spritesheet framedrop", index, value);
161
+ // }
142
162
  return;
143
163
  }
144
164
  else {
@@ -172,7 +192,7 @@ export class SpriteRenderer extends Behaviour {
172
192
 
173
193
  awake(): void {
174
194
  this._currentSprite = undefined;
175
- if(debug) {
195
+ if (debug) {
176
196
  console.log("Awake", this.name, this, this.sprite);
177
197
  }
178
198
  }
@@ -218,6 +238,7 @@ export class SpriteRenderer extends Behaviour {
218
238
  }
219
239
  this.sharedMaterial = mat;
220
240
  this._currentSprite = new THREE.Mesh(SpriteUtils.getOrCreateGeometry(sprite), mat);
241
+ NEEDLE_progressive.assignTextureLOD(this.context, this.sourceId, mat, 0);
221
242
  }
222
243
  else {
223
244
  this._currentSprite.geometry = SpriteUtils.getOrCreateGeometry(sprite);
@@ -231,7 +252,7 @@ export class SpriteRenderer extends Behaviour {
231
252
  this.gameObject.add(this._currentSprite);
232
253
  }
233
254
 
234
- if(this._currentSprite){
255
+ if (this._currentSprite) {
235
256
  this._currentSprite.layers.set(this.layer)
236
257
  }
237
258
 
@@ -240,6 +261,6 @@ export class SpriteRenderer extends Behaviour {
240
261
  this.sharedMaterial.transparent = this.transparent;
241
262
  }
242
263
  this._currentSprite.castShadow = this.castShadows;
243
- this._spriteSheet?.update();
264
+ this._spriteSheet?.update(this.context, this.sourceId, this.sharedMaterial);
244
265
  }
245
266
  }
@@ -1,8 +1,10 @@
1
1
  import { Color, LinearSRGBColorSpace, Object3D, SRGBColorSpace, Texture } from 'three';
2
2
  import * as ThreeMeshUI from 'three-mesh-ui'
3
+ import type { Options } from 'three-mesh-ui/build/types/core/elements/MeshUIBaseElement.js';
3
4
  import SimpleStateBehavior from "three-mesh-ui/examples/behaviors/states/SimpleStateBehavior.js"
4
5
 
5
6
  import { serializable } from '../../engine/engine_serialization_decorator.js';
7
+ import { NEEDLE_progressive } from '../../engine/extensions/NEEDLE_progressive.js';
6
8
  import { GameObject } from '../Component.js';
7
9
  import { RGBAColor } from "../js-extensions/RGBAColor.js"
8
10
  import { BaseUIComponent } from "./BaseUIComponent.js";
@@ -117,7 +119,7 @@ export class Graphic extends BaseUIComponent implements IGraphic, IRectTransform
117
119
  }
118
120
  }
119
121
 
120
- setOptions(opts: object) {
122
+ setOptions(opts: Options) {
121
123
  this.makePanel();
122
124
  if (this.uiObject) {
123
125
  //@ts-ignore
@@ -218,9 +220,14 @@ export class Graphic extends BaseUIComponent implements IGraphic, IRectTransform
218
220
  }
219
221
  // }
220
222
  this.setOptions({ backgroundImage: tex, borderRadius: 0, backgroundOpacity: this.color.alpha, backgroundSize: "stretch" });
223
+ NEEDLE_progressive.assignTextureLOD(this.context, this.sourceId, tex, 0).then(res => {
224
+ if (res instanceof Texture) {
225
+ this.setOptions({ backgroundImage: res });
226
+ }
227
+ });
221
228
  }
222
229
  else {
223
- this.setOptions({ backgroundImage: null, borderRadius: 0, backgroundOpacity: this.color.alpha });
230
+ this.setOptions({ backgroundImage: undefined, borderRadius: 0, backgroundOpacity: this.color.alpha });
224
231
  }
225
232
  this.markDirty();
226
233
  }
@@ -40,7 +40,8 @@ export class Image extends MaskableGraphic {
40
40
  private pixelsPerUnitMultiplier: number = 1;
41
41
 
42
42
  private isBuiltinSprite() {
43
- switch (this.sprite?.texture?.name) {
43
+ const sprite = this.sprite;
44
+ switch (sprite?.texture?.name) {
44
45
  case "InputFieldBackground":
45
46
  case "UISprite":
46
47
  case "Background":
@@ -49,7 +50,7 @@ export class Image extends MaskableGraphic {
49
50
  }
50
51
  // this is a hack/workaround for production builds where the name of the sprite is missing
51
52
  // need to remove this!!!!
52
- if (!this.sprite?.texture?.name?.length && this.sprite?.texture?.image?.width === 32 && this.sprite?.texture?.image?.height === 32)
53
+ if (!sprite?.texture?.name?.length && sprite?.texture?.image?.width === 32 && sprite?.texture?.image?.height === 32)
53
54
  return true;
54
55
  return false;
55
56
  }
@@ -276,7 +276,7 @@ export class PlayerState extends Behaviour {
276
276
  }
277
277
  else if (debug) console.warn("PlayerState.start → owner is still undefined but dontDestroy is set to true", this.name);
278
278
  }
279
- else console.log("PlayerState.start → owner is assigned", this.owner);
279
+ else if(debug) console.log("PlayerState.start → owner is assigned", this.owner);
280
280
  }, 2000);
281
281
  }
282
282
  }