refactor(framework): simplify spawner state management

Consolidate object and data tracking into a single Map in `spawnEffect`
to improve clarity and reduce redundant lookups. Also switch to
type-only
imports for Phaser.
This commit is contained in:
hypercross 2026-04-19 11:00:01 +08:00
parent 648e801dad
commit b0e74a5257
1 changed files with 38 additions and 40 deletions

View File

@ -1,54 +1,52 @@
import Phaser from 'phaser'; import { effect } from "@preact/signals-core";
import { effect } from '@preact/signals-core';
import type Phaser from "phaser";
type GO = Phaser.GameObjects.GameObject; type GO = Phaser.GameObjects.GameObject;
export interface Spawner<TData, TObject extends GO = GO> { export interface Spawner<TData, TObject extends GO = GO> {
/** 数据源迭代器 */ /** 数据源迭代器 */
getData(): Iterable<TData>; getData(): Iterable<TData>;
/** 获取数据的唯一键 */ /** 获取数据的唯一键 */
getKey(t: TData): string; getKey(t: TData): string;
/** 创建新对象 */ /** 创建新对象 */
onSpawn(t: TData): TObject | null; onSpawn(t: TData): TObject | null;
/** 销毁旧对象 */ /** 销毁旧对象 */
onDespawn(obj: TObject, t: TData): void; onDespawn(obj: TObject, t: TData): void;
/** 更新已有对象 */ /** 更新已有对象 */
onUpdate(t: TData, obj: TObject): void; onUpdate(t: TData, obj: TObject): void;
} }
export function spawnEffect<TData, TObject extends GO = GO>( export function spawnEffect<TData, TObject extends GO = GO>(
spawner: Spawner<TData, TObject>, spawner: Spawner<TData, TObject>,
): () => void { ): () => void {
const objects = new Map<string, TObject>(); const entries = new Map<string, { object: TObject; data: TData }>();
const spawnData = new Map<string, TData>();
return effect(() => { return effect(() => {
const current = new Set<string>(); const current = new Set<string>();
for (const t of spawner.getData()) { for (const t of spawner.getData()) {
const key = spawner.getKey(t); const key = spawner.getKey(t);
current.add(key); current.add(key);
const existing = objects.get(key); const existing = entries.get(key);
if (!existing) { if (!existing) {
const obj = spawner.onSpawn(t); const obj = spawner.onSpawn(t);
if (obj) { if (obj) {
spawnData.set(key, t); entries.set(key, { object: obj, data: t });
objects.set(key, obj);
}
} else {
if(spawnData.get(key) === t) continue;
spawner.onUpdate(t, existing);
spawnData.set(key, t);
}
} }
} else {
if (existing.data === t) continue;
spawner.onUpdate(t, existing.object);
existing.data = t;
}
}
for (const [key, obj] of objects) { for (const [key, entry] of entries) {
if (!current.has(key)) { if (!current.has(key)) {
spawner.onDespawn(obj, spawnData.get(key)!); spawner.onDespawn(entry.object, entry.data);
objects.delete(key); entries.delete(key);
spawnData.delete(key); }
} }
} });
});
} }