Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 72 additions & 2 deletions src/engine/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import { Entity } from './entity-component-system/entity';
import type { DebugStats } from './debug/debug-config';
import { DebugConfig } from './debug/debug-config';
import { BrowserEvents } from './util/browser';
import type { AntialiasOptions, ExcaliburGraphicsContext } from './graphics';
import type { AntialiasOptions, ExcaliburGraphicsContext, ExcaliburGraphicsContextWebGLOptions } from './graphics';
import {
DefaultAntialiasOptions,
DefaultPixelArtOptions,
Expand All @@ -64,6 +64,7 @@ import type { GarbageCollectionOptions } from './garbage-collector';
import { DefaultGarbageCollectionOptions, GarbageCollector } from './garbage-collector';
import { mergeDeep } from './util/util';
import { getDefaultGlobal } from './util/iframe';
import type { Plugin } from './plugin';

export interface EngineEvents extends DirectorEvents {
fallbackgraphicscontext: ExcaliburGraphicsContext2DCanvas;
Expand Down Expand Up @@ -118,6 +119,10 @@ export enum ScrollPreventionMode {
* Defines the available options to configure the Excalibur engine at constructor time.
*/
export interface EngineOptions<TKnownScenes extends string = any> {
/**
* Optionally configure Excalibur plugins
*/
plugins?: Plugin[];
/**
* Optionally configure the width of the viewport in css pixels
*/
Expand Down Expand Up @@ -428,6 +433,11 @@ export class Engine<TKnownScenes extends string = any> implements CanInitialize,

public global: GlobalEventHandlers;

private _plugins: Plugin[] = [];
public get plugins(): readonly Plugin[] {
return this._plugins;
}

private _garbageCollector: GarbageCollector;

public readonly garbageCollectorConfig: GarbageCollectionOptions | null;
Expand Down Expand Up @@ -820,6 +830,15 @@ export class Engine<TKnownScenes extends string = any> implements CanInitialize,

Flags.freeze();

if (options.plugins && options.plugins.length > 0) {
this._plugins = [...options.plugins];
this._plugins.sort((a, b) => a.priority - b.priority);
}

for (const plugin of this._plugins) {
plugin.onEnginePreConfig?.(this, options);
}

// Initialize browser events facade
this.browser = new BrowserEvents(window, document);

Expand Down Expand Up @@ -988,6 +1007,36 @@ O|===|* >________________>\n\
if (!useCanvasGraphicsContext) {
// Attempt webgl first
try {
let onGraphicsPreConfig: (context: ExcaliburGraphicsContext, options: ExcaliburGraphicsContextWebGLOptions) => void;
let onGraphicsPostConfig: (context: ExcaliburGraphicsContext, options: ExcaliburGraphicsContextWebGLOptions) => void;
let onGraphicsPreInitialize: (context: ExcaliburGraphicsContext) => void;
let onGraphicsPostInitialize: (context: ExcaliburGraphicsContext) => void;
if (this._plugins.length > 0) {
onGraphicsPreConfig = (context: ExcaliburGraphicsContext, options: ExcaliburGraphicsContextWebGLOptions) => {
for (const plugin of this._plugins) {
plugin.onGraphicsPreConfig?.(context, options);
}
};

onGraphicsPostConfig = (context: ExcaliburGraphicsContext, options: ExcaliburGraphicsContextWebGLOptions) => {
for (const plugin of this._plugins) {
plugin.onGraphicsPostConfig?.(context, options);
}
};

onGraphicsPreInitialize = (context: ExcaliburGraphicsContext) => {
for (const plugin of this._plugins) {
plugin.onGraphicsPreInitialize?.(context);
}
};

onGraphicsPostInitialize = (context: ExcaliburGraphicsContext) => {
for (const plugin of this._plugins) {
plugin.onGraphicsPostInitialize?.(context);
}
};
}

this.graphicsContext = new ExcaliburGraphicsContextWebGL({
canvasElement: this.canvas,
enableTransparency: this.enableCanvasTransparency,
Expand All @@ -1006,7 +1055,11 @@ O|===|* >________________>\n\
}
: null,
handleContextLost: options.handleContextLost ?? this._handleWebGLContextLost,
handleContextRestored: options.handleContextRestored
handleContextRestored: options.handleContextRestored,
onGraphicsPreConfig,
onGraphicsPostConfig,
onGraphicsPreInitialize,
onGraphicsPostInitialize
});
} catch (e) {
this._logger.warn(
Expand Down Expand Up @@ -1241,6 +1294,11 @@ O|===|* >________________>\n\
this.stop();
this._garbageCollector.forceCollectAll();
this.input.toggleEnabled(false);

for (const plugin of this.plugins) {
plugin.dispose?.();
}

if (this._hasCreatedCanvas) {
this.canvas.parentNode.removeChild(this.canvas);
}
Expand Down Expand Up @@ -1507,6 +1565,10 @@ O|===|* >________________>\n\
if (!this.canvasElementId && !options.canvasElement) {
document.body.appendChild(this.canvas);
}

for (const plugin of this._plugins) {
plugin.onEnginePostConfig?.(this, options);
}
}

public toggleInputEnabled(enabled: boolean) {
Expand All @@ -1527,10 +1589,18 @@ O|===|* >________________>\n\

private async _overrideInitialize(engine: Engine) {
if (!this.isInitialized) {
for (const plugin of this._plugins) {
plugin.onEnginePreInitialize(this);
}

await this.director.onInitialize();
await this.onInitialize(engine);
this.events.emit('initialize', new InitializeEvent(engine, this));
this._isInitialized = true;

for (const plugin of this._plugins) {
plugin.onEnginePostInitialize(this);
}
}
}

Expand Down
31 changes: 30 additions & 1 deletion src/engine/graphics/context/excalibur-graphics-context-webgl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,10 @@ export class ExcaliburGraphicsContextWebGL implements ExcaliburGraphicsContext {

public materialScreenTexture!: WebGLTexture | null;

private _onGraphicsPreInitialize?: (context: ExcaliburGraphicsContext) => void;

private _onGraphicsPostInitialize?: (context: ExcaliburGraphicsContext) => void;

public get z(): number {
return this._state.current.z;
}
Expand Down Expand Up @@ -260,6 +264,9 @@ export class ExcaliburGraphicsContextWebGL implements ExcaliburGraphicsContext {
private _isContextLost = false;

constructor(options: ExcaliburGraphicsContextWebGLOptions) {
if (options.onGraphicsPreConfig) {
options.onGraphicsPreConfig(this, options);
}
const {
canvasElement,
context,
Expand All @@ -274,8 +281,12 @@ export class ExcaliburGraphicsContextWebGL implements ExcaliburGraphicsContext {
useDrawSorting,
garbageCollector,
handleContextLost,
handleContextRestored
handleContextRestored,
onGraphicsPostConfig,
onGraphicsPreInitialize,
onGraphicsPostInitialize
} = options;

this.__gl =
context ??
(canvasElement.getContext('webgl2', {
Expand All @@ -296,6 +307,12 @@ export class ExcaliburGraphicsContextWebGL implements ExcaliburGraphicsContext {
if (handleContextRestored) {
this.__gl.canvas.addEventListener('webglcontextrestored', handleContextRestored, false);
}
if (onGraphicsPreInitialize) {
this._onGraphicsPreInitialize = onGraphicsPreInitialize;
}
if (onGraphicsPostInitialize) {
this._onGraphicsPostInitialize = onGraphicsPostInitialize;
}

this.__gl.canvas.addEventListener('webglcontextlost', () => {
this._isContextLost = true;
Expand All @@ -318,6 +335,9 @@ export class ExcaliburGraphicsContextWebGL implements ExcaliburGraphicsContext {
this._drawCallPool.disableWarnings = true;
this._drawCallPool.preallocate();
this._init();
if (onGraphicsPostConfig) {
onGraphicsPostConfig(this, options);
}
}

private _disposed = false;
Expand All @@ -336,7 +356,12 @@ export class ExcaliburGraphicsContextWebGL implements ExcaliburGraphicsContext {
}

private _init() {
// add pre/post init here
const gl = this.__gl;
if (this._onGraphicsPreInitialize) {
this._onGraphicsPreInitialize(this);
}

// Setup viewport and view matrix
this._ortho = Matrix.ortho(0, gl.canvas.width, gl.canvas.height, 0, 400, -400);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
Expand Down Expand Up @@ -420,6 +445,10 @@ export class ExcaliburGraphicsContextWebGL implements ExcaliburGraphicsContext {
});

this.debug = new ExcaliburGraphicsContextWebGLDebug(this);

if (this._onGraphicsPostInitialize) {
this._onGraphicsPostInitialize(this);
}
}

public register<T extends RendererPlugin>(renderer: T) {
Expand Down
6 changes: 6 additions & 0 deletions src/engine/graphics/context/excalibur-graphics-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@ export interface ExcaliburGraphicsContextOptions {
* Feature flag that enables draw sorting will removed in v0.29
*/
useDrawSorting?: boolean;

onGraphicsPreConfig?: (context: ExcaliburGraphicsContextState, options: ExcaliburGraphicsContextOptions) => void;
onGraphicsPostConfig?: (context: ExcaliburGraphicsContextState, options: ExcaliburGraphicsContextOptions) => void;

onGraphicsPreInitialize?: (context: ExcaliburGraphicsContextState) => void;
onGraphicsPostInitialize?: (context: ExcaliburGraphicsContextState) => void;
}

export interface ExcaliburGraphicsContextState {
Expand Down
2 changes: 2 additions & 0 deletions src/engine/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ export * from './util/coroutine';
export * from './util/assert';
export * from './util/rental-pool';

export * from './plugin';

// ex.Deprecated
// import * as deprecated from './deprecated';
// export { deprecated as Deprecated };
Expand Down
95 changes: 95 additions & 0 deletions src/engine/plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import type { Engine, EngineOptions } from './engine';
import type { ExcaliburGraphicsContext, ExcaliburGraphicsContextOptions } from './graphics';
import type { Scene } from './scene';

// TODO should these support async flows???

/**
*
* An Excalibur plugin packages up changes to excalibur in a convenient package such as
* * Custom config intercepting and implementation
* * Engine initialization customization
* * Graphics Context configuration including
* * Custom RendererPlugins
* * Custom PostProcessors
* * Scene customization including default ECS Systems, Components
*/
export abstract class Plugin {
/**
* Unique name of the plugin
*/
name: string;
/**
* Plugin priority determines the order they're run, lower is first, higher is last, default is 0
*/
priority: number = 0;

/**
* Perform any async loading
*/
async onLoad?(): Promise<void> {
return await Promise.resolve();
}

/**
* Perform any extras when load complete
*/
async onLoadComplete?(): Promise<void> {
return await Promise.resolve();
}

/**
* Optionally intercept and mutate the {@param options} passed into the Engine, and modify the engine
*/
onEnginePreConfig?(engine: Engine, options: EngineOptions): void;

/**
* Optionally intercept and mutate the {@param options} passed into the Engine, and modify the engine
*/
onEnginePostConfig?(engine: Engine, options: EngineOptions): void;

/**
* Optinally intercept the engine and modify before initialize
*/
onEnginePreInitialize?(engine: Engine): void;

/**
* Optionally intercept the engine and modify after initialize
*/
onEnginePostInitialize?(engine: Engine): void;

/**
* Optionally intercept the grpahics context before configuration and modify either the context or options
*/
onGraphicsPreConfig?(context: ExcaliburGraphicsContext, options: ExcaliburGraphicsContextOptions): void;

/**
* Optionally intercept the grpahics context after configuration and modify either the context or options
*/
onGraphicsPostConfig?(context: ExcaliburGraphicsContext, options: ExcaliburGraphicsContextOptions): void;

/**
* Optionally intercetp the graphics context and modify before initialize
*/
onGraphicsPreInitialize?(context: ExcaliburGraphicsContext): void;

/**
* Optionally intercetp the graphics context and modify after initialize
*/
onGraphicsPostInitialize?(context: ExcaliburGraphicsContext): void;

/**
* Optionally intercept a scene and modify before initialize
*/
onScenePreInitialize?(scene: Scene): void;

/**
* Optionally intercept a scene and modify after initialize
*/
onScenePostInitialize?(scene: Scene): void;

/**
* Optinally perform any cleanup necessary when the engine is disposed
*/
dispose?(): void;
}
8 changes: 8 additions & 0 deletions src/engine/scene.ts
Original file line number Diff line number Diff line change
Expand Up @@ -352,11 +352,19 @@ export class Scene<TActivationData = unknown> implements CanInitialize, CanActiv

this.world.systemManager.initialize();

for (const plugin of engine.plugins) {
plugin.onScenePreInitialize?.(this);
}

// This order is important! we want to be sure any custom init that add actors
// fire before the actor init
await this.onInitialize(engine);
this._initializeChildren();

for (const plugin of engine.plugins) {
plugin.onScenePostInitialize?.(this);
}

this._logger.debug('Scene.onInitialize', this, engine);
this.events.emit('initialize', new InitializeEvent(engine, this));
} catch (e) {
Expand Down