boardgame-phaser/packages/framework/src/scenes/ReactiveScene.ts

81 lines
2.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Phaser from 'phaser';
import { effect, type ReadonlySignal } from '@preact/signals-core';
import { DisposableBag, type IDisposable } from '../utils';
type CleanupFn = void | (() => void);
// 前向声明,避免循环导入
export interface SceneController {
launch(sceneKey: string, data?: Record<string, unknown>): Promise<void>;
currentScene: ReadonlySignal<string | null>;
isTransitioning: ReadonlySignal<boolean>;
}
export interface ReactiveScenePhaserData {
phaserGame: ReadonlySignal<{ game: Phaser.Game }>;
sceneController: SceneController;
}
export interface ReactiveSceneOptions<TData extends Record<string, unknown> = {}> {
key?: string;
}
/**
* 通用的响应式 Scene 基类
* @typeparam TData - 通过 init(data) 接收的数据类型(必须包含 phaserGame 和 sceneController
*/
export abstract class ReactiveScene<TData extends Record<string, unknown> = {}>
extends Phaser.Scene
implements IDisposable
{
protected disposables = new DisposableBag();
private _initData!: TData & ReactiveScenePhaserData;
/**
* 获取通过 init() 注入的数据
* 在 create() 阶段保证可用
*/
public get initData(): TData & ReactiveScenePhaserData {
return this._initData;
}
/**
* 获取 Phaser game 实例的响应式信号
*/
public get phaserGame(): ReadonlySignal<{ game: Phaser.Game }> {
return this._initData.phaserGame;
}
/**
* 获取场景控制器
*/
public get sceneController(): SceneController {
return this._initData.sceneController;
}
constructor(key?: string) {
super(key);
}
init(data: TData & ReactiveScenePhaserData): void {
this._initData = data;
}
create(): void {
this.events.on('shutdown', this.dispose, this);
}
dispose(): void {
this.disposables.dispose();
}
public addDisposable(disposable: IDisposable): void {
this.disposables.add(disposable);
}
/** 注册响应式监听(场景关闭时自动清理) */
public addEffect(fn: () => CleanupFn): void {
this.disposables.add(effect(fn));
}
}