Style: enforce 2-space indent and double quotes

This commit is contained in:
2026-04-19 10:48:32 +08:00
parent ddc9d057fd
commit 648e801dad
12 changed files with 466 additions and 415 deletions
+66 -72
View File
@@ -1,89 +1,83 @@
import Phaser from 'phaser';
import { ReactiveScene, type ReactiveScenePhaserData } from './ReactiveScene';
import { ReactiveScene } from "./ReactiveScene";
export interface FadeSceneData {
[key: string]: unknown;
[key: string]: unknown;
}
/**
* 处理淡入淡出到黑色的过渡场景
*/
export class FadeScene extends ReactiveScene<FadeSceneData> {
private overlay!: Phaser.GameObjects.Rectangle;
private isFading = false;
private overlay!: Phaser.GameObjects.Rectangle;
private isFading = false;
constructor() {
super(FADE_SCENE_KEY);
constructor() {
super(FADE_SCENE_KEY);
}
create(): void {
super.create();
// 创建黑色遮罩层,覆盖整个游戏区域
const game = this.game;
this.overlay = this.add
.rectangle(0, 0, game.scale.width, game.scale.height, 0x000000, 1)
.setOrigin(0)
.setAlpha(1)
.setDepth(999999)
.setInteractive({ useHandCursor: false });
// 防止遮罩阻挡输入
this.overlay.disableInteractive();
}
/**
* 淡入(从黑色到透明)
* @param duration 动画时长(毫秒)
*/
fadeIn(duration = 300): Promise<void> {
return this.fadeTo(0, duration);
}
/**
* 淡出(从透明到黑色)
* @param duration 动画时长(毫秒)
*/
fadeOut(duration = 300): Promise<void> {
return this.fadeTo(1, duration);
}
/**
* 淡入淡出到指定透明度
*/
private fadeTo(targetAlpha: number, duration: number): Promise<void> {
// 如果 overlay 还未初始化,直接返回 resolved promise
if (!this.overlay) {
console.warn("FadeScene: overlay 未初始化,跳过过渡动画");
return Promise.resolve();
}
create(): void {
super.create();
// 创建黑色遮罩层,覆盖整个游戏区域
const game = this.game;
this.overlay = this.add.rectangle(
0,
0,
game.scale.width,
game.scale.height,
0x000000,
1
).setOrigin(0)
.setAlpha(1)
.setDepth(999999)
.setInteractive({ useHandCursor: false });
// 防止遮罩阻挡输入
this.overlay.disableInteractive();
if (this.isFading) {
console.warn("FadeScene: 正在进行过渡动画");
}
/**
* 淡入(从黑色到透明)
* @param duration 动画时长(毫秒)
*/
fadeIn(duration = 300): Promise<void> {
return this.fadeTo(0, duration);
}
this.isFading = true;
this.overlay.setAlpha(targetAlpha === 1 ? 0 : 1);
/**
* 淡出(从透明到黑色)
* @param duration 动画时长(毫秒)
*/
fadeOut(duration = 300): Promise<void> {
return this.fadeTo(1, duration);
}
/**
* 淡入淡出到指定透明度
*/
private fadeTo(targetAlpha: number, duration: number): Promise<void> {
// 如果 overlay 还未初始化,直接返回 resolved promise
if (!this.overlay) {
console.warn('FadeScene: overlay 未初始化,跳过过渡动画');
return Promise.resolve();
}
if (this.isFading) {
console.warn('FadeScene: 正在进行过渡动画');
}
this.isFading = true;
this.overlay.setAlpha(targetAlpha === 1 ? 0 : 1);
return new Promise<void>((resolve) => {
this.tweens.add({
targets: this.overlay,
alpha: targetAlpha,
duration,
ease: 'Linear',
onComplete: () => {
this.isFading = false;
resolve();
},
});
});
}
return new Promise<void>((resolve) => {
this.tweens.add({
targets: this.overlay,
alpha: targetAlpha,
duration,
ease: "Linear",
onComplete: () => {
this.isFading = false;
resolve();
},
});
});
}
}
// 导出常量供 PhaserGame 使用
export const FADE_SCENE_KEY = '__fade__';
export const FADE_SCENE_KEY = "__fade__";
+28 -27
View File
@@ -1,36 +1,37 @@
import type { GameHost } from 'boardgame-core';
import { ReactiveScene } from './ReactiveScene';
import { ReactiveScene } from "./ReactiveScene";
import type { GameHost } from "boardgame-core";
export interface GameHostSceneOptions<TState extends Record<string, unknown>> {
gameHost: GameHost<TState>;
[key: string]: unknown;
gameHost: GameHost<TState>;
[key: string]: unknown;
}
export abstract class GameHostScene<TState extends Record<string, unknown>>
extends ReactiveScene<GameHostSceneOptions<TState>>
{
public get gameHost(): GameHost<TState> {
const gameHost = this.initData.gameHost as GameHost<TState>;
if (!gameHost) {
throw new Error(
`GameHostScene (${this.scene.key}): gameHost 未提供。` +
`确保在 PhaserScene 组件的 data 属性中传入 gameHost。`
);
}
return gameHost;
export abstract class GameHostScene<
TState extends Record<string, unknown>,
> extends ReactiveScene<GameHostSceneOptions<TState>> {
public get gameHost(): GameHost<TState> {
const gameHost = this.initData.gameHost as GameHost<TState>;
if (!gameHost) {
throw new Error(
`GameHostScene (${this.scene.key}): gameHost 未提供。` +
"确保在 PhaserScene 组件的 data 属性中传入 gameHost。",
);
}
return gameHost;
}
public get state(): TState {
return this.gameHost?.state.value;
}
public get state(): TState {
return this.gameHost?.state.value;
}
addInterruption(promise: Promise<void>){
this.gameHost?.addInterruption(promise);
}
addInterruption(promise: Promise<void>) {
this.gameHost?.addInterruption(promise);
}
addTweenInterruption(tween: Phaser.Tweens.Tween){
this.addInterruption(new Promise(
resolve => tween.once('complete', resolve)
));
}
addTweenInterruption(tween: Phaser.Tweens.Tween) {
this.addInterruption(
new Promise((resolve) => tween.once("complete", resolve)),
);
}
}
+62 -59
View File
@@ -1,87 +1,90 @@
import Phaser from 'phaser';
import { effect, type ReadonlySignal } from '@preact/signals-core';
import { DisposableBag, type IDisposable } from '../utils';
import { effect, type ReadonlySignal } from "@preact/signals-core";
import { Scene } from "phaser";
import { DisposableBag, type IDisposable } from "../utils";
import type Phaser from "phaser";
type CleanupFn = void | (() => void);
// 前向声明,避免循环导入
export interface SceneController {
launch(sceneKey: string): Promise<void>;
restart(): Promise<void>;
currentScene: ReadonlySignal<string | null>;
isTransitioning: ReadonlySignal<boolean>;
launch(sceneKey: string): Promise<void>;
restart(): Promise<void>;
currentScene: ReadonlySignal<string | null>;
isTransitioning: ReadonlySignal<boolean>;
}
export interface ReactiveScenePhaserData {
phaserGame: ReadonlySignal<{ game: Phaser.Game }>;
sceneController: SceneController;
phaserGame: ReadonlySignal<{ game: Phaser.Game }>;
sceneController: SceneController;
}
export interface ReactiveSceneOptions<TData extends Record<string, unknown> = {}> {
key?: string;
export interface ReactiveSceneOptions {
key?: string;
}
/**
* 通用的响应式 Scene 基类
* @typeparam TData - 通过 init(data) 接收的数据类型(必须包含 phaserGame 和 sceneController
*/
export abstract class ReactiveScene<TData extends Record<string, unknown> = {}>
extends Phaser.Scene
implements IDisposable
export abstract class ReactiveScene<TData = object>
extends Scene
implements IDisposable
{
protected disposables = new DisposableBag();
private _initData?: TData & ReactiveScenePhaserData;
protected disposables = new DisposableBag();
private _initData?: TData & ReactiveScenePhaserData;
/**
* 获取通过 init() 注入的数据
* 在 create() 阶段保证可用
*/
public get initData(): TData & ReactiveScenePhaserData {
if (!this._initData) {
throw new Error(
`ReactiveScene (${this.scene.key}): initData 尚未初始化。` +
`确保场景通过 PhaserScene 组件注册,并在 create() 阶段访问。`
);
}
return this._initData;
/**
* 获取通过 init() 注入的数据
* 在 create() 阶段保证可用
*/
public get initData(): TData & ReactiveScenePhaserData {
if (!this._initData) {
throw new Error(
`ReactiveScene (${this.scene.key}): initData 尚未初始化。` +
"确保场景通过 PhaserScene 组件注册,并在 create() 阶段访问。",
);
}
return this._initData;
}
/**
* 获取 Phaser game 实例的响应式信号
*/
public get phaserGame(): ReadonlySignal<{ game: Phaser.Game }> {
return this.initData.phaserGame;
}
/**
* 获取 Phaser game 实例的响应式信号
*/
public get phaserGame(): ReadonlySignal<{ game: Phaser.Game }> {
return this.initData.phaserGame;
}
/**
* 获取场景控制器
*/
public get sceneController(): SceneController {
return this.initData.sceneController;
}
/**
* 获取场景控制器
*/
public get sceneController(): SceneController {
return this.initData.sceneController;
}
constructor(key?: string) {
super(key);
}
constructor(key?: string) {
super(key);
}
init(data: TData & ReactiveScenePhaserData): void {
this._initData = data;
}
init(data: TData & ReactiveScenePhaserData): void {
this._initData = data;
}
create(): void {
this.events.on('shutdown', this.dispose, this);
}
create(): void {
this.events.on("shutdown", this.dispose, this);
}
dispose(): void {
this.disposables.dispose();
}
dispose(): void {
this.disposables.dispose();
}
public addDisposable(disposable: IDisposable): void {
this.disposables.add(disposable);
}
public addDisposable(disposable: IDisposable): void {
this.disposables.add(disposable);
}
/** 注册响应式监听(场景关闭时自动清理) */
public addEffect(fn: () => CleanupFn): void {
this.disposables.add(effect(fn));
}
/** 注册响应式监听(场景关闭时自动清理) */
public addEffect(fn: () => CleanupFn): void {
this.disposables.add(effect(fn));
}
}
+10 -6
View File
@@ -1,8 +1,12 @@
export { ReactiveScene } from './ReactiveScene';
export type { ReactiveSceneOptions, ReactiveScenePhaserData, SceneController } from './ReactiveScene';
export { ReactiveScene } from "./ReactiveScene";
export type {
ReactiveSceneOptions,
ReactiveScenePhaserData,
SceneController,
} from "./ReactiveScene";
export { FadeScene, FADE_SCENE_KEY } from './FadeScene';
export type { FadeSceneData } from './FadeScene';
export { FadeScene, FADE_SCENE_KEY } from "./FadeScene";
export type { FadeSceneData } from "./FadeScene";
export { GameHostScene } from './GameHostScene';
export type { GameHostSceneOptions } from './GameHostScene';
export { GameHostScene } from "./GameHostScene";
export type { GameHostSceneOptions } from "./GameHostScene";