From c6315ebb2929b2116c3365f860c1e20123de4efe Mon Sep 17 00:00:00 2001 From: Yorick van Klinken Date: Fri, 20 Sep 2024 15:00:38 +0200 Subject: [PATCH] feat: static System priority (#3207) Closes #3102 ## Changes: - Refactored `System.priority` to be static - Updated `priority` in all systems - Updated priority sort in `SystemManager` --- CHANGELOG.md | 1 + src/engine/Actions/ActionsSystem.ts | 3 +- src/engine/Collision/CollisionSystem.ts | 3 +- src/engine/Collision/MotionSystem.ts | 3 +- src/engine/Debug/DebugSystem.ts | 3 +- src/engine/EntityComponentSystem/System.ts | 2 +- .../EntityComponentSystem/SystemManager.ts | 2 +- src/engine/Graphics/GraphicsSystem.ts | 3 +- src/engine/Graphics/OffscreenSystem.ts | 3 +- src/engine/Input/PointerSystem.ts | 3 +- src/engine/TileMap/IsometricEntitySystem.ts | 3 +- src/spec/SystemManagerSpec.ts | 60 +++++++++++++++---- 12 files changed, 66 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3729ae263..e1d1c3486 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Breaking Changes +- `System.priority` is refactored to be static. - `ex.Timer` now only takes the option bag constructor - `PreDrawEvent`, `PostDrawEvent`, `PreTransformDrawEvent`, `PostTransformDrawEvent`, `PreUpdateEvent`, `PostUpdateEvent` now use `elapsedMs` instead of `delta` for the elapsed milliseconds between the last frame.460696 - `Trigger` API has been slightly changed: diff --git a/src/engine/Actions/ActionsSystem.ts b/src/engine/Actions/ActionsSystem.ts index 9b1cf400e..8ebde3c38 100644 --- a/src/engine/Actions/ActionsSystem.ts +++ b/src/engine/Actions/ActionsSystem.ts @@ -3,8 +3,9 @@ import { System, SystemType } from '../EntityComponentSystem/System'; import { ActionsComponent } from './ActionsComponent'; export class ActionsSystem extends System { + static priority = SystemPriority.Higher; + systemType = SystemType.Update; - priority = SystemPriority.Higher; private _actions: ActionsComponent[] = []; query: Query; diff --git a/src/engine/Collision/CollisionSystem.ts b/src/engine/Collision/CollisionSystem.ts index 257e3e362..eb82a187d 100644 --- a/src/engine/Collision/CollisionSystem.ts +++ b/src/engine/Collision/CollisionSystem.ts @@ -21,8 +21,9 @@ import { SeparatingAxis } from './Colliders/SeparatingAxis'; import { MotionSystem } from './MotionSystem'; import { Pair } from './Detection/Pair'; export class CollisionSystem extends System { + static priority = SystemPriority.Higher; + public systemType = SystemType.Update; - public priority = SystemPriority.Higher; public query: Query | ComponentCtor | ComponentCtor>; private _engine: Engine; diff --git a/src/engine/Collision/MotionSystem.ts b/src/engine/Collision/MotionSystem.ts index 2a9cef3b6..f593fc8f4 100644 --- a/src/engine/Collision/MotionSystem.ts +++ b/src/engine/Collision/MotionSystem.ts @@ -8,8 +8,9 @@ import { EulerIntegrator } from './Integrator'; import { PhysicsWorld } from './PhysicsWorld'; export class MotionSystem extends System { + static priority = SystemPriority.Higher; + public systemType = SystemType.Update; - public priority = SystemPriority.Higher; private _physicsConfigDirty = false; query: Query; constructor( diff --git a/src/engine/Debug/DebugSystem.ts b/src/engine/Debug/DebugSystem.ts index 84c678bd2..81e09cff2 100644 --- a/src/engine/Debug/DebugSystem.ts +++ b/src/engine/Debug/DebugSystem.ts @@ -18,8 +18,9 @@ import { CoordPlane } from '../Math/coord-plane'; import { Debug } from '../Graphics/Debug'; export class DebugSystem extends System { + static priority = SystemPriority.Lowest; + public readonly systemType = SystemType.Draw; - public priority = SystemPriority.Lowest; private _graphicsContext: ExcaliburGraphicsContext; private _collisionSystem: CollisionSystem; private _camera: Camera; diff --git a/src/engine/EntityComponentSystem/System.ts b/src/engine/EntityComponentSystem/System.ts index 10277a129..8819ab961 100644 --- a/src/engine/EntityComponentSystem/System.ts +++ b/src/engine/EntityComponentSystem/System.ts @@ -42,7 +42,7 @@ export abstract class System { * For a system to execute before all other a lower priority value (-1 for example) must be set. * For a system to execute after all other a higher priority value (10 for example) must be set. */ - public priority: number = SystemPriority.Average; + public static priority: number = SystemPriority.Average; /** * Optionally specify an initialize handler diff --git a/src/engine/EntityComponentSystem/SystemManager.ts b/src/engine/EntityComponentSystem/SystemManager.ts index 67b761ba3..4189496d0 100644 --- a/src/engine/EntityComponentSystem/SystemManager.ts +++ b/src/engine/EntityComponentSystem/SystemManager.ts @@ -47,7 +47,7 @@ export class SystemManager { } this.systems.push(system); - this.systems.sort((a, b) => a.priority - b.priority); + this.systems.sort((a, b) => (a.constructor as typeof System).priority - (b.constructor as typeof System).priority); // If systems are added and the manager has already been init'd // then immediately init the system if (this.initialized && system.initialize) { diff --git a/src/engine/Graphics/GraphicsSystem.ts b/src/engine/Graphics/GraphicsSystem.ts index 23fa6ded2..3ce88fa4b 100644 --- a/src/engine/Graphics/GraphicsSystem.ts +++ b/src/engine/Graphics/GraphicsSystem.ts @@ -18,8 +18,9 @@ import { blendTransform } from './TransformInterpolation'; import { Graphic } from './Graphic'; export class GraphicsSystem extends System { + static priority = SystemPriority.Average; + public readonly systemType = SystemType.Draw; - public priority = SystemPriority.Average; private _token = 0; // Set in the initialize private _graphicsContext!: ExcaliburGraphicsContext; diff --git a/src/engine/Graphics/OffscreenSystem.ts b/src/engine/Graphics/OffscreenSystem.ts index 457774d99..396ca3df8 100644 --- a/src/engine/Graphics/OffscreenSystem.ts +++ b/src/engine/Graphics/OffscreenSystem.ts @@ -12,8 +12,9 @@ import { BoundingBox } from '../Collision/BoundingBox'; import { Query, SystemPriority, World } from '../EntityComponentSystem'; export class OffscreenSystem extends System { + static priority: number = SystemPriority.Higher; + public systemType = SystemType.Draw; - priority: number = SystemPriority.Higher; private _camera!: Camera; private _screen!: Screen; private _worldBounds!: BoundingBox; diff --git a/src/engine/Input/PointerSystem.ts b/src/engine/Input/PointerSystem.ts index 774658c58..506241a86 100644 --- a/src/engine/Input/PointerSystem.ts +++ b/src/engine/Input/PointerSystem.ts @@ -16,8 +16,9 @@ import { SparseHashGrid } from '../Collision/Detection/SparseHashGrid'; * the {@apilink Collider}'s shape for pointer events. */ export class PointerSystem extends System { + static priority = SystemPriority.Higher; + public readonly systemType = SystemType.Update; - public priority = SystemPriority.Higher; private _engine: Engine; private _receivers: PointerEventReceiver[]; diff --git a/src/engine/TileMap/IsometricEntitySystem.ts b/src/engine/TileMap/IsometricEntitySystem.ts index 40ab2ec61..6575c5e64 100644 --- a/src/engine/TileMap/IsometricEntitySystem.ts +++ b/src/engine/TileMap/IsometricEntitySystem.ts @@ -4,8 +4,9 @@ import { IsometricEntityComponent } from './IsometricEntityComponent'; import { Query, SystemPriority, World } from '../EntityComponentSystem'; export class IsometricEntitySystem extends System { + static priority: number = SystemPriority.Lower; + public readonly systemType = SystemType.Update; - priority: number = SystemPriority.Lower; query: Query; constructor(public world: World) { super(); diff --git a/src/spec/SystemManagerSpec.ts b/src/spec/SystemManagerSpec.ts index 036743617..ea23b25a1 100644 --- a/src/spec/SystemManagerSpec.ts +++ b/src/spec/SystemManagerSpec.ts @@ -5,11 +5,45 @@ class FakeComponentA extends ex.Component {} class FakeComponentB extends ex.Component {} class FakeComponentC extends ex.Component {} -class FakeSystem extends ex.System { +class FakeSystemPriority1 extends ex.System { + static priority = 1; + query: ex.Query>; + constructor( + public world: ex.World, + public name: string, + public types: ex.ComponentCtor[], + public systemType: ex.SystemType + ) { + super(); + this.query = this.world.query(types); + } + update(elapsedMs: number): void { + // fake + } +} + +class FakeSystemPriority2 extends ex.System { + static priority = 2; + query: ex.Query>; + constructor( + public world: ex.World, + public name: string, + public types: ex.ComponentCtor[], + public systemType: ex.SystemType + ) { + super(); + this.query = this.world.query(types); + } + update(elapsedMs: number): void { + // fake + } +} + +class FakeSystemPriority3 extends ex.System { + static priority = 3; query: ex.Query>; constructor( public world: ex.World, - public priority: number, public name: string, public types: ex.ComponentCtor[], public systemType: ex.SystemType @@ -36,11 +70,11 @@ describe('A SystemManager', () => { const sm = world.systemManager; // Lower priority - const s3 = new FakeSystem(world, 2, 'System3', [FakeComponentC], SystemType.Update); + const s3 = new FakeSystemPriority2(world, 'System3', [FakeComponentC], SystemType.Update); sm.addSystem(s3); // Systems of equal priority should preserve order - const s1 = new FakeSystem(world, 1, 'System1', [FakeComponentA], SystemType.Update); - const s2 = new FakeSystem(world, 1, 'System2', [FakeComponentC, FakeComponentB], SystemType.Update); + const s1 = new FakeSystemPriority1(world, 'System1', [FakeComponentA], SystemType.Update); + const s2 = new FakeSystemPriority1(world, 'System2', [FakeComponentC, FakeComponentB], SystemType.Update); sm.addSystem(s1); sm.addSystem(s2); @@ -52,11 +86,11 @@ describe('A SystemManager', () => { const sm = world.systemManager; // Lower priority - const s3 = new FakeSystem(world, 2, 'System3', [FakeComponentC], SystemType.Update); + const s3 = new FakeSystemPriority2(world, 'System3', [FakeComponentC], SystemType.Update); sm.addSystem(s3); // Systems of equal priority should preserve order - const s1 = new FakeSystem(world, 1, 'System1', [FakeComponentA], SystemType.Update); - const s2 = new FakeSystem(world, 1, 'System2', [FakeComponentC, FakeComponentB], SystemType.Update); + const s1 = new FakeSystemPriority1(world, 'System1', [FakeComponentA], SystemType.Update); + const s2 = new FakeSystemPriority1(world, 'System2', [FakeComponentC, FakeComponentB], SystemType.Update); sm.addSystem(s1); sm.addSystem(s2); @@ -69,7 +103,7 @@ describe('A SystemManager', () => { it('can update systems', () => { const world = new ex.World(null); const sm = world.systemManager; - const system = new FakeSystem(world, 2, 'System3', [FakeComponentC], SystemType.Update); + const system = new FakeSystemPriority2(world, 'System3', [FakeComponentC], SystemType.Update); system.preupdate = () => {}; // eslint-disable-line @typescript-eslint/no-empty-function system.postupdate = () => {}; // eslint-disable-line @typescript-eslint/no-empty-function spyOn(system, 'preupdate'); @@ -87,7 +121,7 @@ describe('A SystemManager', () => { const sm = world.systemManager; const qm = world.queryManager; const em = world.entityManager; - const system = new FakeSystem(world, 2, 'System3', [FakeComponentA, FakeComponentC], SystemType.Update); + const system = new FakeSystemPriority2(world, 'System3', [FakeComponentA, FakeComponentC], SystemType.Update); spyOn(system, 'update').and.callThrough(); sm.addSystem(system); @@ -119,10 +153,10 @@ describe('A SystemManager', () => { const sm = world.systemManager; const qm = world.queryManager; const em = world.entityManager; - const system1 = new FakeSystem(world, 2, 'System1', [FakeComponentA, FakeComponentC], SystemType.Update); + const system1 = new FakeSystemPriority2(world, 'System1', [FakeComponentA, FakeComponentC], SystemType.Update); spyOn(system1, 'update').and.callThrough(); sm.addSystem(system1); - const system2 = new FakeSystem(world, 2, 'System1', [FakeComponentA, FakeComponentC], SystemType.Draw); + const system2 = new FakeSystemPriority2(world, 'System1', [FakeComponentA, FakeComponentC], SystemType.Draw); spyOn(system2, 'update').and.callThrough(); sm.addSystem(system2); @@ -136,7 +170,7 @@ describe('A SystemManager', () => { const world = new ex.World(null); const sm = world.systemManager; expect(() => { - sm.addSystem(new FakeSystem(world, 0, 'ErrorSystem', [], SystemType.Update)); + sm.addSystem(new FakeSystemPriority1(world, 'ErrorSystem', [], SystemType.Update)); }).toThrow(new Error('Cannot create query without components')); }); });