Skip to main content

Custom Components

Spinorium provides a practical way to merge layout exported from the editor with runtime code. You can declare custom containers in TypeScript, let the editor instantiate them via linkage, and get strongly-typed references to child components without manual lookups.

Creating Custom Components

A custom component is any class that extends Container (or another component class) and encapsulates behavior for a part of the scene. Declare readonly fields that match the linkage names of child components so the factory can inject them at construction time.

Keep constructor logic lightweight because children are not yet attached. Custom containers receive their linked children once the main scene has been constructed. Listen for TemplateEvent.CREATE_MAIN_SCENE before touching any injected references or dispatching game logic. Scene objects are safe to access inside handlers registered for this event or later moments such as TemplateEvent.SHOW_MAIN_SCREEN. Earlier lifecycle hooks, including the constructor, should only prepare state or subscribe to events.

export class BackgroundContainer extends Container {
protected readonly _freeSpins:SpineAnimation;
protected readonly _baseGame:SpineAnimation;

public constructor() {
super();

System.appendListener(TemplateEvent.CREATE_MAIN_SCENE, this.createMainSceneHandler);
}

private createMainSceneHandler = () => {
this._freeSpins.play('track', true, 1);
this._baseGame.play('track', true, 1);
};
}

Binding Custom Components

Register custom classes in the dependency manager so the component factory knows which class to instantiate when it encounters a linkage type exported by the editor. Override createSystemConfig in the player controller to append your registrations. The registration key must match the linkageType value assigned in the editor. Add every custom container that needs to be resolved during scene creation.

export class GameController extends PlayerController {
protected override createSystemConfig():Optional<SystemConfig> {
return ConfigUtil.overrideConfig(super.createSystemConfig(), {
dependencyManager: {
componentsList: [
[ 'BackgroundContainer', BackgroundContainer ]
]
}
});
}
}

Background Container Example

Below is a complete example that reacts to reel-state events and switches the active background smoothly. The states are dispatched elsewhere in the game flow.

export enum ReelsState {
BASE_GAME,
FREE_SPINS
}

export const enum ReelsStateEvent {
TOGGLE_STATE = 'reels/toggle-state'
}

export class BackgroundContainer extends Container {
protected readonly _freeSpins:SpineAnimation;
protected readonly _baseGame:SpineAnimation;

protected _backgroundsMap:Record<ReelsState, SpineAnimation>;
protected _currentState:ReelsState;

public constructor() {
super();

System.appendListener(TemplateEvent.CREATE_MAIN_SCENE, this.createMainSceneHandler);
System.appendListener(ReelsStateEvent.TOGGLE_STATE, this.toggleGameStateHandler);

this._currentState = ReelsState.BASE_GAME;
}

protected createMainSceneHandler = ():void => {
this._freeSpins.play('track', true, 1);
this._baseGame.play('track', true, 1);

this._backgroundsMap = {
[ReelsState.FREE_SPINS]: this._freeSpins,
[ReelsState.BASE_GAME]: this._baseGame
};
};

protected toggleGameStateHandler = (event:Event<ReelsState>):void => {
if (this._currentState === event.data) {
return;
}

const prevState = this._currentState;
const prevBackground = this._backgroundsMap[prevState];
const nextState = event.data;
const nextBackground = this._backgroundsMap[nextState];
this._currentState = nextState;

if (prevBackground !== nextBackground) {
Actions.stage.addObject(this, nextBackground)
.stage.fadeInObject(nextBackground, 0.35)
.stage.removeObject(prevBackground)
.start();
}
};
}