From 6dedb75b1bd74be4f5f858cd306e4e89defa080d Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Sat, 3 Oct 2026 11:44:02 -0500 Subject: [PATCH] wip prefer null for empty over undefined --- src/engine/actions/action/rotate-by.ts | 19 +++++---- src/engine/collision/body-component.ts | 16 +++---- .../collision/colliders/circle-collider.ts | 6 +-- .../collision/detection/dynamic-tree.ts | 26 ++++++------ src/engine/director/director.ts | 42 +++++++++---------- .../entity-component-system/component.ts | 2 +- .../excalibur-graphics-context-2d-canvas.ts | 15 ++++--- .../excalibur-graphics-context-webgl.ts | 8 ++-- .../context/excalibur-graphics-context.ts | 4 +- .../image-renderer-v2/image-renderer-v2.ts | 2 +- .../context/image-renderer/image-renderer.ts | 2 +- .../material-renderer/material-renderer.ts | 4 +- src/engine/graphics/context/material.ts | 10 ++--- .../particle-renderer/particle-renderer.ts | 2 +- .../context/shader-pipeline/shader-pass.ts | 16 +++---- src/engine/graphics/context/shader.ts | 4 +- src/engine/graphics/filtering.ts | 4 +- src/engine/graphics/font-text-instance.ts | 30 ++++++------- src/engine/graphics/font.ts | 4 +- src/engine/graphics/graphics-component.ts | 28 ++++++------- src/engine/graphics/image-source.ts | 4 +- .../shader-pipeline-post-processor.ts | 4 +- src/engine/graphics/sprite-font.ts | 4 +- src/engine/input/gamepad.ts | 8 +--- src/engine/particles/particles.ts | 2 +- src/engine/resources/sound/sound-manager.ts | 4 +- src/engine/scene.ts | 6 +-- src/engine/util/log.ts | 2 +- src/engine/util/serializer.ts | 4 +- src/spec/vitest/body-component-spec.ts | 4 +- src/spec/vitest/director-spec.ts | 16 +++---- src/spec/vitest/dynamic-tree-spec.ts | 2 +- src/spec/vitest/material-pipeline-spec.ts | 2 +- 33 files changed, 154 insertions(+), 152 deletions(-) diff --git a/src/engine/actions/action/rotate-by.ts b/src/engine/actions/action/rotate-by.ts index ec097a5b..f3e30875 100644 --- a/src/engine/actions/action/rotate-by.ts +++ b/src/engine/actions/action/rotate-by.ts @@ -136,18 +136,18 @@ export class RotateBy implements Action { private _motion: MotionComponent; public x!: number; public y!: number; - private _start!: number; + private _start: number | null = null; private _end!: number; private _speed: number; private _offset: number; private _rotationType: RotationType; private _direction!: number; - private _distance!: number; + private _distance: number | null = null; private _shortDistance!: number; private _longDistance!: number; private _shortestPathIsPositive!: boolean; - private _currentNonCannonAngle!: number; + private _currentNonCannonAngle: number | null = null; private _started = false; private _stopped = false; constructor(entity: Entity, angleRadiansOffset: number, speed: number, rotationType?: RotationType) { @@ -214,7 +214,7 @@ export class RotateBy implements Action { } this._motion.angularVelocity = this._direction * this._speed; - this._currentNonCannonAngle += this._direction * this._speed * (elapsed / 1000); + this._currentNonCannonAngle! += this._direction * this._speed * (elapsed / 1000); if (this.isComplete()) { this._tx.rotation = this._end; @@ -224,7 +224,10 @@ export class RotateBy implements Action { } public isComplete(): boolean { - const distanceTraveled = Math.abs(this._currentNonCannonAngle - this._start); + if (this._distance === null) { + return this._stopped; + } + const distanceTraveled = Math.abs(this._currentNonCannonAngle! - this._start!); return this._stopped || distanceTraveled >= Math.abs(this._distance); } @@ -237,8 +240,8 @@ export class RotateBy implements Action { public reset(): void { this._started = false; this._stopped = false; - this._start = undefined as any; - this._currentNonCannonAngle = undefined as any; - this._distance = undefined as any; + this._start = null; + this._currentNonCannonAngle = null; + this._distance = null; } } diff --git a/src/engine/collision/body-component.ts b/src/engine/collision/body-component.ts index 94e326ee..4e24208f 100644 --- a/src/engine/collision/body-component.ts +++ b/src/engine/collision/body-component.ts @@ -165,8 +165,8 @@ export class BodyComponent extends Component implements Clonable public set mass(newMass: number) { this._mass = newMass; - this._cachedInertia = undefined as any; - this._cachedInverseInertia = undefined as any; + this._cachedInertia = null; + this._cachedInverseInertia = null; } /** @@ -261,12 +261,12 @@ export class BodyComponent extends Component implements Clonable } } - private _cachedInertia!: number; + private _cachedInertia: number | null = null; /** * Get the moment of inertia from the {@apilink ColliderComponent} */ public get inertia() { - if (this._cachedInertia) { + if (this._cachedInertia !== null) { return this._cachedInertia; } @@ -274,10 +274,10 @@ export class BodyComponent extends Component implements Clonable const collider = this.owner!.get(ColliderComponent); if (collider) { collider.$colliderAdded.subscribe(() => { - this._cachedInertia = null as any; + this._cachedInertia = null; }); collider.$colliderRemoved.subscribe(() => { - this._cachedInertia = null as any; + this._cachedInertia = null; }); const maybeCollider = collider.get(); if (maybeCollider) { @@ -287,12 +287,12 @@ export class BodyComponent extends Component implements Clonable return 0; } - private _cachedInverseInertia!: number; + private _cachedInverseInertia: number | null = null; /** * Get the inverse moment of inertial from the {@apilink ColliderComponent}. If {@apilink CollisionType.Fixed} this is 0, meaning "infinite" mass */ public get inverseInertia() { - if (this._cachedInverseInertia) { + if (this._cachedInverseInertia !== null) { return this._cachedInverseInertia; } return (this._cachedInverseInertia = this.collisionType === CollisionType.Fixed ? 0 : 1 / this.inertia); diff --git a/src/engine/collision/colliders/circle-collider.ts b/src/engine/collision/colliders/circle-collider.ts index 8a033dc7..05b3decb 100644 --- a/src/engine/collision/colliders/circle-collider.ts +++ b/src/engine/collision/colliders/circle-collider.ts @@ -61,7 +61,7 @@ export class CircleCollider extends Collider { this._syncedOffsetY = this.offset.y; tx.matrix.clone(this._globalMatrix); this._globalMatrix.translate(this.offset.x, this.offset.y); - this._radius = undefined; + this._radius = null; this._worldVersion++; } @@ -77,13 +77,13 @@ export class CircleCollider extends Collider { private _naturalRadius!: number; - private _radius: number | undefined; + private _radius: number | null = null; /** * Get the radius of the circle */ public get radius(): number { this._ensureWorld(); - if (this._radius) { + if (this._radius !== null) { return this._radius; } const tx = this._transform; diff --git a/src/engine/collision/detection/dynamic-tree.ts b/src/engine/collision/detection/dynamic-tree.ts index cb5dae2f..fa2a6b9e 100644 --- a/src/engine/collision/detection/dynamic-tree.ts +++ b/src/engine/collision/detection/dynamic-tree.ts @@ -12,17 +12,17 @@ import type { DynamicTreeConfig } from '../physics-config'; * Dynamic Tree Node used for tracking bounds within the tree */ export class TreeNode { - public left?: TreeNode; - public right?: TreeNode; + public left: TreeNode | null = null; + public right: TreeNode | null = null; public bounds: BoundingBox; public height: number; - public data?: T; - constructor(public parent?: TreeNode) { - this.parent = parent || undefined; - this.data = undefined; + public data: T | null = null; + constructor(public parent: TreeNode | null = null) { + this.parent = parent || null; + this.data = null; this.bounds = new BoundingBox(); - this.left = undefined; - this.right = undefined; + this.left = null; + this.right = null; this.height = 0; } @@ -45,13 +45,13 @@ export interface ColliderProxy { * Every non-leaf node is a bounding box that contains child bounding boxes. */ export class DynamicTree> { - public root?: TreeNode; + public root: TreeNode | null = null; public nodes: Record>; constructor( private _config: Required, public worldBounds: BoundingBox = new BoundingBox(-Number.MAX_VALUE, -Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE) ) { - this.root = undefined; + this.root = null; this.nodes = {}; } @@ -62,7 +62,7 @@ export class DynamicTree> { // If there are no nodes in the tree, make this the root leaf if (!this.root) { this.root = leaf; - this.root.parent = undefined; + this.root.parent = null; return; } @@ -172,7 +172,7 @@ export class DynamicTree> { */ private _remove(leaf: TreeNode) { if (leaf === this.root) { - this.root = undefined; + this.root = null; return; } @@ -203,7 +203,7 @@ export class DynamicTree> { } } else { this.root = sibling; - sibling.parent = undefined; + sibling.parent = null; } } diff --git a/src/engine/director/director.ts b/src/engine/director/director.ts index 5f01c834..7c3ac5ca 100644 --- a/src/engine/director/director.ts +++ b/src/engine/director/director.ts @@ -80,11 +80,11 @@ export interface GoToOptions { /** * Optionally supply destination scene "in" transition, this will override any previously defined transition */ - destinationIn?: Transition; + destinationIn?: Transition | null; /** * Optionally supply source scene "out" transition, this will override any previously defined transition */ - sourceOut?: Transition; + sourceOut?: Transition | null; /** * Optionally supply a different loader for the destination scene, this will override any previously defined loader */ @@ -102,8 +102,8 @@ export interface GoToOptions { export class Director { public events = new EventEmitter(); private _logger = Logger.getInstance(); - private _deferredGoto?: string; - private _deferredTransition?: Transition; + private _deferredGoto: string | null = null; + private _deferredTransition: Transition | null = null; private _initialized = false; /** @@ -117,7 +117,7 @@ export class Director { /** * Current transition if any */ - currentTransition?: Transition; + currentTransition: Transition | null = null; /** * All registered scenes in Excalibur @@ -181,9 +181,9 @@ export class Director { this._initialized = true; if (this._deferredGoto) { const deferredScene = this._deferredGoto; - this._deferredGoto = undefined; + this._deferredGoto = null; const deferredTransition = this._deferredTransition; - this._deferredTransition = undefined; + this._deferredTransition = null; const deferredSceneInstance = this.getSceneInstance(deferredScene); if (deferredSceneInstance && deferredTransition) { deferredTransition._addToTargetScene(this._engine, deferredSceneInstance); @@ -224,7 +224,7 @@ export class Director { this.mainLoader = new Loader(); } - let maybeStartTransition: Transition | undefined; + let maybeStartTransition: Transition | null = null; if (options?.inTransition) { const { inTransition } = options; @@ -261,24 +261,24 @@ export class Director { return this._sceneToLoader.get(sceneName); } - private _getInTransition(sceneName: string): Transition | undefined { + private _getInTransition(sceneName: string): Transition | null { const sceneOrRoute = this.scenes[sceneName as TKnownScenes]; if (sceneOrRoute instanceof Scene || isSceneConstructor(sceneOrRoute)) { - return undefined; + return null; } - return sceneOrRoute?.transitions?.in; + return sceneOrRoute?.transitions?.in ?? null; } - private _getOutTransition(sceneName: string): Transition | undefined { + private _getOutTransition(sceneName: string): Transition | null { const sceneOrRoute = this.scenes[sceneName as TKnownScenes]; if (sceneOrRoute instanceof Scene || isSceneConstructor(sceneOrRoute)) { - return undefined; + return null; } - return sceneOrRoute?.transitions?.out; + return sceneOrRoute?.transitions?.out ?? null; } getDeferredScene() { - const maybeDeferred = this.getSceneDefinition(this._deferredGoto); + const maybeDeferred = this._deferredGoto ? this.getSceneDefinition(this._deferredGoto) : null; if (this._deferredGoto && maybeDeferred) { return maybeDeferred; } @@ -289,14 +289,14 @@ export class Director { * Returns a scene by name if it exists, might be the constructor and not the instance of a scene * @param name */ - getSceneDefinition(name?: string): Scene | SceneConstructor | undefined { + getSceneDefinition(name?: string): Scene | SceneConstructor | null { const maybeScene = this.scenes[name as TKnownScenes]; if (maybeScene instanceof Scene || isSceneConstructor(maybeScene)) { return maybeScene; } else if (maybeScene) { return maybeScene.scene; } - return undefined; + return null; } /** @@ -497,10 +497,10 @@ export class Director { * This will call any constructors that were given as a definition * @param scene */ - getSceneInstance(scene: string): Scene | undefined { + getSceneInstance(scene: string): Scene | null { const sceneDefinition = this.getSceneDefinition(scene); if (!sceneDefinition) { - return undefined; + return null; } if (this._sceneToInstance.has(scene)) { return this._sceneToInstance.get(scene) as Scene; @@ -564,7 +564,7 @@ export class Director { } this.currentTransition?.kill(); this.currentTransition?.reset(); - this.currentTransition = undefined; + this.currentTransition = null; } /** @@ -587,7 +587,7 @@ export class Director { if (maybeDest) { const previousScene = this.currentScene; const nextScene = maybeDest; - let previousSceneData: any = undefined; + let previousSceneData: any = null; this._logger.debug('Going to scene:', destinationScene); // only deactivate when initialized diff --git a/src/engine/entity-component-system/component.ts b/src/engine/entity-component-system/component.ts index 770a6ce2..92a57405 100644 --- a/src/engine/entity-component-system/component.ts +++ b/src/engine/entity-component-system/component.ts @@ -155,7 +155,7 @@ export abstract class Component { } // Skip non-serializable objects - return undefined; + return null; } protected _deserializeValue(data: any, existingValue?: any): any { diff --git a/src/engine/graphics/context/excalibur-graphics-context-2d-canvas.ts b/src/engine/graphics/context/excalibur-graphics-context-2d-canvas.ts index 33532278..7df3836d 100644 --- a/src/engine/graphics/context/excalibur-graphics-context-2d-canvas.ts +++ b/src/engine/graphics/context/excalibur-graphics-context-2d-canvas.ts @@ -116,11 +116,14 @@ export interface ExcaliburGraphicsContext2DOptions extends ExcaliburGraphicsCont } export class ExcaliburGraphicsContext2DCanvas implements ExcaliburGraphicsContext { + private _ctx: CanvasRenderingContext2D | null = null; /** * Meant for internal use only. Access the internal context at your own risk and no guarantees this will exist in the future. * @internal */ - public __ctx!: CanvasRenderingContext2D; + public get __ctx(): CanvasRenderingContext2D { + return this._ctx!; + } public get width() { return this.__ctx.canvas.width; } @@ -171,12 +174,12 @@ export class ExcaliburGraphicsContext2DCanvas implements ExcaliburGraphicsContex constructor(options: ExcaliburGraphicsContext2DOptions) { const { canvasElement, context, enableTransparency, snapToPixel, antialiasing: smoothing, backgroundColor } = options; - this.__ctx = + this._ctx = context ?? (canvasElement.getContext('2d', { alpha: enableTransparency ?? true }) as CanvasRenderingContext2D); - if (!this.__ctx) { + if (!this._ctx) { throw new Error('Cannot build new ExcaliburGraphicsContext2D for some reason!'); } this.backgroundColor = backgroundColor ?? this.backgroundColor; @@ -381,9 +384,9 @@ export class ExcaliburGraphicsContext2DCanvas implements ExcaliburGraphicsContex // WebGL only - no-op in Canvas 2D fallback } - public get(_rendererName: string): RendererPlugin | undefined { + public get(_rendererName: string): RendererPlugin | null { // WebGL only - no renderers in Canvas 2D fallback - return undefined; + return null; } public draw(_rendererName: TRenderer['type'], ..._args: Parameters): void { @@ -427,6 +430,6 @@ export class ExcaliburGraphicsContext2DCanvas implements ExcaliburGraphicsContex } dispose(): void { - this.__ctx = undefined as any; + this._ctx = null; } } diff --git a/src/engine/graphics/context/excalibur-graphics-context-webgl.ts b/src/engine/graphics/context/excalibur-graphics-context-webgl.ts index e142ab37..afa00a8d 100644 --- a/src/engine/graphics/context/excalibur-graphics-context-webgl.ts +++ b/src/engine/graphics/context/excalibur-graphics-context-webgl.ts @@ -267,7 +267,7 @@ export class ExcaliburGraphicsContextWebGL implements ExcaliburGraphicsContext { return this._drawTarget; } - private _screenQuadBuffer?: VertexBuffer; + private _screenQuadBuffer: VertexBuffer | null = null; /** * Shared static clip-space quad (interleaved [x, y, u, v] * 6 vertices) used by every * {@apilink ShaderPass} on this context, meant for internal use only. @@ -504,7 +504,7 @@ export class ExcaliburGraphicsContextWebGL implements ExcaliburGraphicsContext { this._drawTarget = this._msaaTarget ?? this._renderTarget; // drop the shared screen quad so it is rebuilt against a restored context - this._screenQuadBuffer = undefined; + this._screenQuadBuffer = null; this.debug = new ExcaliburGraphicsContextWebGLDebug(this); @@ -522,7 +522,7 @@ export class ExcaliburGraphicsContextWebGL implements ExcaliburGraphicsContext { this._lazyRenderersFactory.set(type, renderer); } - public get(rendererName: string): RendererPlugin | undefined { + public get(rendererName: string): RendererPlugin | null { let maybeRenderer = this._renderers.get(rendererName); if (!maybeRenderer) { const lazyFactory = this._lazyRenderersFactory.get(rendererName); @@ -532,7 +532,7 @@ export class ExcaliburGraphicsContextWebGL implements ExcaliburGraphicsContext { this.register(maybeRenderer); } } - return maybeRenderer; + return maybeRenderer ?? null; } private _currentRenderer: RendererPlugin | undefined; diff --git a/src/engine/graphics/context/excalibur-graphics-context.ts b/src/engine/graphics/context/excalibur-graphics-context.ts index d1caa5df..2407b335 100644 --- a/src/engine/graphics/context/excalibur-graphics-context.ts +++ b/src/engine/graphics/context/excalibur-graphics-context.ts @@ -470,9 +470,9 @@ export interface ExcaliburGraphicsContext { * on first access. * * @param rendererName The unique renderer type name - * @returns The renderer plugin, or `undefined` if not found + * @returns The renderer plugin, or `null` if not found */ - get(rendererName: string): RendererPlugin | undefined; + get(rendererName: string): RendererPlugin | null; /** * Issue a draw command to a named renderer plugin. diff --git a/src/engine/graphics/context/image-renderer-v2/image-renderer-v2.ts b/src/engine/graphics/context/image-renderer-v2/image-renderer-v2.ts index ec2d37e8..6020873f 100644 --- a/src/engine/graphics/context/image-renderer-v2/image-renderer-v2.ts +++ b/src/engine/graphics/context/image-renderer-v2/image-renderer-v2.ts @@ -219,7 +219,7 @@ export class ImageRendererV2 implements RendererPlugin { return; } const maybeFiltering = image.getAttribute(ImageSourceAttributeConstants.Filtering); - const filtering = maybeFiltering ? parseImageFiltering(maybeFiltering) : undefined; + const filtering = maybeFiltering ? parseImageFiltering(maybeFiltering) : null; const wrapX = parseImageWrapping(image.getAttribute(ImageSourceAttributeConstants.WrappingX) as any); const wrapY = parseImageWrapping(image.getAttribute(ImageSourceAttributeConstants.WrappingY) as any); diff --git a/src/engine/graphics/context/image-renderer/image-renderer.ts b/src/engine/graphics/context/image-renderer/image-renderer.ts index e79bcec6..c04837e8 100644 --- a/src/engine/graphics/context/image-renderer/image-renderer.ts +++ b/src/engine/graphics/context/image-renderer/image-renderer.ts @@ -130,7 +130,7 @@ export class ImageRenderer implements RendererPlugin { return; } const maybeFiltering = image.getAttribute(ImageSourceAttributeConstants.Filtering); - const filtering = maybeFiltering ? parseImageFiltering(maybeFiltering) : undefined; + const filtering = maybeFiltering ? parseImageFiltering(maybeFiltering) : null; const wrapX = parseImageWrapping(image.getAttribute(ImageSourceAttributeConstants.WrappingX) as any); const wrapY = parseImageWrapping(image.getAttribute(ImageSourceAttributeConstants.WrappingY) as any); diff --git a/src/engine/graphics/context/material-renderer/material-renderer.ts b/src/engine/graphics/context/material-renderer/material-renderer.ts index 3c8e2b43..e3d8e719 100644 --- a/src/engine/graphics/context/material-renderer/material-renderer.ts +++ b/src/engine/graphics/context/material-renderer/material-renderer.ts @@ -163,7 +163,7 @@ export class MaterialRenderer implements RendererPlugin { const seedWidth = sw + 2 * pad; const seedHeight = sh + 2 * pad; const maybeFiltering = image.getAttribute(ImageSourceAttributeConstants.Filtering); - const graphicFiltering = maybeFiltering ? parseImageFiltering(maybeFiltering) : undefined; + const graphicFiltering = maybeFiltering ? parseImageFiltering(maybeFiltering) : null; const seed = material.getSeedFramebuffer(seedWidth, seedHeight); const output = material.getOutputFramebuffer(seedWidth, seedHeight, graphicFiltering); @@ -296,7 +296,7 @@ export class MaterialRenderer implements RendererPlugin { private _addImageAsTexture(image: HTMLImageSource) { const maybeFiltering = image.getAttribute(ImageSourceAttributeConstants.Filtering); - const filtering = maybeFiltering ? parseImageFiltering(maybeFiltering) : undefined; + const filtering = maybeFiltering ? parseImageFiltering(maybeFiltering) : null; const wrapX = parseImageWrapping(image.getAttribute(ImageSourceAttributeConstants.WrappingX) as any); const wrapY = parseImageWrapping(image.getAttribute(ImageSourceAttributeConstants.WrappingY) as any); diff --git a/src/engine/graphics/context/material.ts b/src/engine/graphics/context/material.ts index 922f3896..3615626c 100644 --- a/src/engine/graphics/context/material.ts +++ b/src/engine/graphics/context/material.ts @@ -232,10 +232,10 @@ export class Material { private _graphicsContext?: ExcaliburGraphicsContextWebGL; private _passes?: ShaderPassLike[] | ShaderPipelineLike; - private _pipeline?: ShaderPipelineLike; + private _pipeline: ShaderPipelineLike | null = null; private _padding: number = 0; private _seedFramebuffer?: Framebuffer; - private _outputFramebuffer?: Framebuffer; + private _outputFramebuffer: Framebuffer | null = null; constructor(options: MaterialOptions) { const { color, name, vertexSource, fragmentSource, passes, padding, graphicsContext, images, uniforms } = options; @@ -375,7 +375,7 @@ export class Material { /** * The multipass pipeline run on the graphic before the composite fragment, if any */ - get pipeline(): ShaderPipelineLike | undefined { + get pipeline(): ShaderPipelineLike | null { return this._pipeline; } @@ -408,11 +408,11 @@ export class Material { * Lazily creates/resizes the final output for compositing * @internal */ - public getOutputFramebuffer(width: number, height: number, filtering?: ImageFiltering): Framebuffer { + public getOutputFramebuffer(width: number, height: number, filtering?: ImageFiltering | null): Framebuffer { const resolvedFiltering = filtering ?? this._outputFramebuffer?.filtering ?? ImageFiltering.Blended; if (this._outputFramebuffer && this._outputFramebuffer.filtering !== resolvedFiltering) { this._outputFramebuffer.dispose(); - this._outputFramebuffer = undefined; + this._outputFramebuffer = null; } if (!this._outputFramebuffer) { this._outputFramebuffer = new Framebuffer({ diff --git a/src/engine/graphics/context/particle-renderer/particle-renderer.ts b/src/engine/graphics/context/particle-renderer/particle-renderer.ts index aa561df1..1eac0eb6 100644 --- a/src/engine/graphics/context/particle-renderer/particle-renderer.ts +++ b/src/engine/graphics/context/particle-renderer/particle-renderer.ts @@ -41,7 +41,7 @@ export class ParticleRenderer implements RendererPlugin { private _getTexture(image: HTMLImageSource) { const maybeFiltering = image.getAttribute(ImageSourceAttributeConstants.Filtering); - const filtering = maybeFiltering ? parseImageFiltering(maybeFiltering) : undefined; + const filtering = maybeFiltering ? parseImageFiltering(maybeFiltering) : null; const wrapX = parseImageWrapping(image.getAttribute(ImageSourceAttributeConstants.WrappingX) as any); const wrapY = parseImageWrapping(image.getAttribute(ImageSourceAttributeConstants.WrappingY) as any); diff --git a/src/engine/graphics/context/shader-pipeline/shader-pass.ts b/src/engine/graphics/context/shader-pipeline/shader-pass.ts index abcea789..f60fdadc 100644 --- a/src/engine/graphics/context/shader-pipeline/shader-pass.ts +++ b/src/engine/graphics/context/shader-pipeline/shader-pass.ts @@ -160,9 +160,9 @@ export class ShaderPass { private _graphicsContext: ExcaliburGraphicsContextWebGL; private _fragmentSource: string; - private _initialUniforms?: UniformDictionary; - private _shader?: Shader; - private _layout?: VertexLayout; + private _initialUniforms: UniformDictionary | null = null; + private _shader: Shader | null = null; + private _layout: VertexLayout | null = null; private _disposed = false; constructor(options: ShaderPassOptions) { @@ -170,7 +170,7 @@ export class ShaderPass { this._graphicsContext = graphicsContext; this.name = name ?? 'anonymous shader pass'; this._fragmentSource = fragmentSource ? fragmentSource : defaultPassthroughFragment; - this._initialUniforms = uniforms; + this._initialUniforms = uniforms ?? null; this.scale = scale ?? 1; this.filtering = filtering ?? ImageFiltering.Blended; @@ -207,7 +207,7 @@ export class ShaderPass { ], suppressWarnings: true }); - this._initialUniforms = undefined; + this._initialUniforms = null; } /** @@ -243,7 +243,7 @@ export class ShaderPass { } const image = source.image; const maybeFiltering = image.getAttribute(ImageSourceAttributeConstants.Filtering); - const filtering = maybeFiltering ? parseImageFiltering(maybeFiltering) : undefined; + const filtering = maybeFiltering ? parseImageFiltering(maybeFiltering) : null; const wrapX = parseImageWrapping(image.getAttribute(ImageSourceAttributeConstants.WrappingX) as any); const wrapY = parseImageWrapping(image.getAttribute(ImageSourceAttributeConstants.WrappingY) as any); const texture = this._graphicsContext.textureLoader.load(image, { filtering, wrapping: { x: wrapX, y: wrapY } }); @@ -345,8 +345,8 @@ export class ShaderPass { if (!this._disposed) { this._disposed = true; this._shader?.dispose(); - this._shader = undefined; - this._layout = undefined; + this._shader = null; + this._layout = null; } } } diff --git a/src/engine/graphics/context/shader.ts b/src/engine/graphics/context/shader.ts index 2268a570..7bb96a70 100644 --- a/src/engine/graphics/context/shader.ts +++ b/src/engine/graphics/context/shader.ts @@ -268,7 +268,7 @@ export interface ShaderOptions { /** * Set initial uniforms */ - uniforms?: UniformDictionary; + uniforms?: UniformDictionary | null; /** * Set initial images as uniform sampler2D @@ -519,7 +519,7 @@ export class Shader { private _loadImageSource(image: ImageSource): WebGLTexture | null { const imageElement = image.image; const maybeFiltering = imageElement.getAttribute(ImageSourceAttributeConstants.Filtering); - const filtering = maybeFiltering ? parseImageFiltering(maybeFiltering) : undefined; + const filtering = maybeFiltering ? parseImageFiltering(maybeFiltering) : null; const wrapX = parseImageWrapping(imageElement.getAttribute(ImageSourceAttributeConstants.WrappingX) as any); const wrapY = parseImageWrapping(imageElement.getAttribute(ImageSourceAttributeConstants.WrappingY) as any); diff --git a/src/engine/graphics/filtering.ts b/src/engine/graphics/filtering.ts index 8267a9d0..040f941b 100644 --- a/src/engine/graphics/filtering.ts +++ b/src/engine/graphics/filtering.ts @@ -18,13 +18,13 @@ export enum ImageFiltering { /** * Parse the image filtering attribute value, if it doesn't match returns null */ -export function parseImageFiltering(val: string): ImageFiltering | undefined { +export function parseImageFiltering(val: string): ImageFiltering | null { switch (val) { case ImageFiltering.Pixel: return ImageFiltering.Pixel; case ImageFiltering.Blended: return ImageFiltering.Blended; default: - return undefined; + return null; } } diff --git a/src/engine/graphics/font-text-instance.ts b/src/engine/graphics/font-text-instance.ts index 7b73bb0d..3e200f92 100644 --- a/src/engine/graphics/font-text-instance.ts +++ b/src/engine/graphics/font-text-instance.ts @@ -7,10 +7,10 @@ import { vec, Vector } from '../math'; import { combineHashes, hashString } from '../util/string'; export class FontTextInstance { - public canvas: HTMLCanvasElement; - public ctx: CanvasRenderingContext2D; + public canvas: HTMLCanvasElement | null = null; + public ctx: CanvasRenderingContext2D | null = null; private _textFragments: { x: number; y: number; canvas: HTMLCanvasElement }[] = []; - public dimensions: BoundingBox; + public dimensions: BoundingBox | null = null; public disposed: boolean = false; private _lastHashCode: number; /** @@ -57,13 +57,13 @@ export class FontTextInstance { lines = text.split('\n'); } - this._applyFont(this.ctx); // font must be applied to the context to measure it + this._applyFont(this.ctx!); // font must be applied to the context to measure it let maxWidthLine = 0; let maxAscent = 0; let maxDescent = 0; const adjustedPadding = this.font.padding / this.font.quality; for (let i = 0; i < lines.length; i++) { - const metrics = this.ctx.measureText(lines[i]); + const metrics = this.ctx!.measureText(lines[i]); const width = metrics.width + adjustedPadding * 2; maxWidthLine = Math.max(maxWidthLine, width); maxAscent = Math.max(maxAscent, metrics.actualBoundingBoxAscent); @@ -137,14 +137,14 @@ export class FontTextInstance { switch (this.font.textAlign) { case 'left': case 'start': - x = ltr ? 0 : this.canvas.width; + x = ltr ? 0 : this.canvas!.width; break; case 'center': - x = this.canvas.width / 2; + x = this.canvas!.width / 2; break; case 'right': case 'end': - x = ltr ? this.canvas.width : 0; + x = ltr ? this.canvas!.width : 0; break; default: x = 0; @@ -281,13 +281,13 @@ export class FontTextInstance { // Calculate image chunks if (this._dirty) { this.dimensions = this.measureText(this.text, maxWidth); - this._setDimension(this.dimensions, this.ctx); + this._setDimension(this.dimensions, this.ctx!); const lines = this._getLinesFromText(this.text, maxWidth); const lineHeight = !this.font.lineHeight ? (this.dimensions.height - this._maxAscent) / lines.length : this.font.lineHeight; // draws the text to the main bitmap - this._drawText(this.ctx, lines, lineHeight); + this._drawText(this.ctx!, lines, lineHeight); // clear any out old fragments if (ex instanceof ExcaliburGraphicsContextWebGL) { @@ -297,7 +297,7 @@ export class FontTextInstance { } // splits to < 4k fragments for large text - this._textFragments = this._splitTextBitmap(this.ctx); + this._textFragments = this._splitTextBitmap(this.ctx!); if (ex instanceof ExcaliburGraphicsContextWebGL) { for (const frag of this._textFragments) { @@ -309,7 +309,7 @@ export class FontTextInstance { } const adjustedPadding = this.font.padding / this.font.quality; // text space - const destWidth = this.canvas.width / this.font.quality - adjustedPadding; // text space + const destWidth = this.canvas!.width / this.font.quality - adjustedPadding; // text space const destHeight = this._totalHeight; // text space const alignmentFromAnchor = this._xAnchorFromAlignment() * destWidth + adjustedPadding; @@ -336,9 +336,9 @@ export class FontTextInstance { dispose() { this.disposed = true; - this.dimensions = undefined as any; - this.canvas = undefined as any; - this.ctx = undefined as any; + this.dimensions = null; + this.canvas = null; + this.ctx = null; if (this._ex instanceof ExcaliburGraphicsContextWebGL) { for (const frag of this._textFragments) { this._ex.textureLoader.delete(frag.canvas); diff --git a/src/engine/graphics/font.ts b/src/engine/graphics/font.ts index eddc5923..c6997474 100644 --- a/src/engine/graphics/font.ts +++ b/src/engine/graphics/font.ts @@ -130,7 +130,7 @@ export class Font extends Graphic implements FontRenderer { /** * Font line height in pixels, default line height if unset */ - public lineHeight: number | undefined = undefined; + public lineHeight: number | null = null; public size: number = 10; public shadow?: { blur?: number; offset?: Vector; color?: Color }; @@ -193,7 +193,7 @@ export class Font extends Graphic implements FontRenderer { const textInstance = FontCache.getTextInstance(text, this, colorOverride); // Apply affine transformations - this._textBounds = textInstance.dimensions; + this._textBounds = textInstance.dimensions!; this._preDraw(ex, x, y); textInstance.render(ex, x, y, maxWidth); diff --git a/src/engine/graphics/graphics-component.ts b/src/engine/graphics/graphics-component.ts index 674a1281..9a00311c 100644 --- a/src/engine/graphics/graphics-component.ts +++ b/src/engine/graphics/graphics-component.ts @@ -39,7 +39,7 @@ export interface GraphicsComponentData { offset?: { x: number; y: number }; anchor?: { x: number; y: number }; } - | undefined; + | null; }; isVisible: boolean; opacity: number; @@ -122,7 +122,7 @@ export class GraphicsComponent extends Component { private _current: string = 'default'; private _graphics: Record = {}; - private _options: Record = {}; + private _options: Record = {}; public material: Material | null = null; @@ -134,7 +134,7 @@ export class GraphicsComponent extends Component { /** * Draws after the entity transform has been applied, and after graphics component graphics has been drawn */ - public onPostDraw?: (ctx: ExcaliburGraphicsContext, elapsed: number) => void; + public onPostDraw: ((ctx: ExcaliburGraphicsContext, elapsed: number) => void) | null = null; /** * Draws before the entity transform has been applied before any any graphics component drawing @@ -260,7 +260,7 @@ export class GraphicsComponent extends Component { this._graphics[key] = graphicOrOptions; } else { this._graphics[key] = graphicOrOptions.graphic; - this._options[key] = graphicOrOptions.options; + this._options[key] = graphicOrOptions.options ?? null; } } @@ -285,7 +285,7 @@ export class GraphicsComponent extends Component { public getGraphic(name: string): Graphic | undefined { return this._graphics[name]; } - public getOptions(name: string): GraphicsShowOptions | undefined { + public getOptions(name: string): GraphicsShowOptions | null { return this._options[name]; } @@ -306,7 +306,7 @@ export class GraphicsComponent extends Component { /** * Returns the currently displayed graphic offsets */ - public get currentOptions(): GraphicsShowOptions | undefined { + public get currentOptions(): GraphicsShowOptions | null { return this._options[this._current]; } @@ -320,7 +320,7 @@ export class GraphicsComponent extends Component { /** * Returns all graphics options associated with this component */ - public get options(): { [graphicName: string]: GraphicsShowOptions | undefined } { + public get options(): { [graphicName: string]: GraphicsShowOptions | null } { return this._options; } @@ -333,15 +333,15 @@ export class GraphicsComponent extends Component { public add(nameOrGraphic: string | Graphic, graphicOrOptions?: Graphic | GraphicsShowOptions, options?: GraphicsShowOptions): Graphic { let name = 'default'; let graphicToSet: Graphic | null = null; - let optionsToSet: GraphicsShowOptions | undefined = undefined; + let optionsToSet: GraphicsShowOptions | null = null; if (typeof nameOrGraphic === 'string' && graphicOrOptions instanceof Graphic) { name = nameOrGraphic; graphicToSet = graphicOrOptions; - optionsToSet = options; + optionsToSet = options ?? null; } if (nameOrGraphic instanceof Graphic && !(graphicOrOptions instanceof Graphic)) { graphicToSet = nameOrGraphic; - optionsToSet = graphicOrOptions; + optionsToSet = graphicOrOptions ?? null; } if (!graphicToSet) { @@ -383,10 +383,10 @@ export class GraphicsComponent extends Component { } this._current = 'default'; this._graphics[this._current] = graphic; - this._options[this._current] = options; + this._options[this._current] = options ?? null; } else { this._current = nameOrGraphic; - this._options[this._current] = options; + this._options[this._current] = options ?? null; if (!(this._current in this._graphics)) { this._logger.warn( `Graphic ${this._current} is not registered with the graphics component owned by ${this.owner?.name}. Nothing will be drawn.` @@ -575,7 +575,7 @@ export class GraphicsComponent extends Component { anchor: option.anchor ? { x: option.anchor.x, y: option.anchor.y } : undefined }; } else { - data.options[name] = undefined; + data.options[name] = null; } } @@ -632,7 +632,7 @@ export class GraphicsComponent extends Component { anchor: option.anchor ? ({ x: option.anchor.x, y: option.anchor.y } as Vector) : undefined }; } else { - this._options[name] = undefined; + this._options[name] = null; } } } diff --git a/src/engine/graphics/image-source.ts b/src/engine/graphics/image-source.ts index f28b69c2..4509554a 100644 --- a/src/engine/graphics/image-source.ts +++ b/src/engine/graphics/image-source.ts @@ -10,7 +10,7 @@ import { ImageWrapping } from './wrapping'; import type { GraphicOptions } from './graphic'; export interface ImageSourceOptions { - filtering?: ImageFiltering; + filtering?: ImageFiltering | null; wrapping?: ImageWrapConfiguration | ImageWrapping; bustCache?: boolean; } @@ -89,7 +89,7 @@ export class ImageSource implements Loadable { * @param filtering {ImageFiltering} Optionally override the image filtering set by {@apilink EngineOptions.antialiasing} */ constructor(pathOrBase64: string, bustCache: boolean, filtering?: ImageFiltering); - constructor(pathOrBase64: string, bustCacheOrOptions: boolean | ImageSourceOptions | undefined, filtering?: ImageFiltering) { + constructor(pathOrBase64: string, bustCacheOrOptions: boolean | ImageSourceOptions | undefined, filtering?: ImageFiltering | null) { this.path = pathOrBase64; let bustCache: boolean | undefined = false; let wrapping: ImageWrapConfiguration | ImageWrapping | undefined; diff --git a/src/engine/graphics/post-processor/shader-pipeline-post-processor.ts b/src/engine/graphics/post-processor/shader-pipeline-post-processor.ts index e2ac6fc7..92ebbf05 100644 --- a/src/engine/graphics/post-processor/shader-pipeline-post-processor.ts +++ b/src/engine/graphics/post-processor/shader-pipeline-post-processor.ts @@ -34,7 +34,7 @@ export interface ShaderPipelinePostProcessorOptions { export class ShaderPipelinePostProcessor implements PostProcessor { public readonly name: string; private _passes: ShaderPassLike[] | ShaderPipelineLike; - private _pipeline?: ShaderPipelineLike; + private _pipeline: ShaderPipelineLike | null = null; private _elapsed = 0; constructor(options: ShaderPipelinePostProcessorOptions) { @@ -83,6 +83,6 @@ export class ShaderPipelinePostProcessor implements PostProcessor { public dispose(): void { this._pipeline?.dispose?.(); - this._pipeline = undefined; + this._pipeline = null; } } diff --git a/src/engine/graphics/sprite-font.ts b/src/engine/graphics/sprite-font.ts index ba2b3f90..c71c0187 100644 --- a/src/engine/graphics/sprite-font.ts +++ b/src/engine/graphics/sprite-font.ts @@ -41,10 +41,10 @@ export class SpriteFont extends Graphic implements FontRenderer { public alphabet: string = ''; public spriteSheet: SpriteSheet; - public shadow?: { offset: Vector } = undefined; + public shadow: { offset: Vector } | null = null; public caseInsensitive = false; public spacing: number = 0; - public lineHeight: number | undefined = undefined; + public lineHeight: number | null = null; private _logger = Logger.getInstance(); diff --git a/src/engine/input/gamepad.ts b/src/engine/input/gamepad.ts index d95f94dd..b43c3c5e 100644 --- a/src/engine/input/gamepad.ts +++ b/src/engine/input/gamepad.ts @@ -100,13 +100,9 @@ export class Gamepads { if (!pad) { return false; } - const axesLength = pad.axes.filter((value) => { - return typeof value !== undefined; - }).length; + const axesLength = pad.axes.length; - const buttonLength = pad.buttons.filter((value) => { - return typeof value !== undefined; - }).length; + const buttonLength = pad.buttons.length; return axesLength >= this._minimumConfiguration.axis && buttonLength >= this._minimumConfiguration.buttons && pad.connected; } diff --git a/src/engine/particles/particles.ts b/src/engine/particles/particles.ts index a95337b7..39b817aa 100644 --- a/src/engine/particles/particles.ts +++ b/src/engine/particles/particles.ts @@ -117,7 +117,7 @@ export class Particle extends Entity { } if (this.graphic) { this.graphics.use(this.graphic); - this.graphics.onPostDraw = undefined; + this.graphics.onPostDraw = null; } else { this.graphics.localBounds = BoundingBox.fromDimension(this.size, this.size, Vector.Half); this.graphics.onPostDraw = (ctx) => { diff --git a/src/engine/resources/sound/sound-manager.ts b/src/engine/resources/sound/sound-manager.ts index a27b3e79..0b5823e9 100644 --- a/src/engine/resources/sound/sound-manager.ts +++ b/src/engine/resources/sound/sound-manager.ts @@ -214,10 +214,10 @@ export class SoundManager impl return sound.play(effectiveVolume) as unknown as Promise; } - public getSound(soundName: SoundName | AnyString): Sound | undefined { + public getSound(soundName: SoundName | AnyString): Sound | null { const soundSound = this._nameToConfig.get(soundName); if (!soundSound) { - return undefined; + return null; } const { sound } = soundSound; diff --git a/src/engine/scene.ts b/src/engine/scene.ts index b3fe3b4c..73ac448e 100644 --- a/src/engine/scene.ts +++ b/src/engine/scene.ts @@ -253,9 +253,9 @@ export class Scene implements CanInitialize, CanActiv * * This can be used to configure custom transitions for a scene dynamically */ - public onTransition(direction: 'in' | 'out'): Transition | undefined { + public onTransition(direction: 'in' | 'out'): Transition | null { // will be overridden - return undefined; + return null; } /** @@ -702,7 +702,7 @@ export class Scene implements CanInitialize, CanActiv * @param entity */ public transfer(entity: any): void { - let scene: Scene | undefined = undefined; + let scene: Scene | null = null; if (entity instanceof Entity && entity.scene && entity.scene !== this) { scene = entity.scene; entity.scene.world.remove(entity, false); diff --git a/src/engine/util/log.ts b/src/engine/util/log.ts index 3299f0b9..72180a28 100644 --- a/src/engine/util/log.ts +++ b/src/engine/util/log.ts @@ -21,7 +21,7 @@ export enum LogLevel { * Derive from {@apilink Appender} to create your own logging appenders. */ export class Logger { - private static _INSTANCE?: Logger = undefined; + private static _INSTANCE: Logger | null = null; private _appenders: Appender[] = []; constructor() { diff --git a/src/engine/util/serializer.ts b/src/engine/util/serializer.ts index d98b8b14..e4c36151 100644 --- a/src/engine/util/serializer.ts +++ b/src/engine/util/serializer.ts @@ -35,7 +35,7 @@ export interface EntityData extends SerializedData { tags: string[]; components: ComponentData[]; children: EntityData[]; - customInstance?: string; + customInstance?: string | null; } /** @@ -428,7 +428,7 @@ export class Serializer { // is actor custom actor - let customInstance: string | undefined = undefined; + let customInstance: string | null = null; for (const [key, ctor] of Serializer._ACTORREGISTRY.entries()) { if (actor instanceof ctor) { diff --git a/src/spec/vitest/body-component-spec.ts b/src/spec/vitest/body-component-spec.ts index 9f4c2a36..46cbbd48 100644 --- a/src/spec/vitest/body-component-spec.ts +++ b/src/spec/vitest/body-component-spec.ts @@ -24,8 +24,8 @@ describe('A body component', () => { expect((actor.body as any)._cachedInverseInertia).toBeCloseTo(0.0006, 0); actor.body.mass = 1; - expect((actor.body as any)._cachedInertia).toBe(undefined); - expect((actor.body as any)._cachedInverseInertia).toBe(undefined); + expect((actor.body as any)._cachedInertia).toBeNull(); + expect((actor.body as any)._cachedInverseInertia).toBeNull(); }); it('will reflect the transform positions', () => { diff --git a/src/spec/vitest/director-spec.ts b/src/spec/vitest/director-spec.ts index 46417945..c2a117b8 100644 --- a/src/spec/vitest/director-spec.ts +++ b/src/spec/vitest/director-spec.ts @@ -187,17 +187,17 @@ describe('A Director', () => { }); sut.remove('scene1'); - expect(sut.getSceneDefinition('scene1')).toBe(undefined); - expect(sut.getSceneInstance('scene1')).toBe(undefined); + expect(sut.getSceneDefinition('scene1')).toBeNull(); + expect(sut.getSceneInstance('scene1')).toBeNull(); sut.remove('scene2'); - expect(sut.getSceneDefinition('scene2')).toBe(undefined); - expect(sut.getSceneInstance('scene2')).toBe(undefined); + expect(sut.getSceneDefinition('scene2')).toBeNull(); + expect(sut.getSceneInstance('scene2')).toBeNull(); sut.remove('scene3'); - expect(sut.getSceneDefinition('scene3')).toBe(undefined); - expect(sut.getSceneInstance('scene3')).toBe(undefined); + expect(sut.getSceneDefinition('scene3')).toBeNull(); + expect(sut.getSceneInstance('scene3')).toBeNull(); sut.remove('scene4'); - expect(sut.getSceneDefinition('scene4')).toBe(undefined); - expect(sut.getSceneInstance('scene4')).toBe(undefined); + expect(sut.getSceneDefinition('scene4')).toBeNull(); + expect(sut.getSceneInstance('scene4')).toBeNull(); engine.dispose(); }); diff --git a/src/spec/vitest/dynamic-tree-spec.ts b/src/spec/vitest/dynamic-tree-spec.ts index 8fe7c522..fe78f355 100644 --- a/src/spec/vitest/dynamic-tree-spec.ts +++ b/src/spec/vitest/dynamic-tree-spec.ts @@ -20,7 +20,7 @@ describe('A DynamicTree', () => { it('can be constructed with default world bounds', () => { const tree = new ex.DynamicTree(defaultConfig); - expect(tree.root).toBeUndefined(); + expect(tree.root).toBeNull(); expect(tree.getHeight()).toBe(0); expect(tree.getNodes()).toEqual([]); }); diff --git a/src/spec/vitest/material-pipeline-spec.ts b/src/spec/vitest/material-pipeline-spec.ts index 6e8affdb..91e04ad0 100644 --- a/src/spec/vitest/material-pipeline-spec.ts +++ b/src/spec/vitest/material-pipeline-spec.ts @@ -92,7 +92,7 @@ describe('A Material with a shader pipeline', () => { void main() { color = vec4(1.0, 0.0, 0.0, 1.0); }` }); - expect(material.pipeline).toBeUndefined(); + expect(material.pipeline).toBeNull(); }); it('resolves u_image as a synonym for u_graphic with no pipeline', async () => { -- 2.51.2