feat(core): 添加持久化实体支持跨场景迁移 (#285)
实现实体生命周期策略,允许标记实体为持久化,在场景切换时自动迁移到新场景。 主要变更: - 新增 EEntityLifecyclePolicy 枚举(SceneLocal/Persistent) - Entity 添加 setPersistent()、setSceneLocal()、isPersistent API - Scene 添加 findPersistentEntities()、extractPersistentEntities()、receiveMigratedEntities() - SceneManager.setScene() 自动处理持久化实体迁移 - 添加完整的中英文文档和 21 个测试用例
This commit is contained in:
26
packages/core/src/ECS/Core/EntityLifecyclePolicy.ts
Normal file
26
packages/core/src/ECS/Core/EntityLifecyclePolicy.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* 实体生命周期策略
|
||||
*
|
||||
* 定义实体在场景切换时的行为。
|
||||
*
|
||||
* Entity lifecycle policy.
|
||||
* Defines entity behavior during scene transitions.
|
||||
*/
|
||||
export const enum EEntityLifecyclePolicy {
|
||||
/**
|
||||
* 默认策略 - 随场景销毁
|
||||
*
|
||||
* Default policy - destroyed with scene.
|
||||
*/
|
||||
SceneLocal = 0,
|
||||
|
||||
/**
|
||||
* 持久化策略 - 跨场景保留
|
||||
*
|
||||
* 实体在场景切换时自动迁移到新场景。
|
||||
*
|
||||
* Persistent policy - survives scene transitions.
|
||||
* Entity is automatically migrated to the new scene.
|
||||
*/
|
||||
Persistent = 1
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Component } from './Component';
|
||||
import { ComponentRegistry, ComponentType } from './Core/ComponentStorage';
|
||||
import { EEntityLifecyclePolicy } from './Core/EntityLifecyclePolicy';
|
||||
import { BitMask64Utils, BitMask64Data } from './Utils/BigIntCompatibility';
|
||||
import { createLogger } from '../Utils/Logger';
|
||||
import { getComponentInstanceTypeName, getComponentTypeName } from './Decorators';
|
||||
@@ -118,6 +119,13 @@ export class Entity {
|
||||
*/
|
||||
private _componentCache: Component[] | null = null;
|
||||
|
||||
/**
|
||||
* 生命周期策略
|
||||
*
|
||||
* Lifecycle policy for scene transitions.
|
||||
*/
|
||||
private _lifecyclePolicy: EEntityLifecyclePolicy = EEntityLifecyclePolicy.SceneLocal;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
@@ -129,6 +137,61 @@ export class Entity {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取生命周期策略
|
||||
*
|
||||
* Get lifecycle policy.
|
||||
*/
|
||||
public get lifecyclePolicy(): EEntityLifecyclePolicy {
|
||||
return this._lifecyclePolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查实体是否为持久化实体
|
||||
*
|
||||
* Check if entity is persistent (survives scene transitions).
|
||||
*/
|
||||
public get isPersistent(): boolean {
|
||||
return this._lifecyclePolicy === EEntityLifecyclePolicy.Persistent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置实体为持久化(跨场景保留)
|
||||
*
|
||||
* 标记后的实体在场景切换时不会被销毁,会自动迁移到新场景。
|
||||
*
|
||||
* Mark entity as persistent (survives scene transitions).
|
||||
* Persistent entities are automatically migrated to the new scene.
|
||||
*
|
||||
* @returns this,支持链式调用 | Returns this for chaining
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const player = scene.createEntity('Player')
|
||||
* .setPersistent()
|
||||
* .addComponent(new PlayerComponent());
|
||||
* ```
|
||||
*/
|
||||
public setPersistent(): this {
|
||||
this._lifecyclePolicy = EEntityLifecyclePolicy.Persistent;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置实体为场景本地(随场景销毁)
|
||||
*
|
||||
* 将实体恢复为默认行为。
|
||||
*
|
||||
* Mark entity as scene-local (destroyed with scene).
|
||||
* Restores default behavior.
|
||||
*
|
||||
* @returns this,支持链式调用 | Returns this for chaining
|
||||
*/
|
||||
public setSceneLocal(): this {
|
||||
this._lifecyclePolicy = EEntityLifecyclePolicy.SceneLocal;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取销毁状态
|
||||
* @returns 如果实体已被销毁则返回true
|
||||
|
||||
@@ -819,6 +819,81 @@ export class Scene implements IScene {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找所有持久化实体
|
||||
*
|
||||
* Find all persistent entities in this scene.
|
||||
*
|
||||
* @returns 持久化实体数组 | Array of persistent entities
|
||||
*/
|
||||
public findPersistentEntities(): Entity[] {
|
||||
return this.entities.buffer.filter(entity => entity.isPersistent);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取持久化实体(从场景中分离但不销毁)
|
||||
*
|
||||
* 用于场景切换时收集需要迁移的实体。
|
||||
*
|
||||
* Extract persistent entities (detach from scene without destroying).
|
||||
* Used during scene transitions to collect entities for migration.
|
||||
*
|
||||
* @returns 被提取的持久化实体数组 | Array of extracted persistent entities
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public extractPersistentEntities(): Entity[] {
|
||||
const persistentEntities = this.findPersistentEntities();
|
||||
|
||||
for (const entity of persistentEntities) {
|
||||
// 从实体列表移除
|
||||
this.entities.remove(entity);
|
||||
|
||||
// 从查询系统移除
|
||||
this.querySystem.removeEntity(entity);
|
||||
|
||||
// 清除场景引用(但保留组件数据)
|
||||
entity.scene = null;
|
||||
}
|
||||
|
||||
return persistentEntities;
|
||||
}
|
||||
|
||||
/**
|
||||
* 接收迁移的实体
|
||||
*
|
||||
* 将从其他场景迁移来的实体添加到当前场景。
|
||||
*
|
||||
* Receive migrated entities from another scene.
|
||||
*
|
||||
* @param entities 要接收的实体数组 | Entities to receive
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public receiveMigratedEntities(entities: Entity[]): void {
|
||||
for (const entity of entities) {
|
||||
// 设置新场景引用
|
||||
entity.scene = this;
|
||||
|
||||
// 添加到实体列表
|
||||
this.entities.add(entity);
|
||||
|
||||
// 添加到查询系统
|
||||
this.querySystem.addEntity(entity);
|
||||
|
||||
// 重新注册组件到新场景的存储
|
||||
for (const component of entity.components) {
|
||||
this.componentStorageManager.addComponent(entity.id, component);
|
||||
this.referenceTracker?.registerEntityScene(entity.id, this);
|
||||
}
|
||||
}
|
||||
|
||||
// 清除系统缓存
|
||||
if (entities.length > 0) {
|
||||
this.clearSystemEntityCaches();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据名称查找实体(别名方法)
|
||||
*
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { IScene } from './IScene';
|
||||
import { Scene } from './Scene';
|
||||
import { Entity } from './Entity';
|
||||
import { ECSFluentAPI, createECSAPI } from './Core/FluentAPI';
|
||||
import { Time } from '../Utils/Time';
|
||||
import { createLogger } from '../Utils/Logger';
|
||||
@@ -79,6 +81,13 @@ export class SceneManager implements IService {
|
||||
*/
|
||||
private _performanceMonitor: PerformanceMonitor | null = null;
|
||||
|
||||
/**
|
||||
* 待迁移的持久化实体
|
||||
*
|
||||
* Pending persistent entities for migration.
|
||||
*/
|
||||
private _pendingPersistentEntities: Entity[] = [];
|
||||
|
||||
/**
|
||||
* 默认场景ID
|
||||
*/
|
||||
@@ -104,17 +113,33 @@ export class SceneManager implements IService {
|
||||
* 设置当前场景(立即切换)
|
||||
*
|
||||
* 会自动处理旧场景的结束和新场景的初始化。
|
||||
* 持久化实体会自动迁移到新场景。
|
||||
*
|
||||
* @param scene - 要设置的场景实例
|
||||
* @returns 返回设置的场景实例,便于链式调用
|
||||
* Set current scene (immediate transition).
|
||||
* Automatically handles old scene cleanup and new scene initialization.
|
||||
* Persistent entities are automatically migrated to the new scene.
|
||||
*
|
||||
* @param scene - 要设置的场景实例 | Scene instance to set
|
||||
* @returns 返回设置的场景实例,便于链式调用 | Returns the scene for chaining
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const gameScene = sceneManager.setScene(new GameScene());
|
||||
* console.log(gameScene.name); // 可以立即使用返回的场景
|
||||
* console.log(gameScene.name);
|
||||
* ```
|
||||
*/
|
||||
public setScene<T extends IScene>(scene: T): T {
|
||||
// 从当前场景提取持久化实体
|
||||
const currentScene = this.currentScene;
|
||||
if (currentScene && currentScene instanceof Scene) {
|
||||
this._pendingPersistentEntities = currentScene.extractPersistentEntities();
|
||||
if (this._pendingPersistentEntities.length > 0) {
|
||||
this._logger.debug(
|
||||
`Extracted ${this._pendingPersistentEntities.length} persistent entities for migration`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 移除旧场景
|
||||
this._defaultWorld.removeAllScenes();
|
||||
|
||||
@@ -127,6 +152,15 @@ export class SceneManager implements IService {
|
||||
this._defaultWorld.createScene(SceneManager.DEFAULT_SCENE_ID, scene);
|
||||
this._defaultWorld.setSceneActive(SceneManager.DEFAULT_SCENE_ID, true);
|
||||
|
||||
// 迁移持久化实体到新场景
|
||||
if (this._pendingPersistentEntities.length > 0 && scene instanceof Scene) {
|
||||
scene.receiveMigratedEntities(this._pendingPersistentEntities);
|
||||
this._logger.debug(
|
||||
`Migrated ${this._pendingPersistentEntities.length} persistent entities to new scene`
|
||||
);
|
||||
this._pendingPersistentEntities = [];
|
||||
}
|
||||
|
||||
// 重建ECS API
|
||||
if (scene.querySystem && scene.eventSystem) {
|
||||
this._ecsAPI = createECSAPI(scene, scene.querySystem, scene.eventSystem);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export { Entity } from './Entity';
|
||||
export { Component } from './Component';
|
||||
export { EEntityLifecyclePolicy } from './Core/EntityLifecyclePolicy';
|
||||
export { ECSEventType, EventPriority, EVENT_TYPES, EventTypeValidator } from './CoreEvents';
|
||||
export * from './Systems';
|
||||
export * from './Utils';
|
||||
|
||||
424
packages/core/tests/ECS/PersistentEntity.test.ts
Normal file
424
packages/core/tests/ECS/PersistentEntity.test.ts
Normal file
@@ -0,0 +1,424 @@
|
||||
import { Entity } from '../../src/ECS/Entity';
|
||||
import { Component } from '../../src/ECS/Component';
|
||||
import { Scene } from '../../src/ECS/Scene';
|
||||
import { SceneManager } from '../../src/ECS/SceneManager';
|
||||
import { EEntityLifecyclePolicy } from '../../src/ECS/Core/EntityLifecyclePolicy';
|
||||
|
||||
// 测试组件
|
||||
class PositionComponent extends Component {
|
||||
public x: number;
|
||||
public y: number;
|
||||
|
||||
constructor(x: number = 0, y: number = 0) {
|
||||
super();
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
}
|
||||
|
||||
class PlayerComponent extends Component {
|
||||
public name: string;
|
||||
public score: number;
|
||||
|
||||
constructor(name: string = 'Player', score: number = 0) {
|
||||
super();
|
||||
this.name = name;
|
||||
this.score = score;
|
||||
}
|
||||
}
|
||||
|
||||
class EnemyComponent extends Component {
|
||||
public type: string;
|
||||
|
||||
constructor(type: string = 'normal') {
|
||||
super();
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
// 测试场景
|
||||
class TestScene extends Scene {
|
||||
public initializeCalled = false;
|
||||
|
||||
override initialize(): void {
|
||||
this.initializeCalled = true;
|
||||
}
|
||||
}
|
||||
|
||||
describe('PersistentEntity - 持久化实体测试', () => {
|
||||
describe('Entity.setPersistent', () => {
|
||||
let scene: Scene;
|
||||
|
||||
beforeEach(() => {
|
||||
scene = new Scene();
|
||||
});
|
||||
|
||||
test('默认实体应为 SceneLocal 策略', () => {
|
||||
const entity = scene.createEntity('NormalEntity');
|
||||
|
||||
expect(entity.lifecyclePolicy).toBe(EEntityLifecyclePolicy.SceneLocal);
|
||||
expect(entity.isPersistent).toBe(false);
|
||||
});
|
||||
|
||||
test('setPersistent() 应标记实体为持久化', () => {
|
||||
const entity = scene.createEntity('Player');
|
||||
entity.setPersistent();
|
||||
|
||||
expect(entity.lifecyclePolicy).toBe(EEntityLifecyclePolicy.Persistent);
|
||||
expect(entity.isPersistent).toBe(true);
|
||||
});
|
||||
|
||||
test('setPersistent() 应支持链式调用', () => {
|
||||
const entity = scene.createEntity('Player').setPersistent();
|
||||
entity.addComponent(new PositionComponent(100, 200));
|
||||
|
||||
expect(entity.isPersistent).toBe(true);
|
||||
expect(entity.hasComponent(PositionComponent)).toBe(true);
|
||||
});
|
||||
|
||||
test('setSceneLocal() 应恢复为默认策略', () => {
|
||||
const entity = scene.createEntity('Player');
|
||||
entity.setPersistent();
|
||||
expect(entity.isPersistent).toBe(true);
|
||||
|
||||
entity.setSceneLocal();
|
||||
expect(entity.isPersistent).toBe(false);
|
||||
expect(entity.lifecyclePolicy).toBe(EEntityLifecyclePolicy.SceneLocal);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Scene.findPersistentEntities', () => {
|
||||
let scene: Scene;
|
||||
|
||||
beforeEach(() => {
|
||||
scene = new Scene();
|
||||
});
|
||||
|
||||
test('应返回所有持久化实体', () => {
|
||||
// 创建混合实体
|
||||
const player = scene.createEntity('Player').setPersistent();
|
||||
const enemy1 = scene.createEntity('Enemy1');
|
||||
const gameManager = scene.createEntity('GameManager').setPersistent();
|
||||
const enemy2 = scene.createEntity('Enemy2');
|
||||
|
||||
const persistentEntities = scene.findPersistentEntities();
|
||||
|
||||
expect(persistentEntities.length).toBe(2);
|
||||
expect(persistentEntities).toContain(player);
|
||||
expect(persistentEntities).toContain(gameManager);
|
||||
expect(persistentEntities).not.toContain(enemy1);
|
||||
expect(persistentEntities).not.toContain(enemy2);
|
||||
});
|
||||
|
||||
test('没有持久化实体时应返回空数组', () => {
|
||||
scene.createEntity('Enemy1');
|
||||
scene.createEntity('Enemy2');
|
||||
|
||||
const persistentEntities = scene.findPersistentEntities();
|
||||
|
||||
expect(persistentEntities).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Scene.extractPersistentEntities', () => {
|
||||
let scene: Scene;
|
||||
|
||||
beforeEach(() => {
|
||||
scene = new Scene();
|
||||
});
|
||||
|
||||
test('应提取并从场景中移除持久化实体', () => {
|
||||
const player = scene.createEntity('Player').setPersistent();
|
||||
player.addComponent(new PositionComponent(100, 200));
|
||||
|
||||
const enemy = scene.createEntity('Enemy');
|
||||
|
||||
expect(scene.entities.count).toBe(2);
|
||||
|
||||
const extracted = scene.extractPersistentEntities();
|
||||
|
||||
expect(extracted.length).toBe(1);
|
||||
expect(extracted[0]).toBe(player);
|
||||
expect(scene.entities.count).toBe(1);
|
||||
expect(scene.findEntity('Player')).toBeNull();
|
||||
expect(scene.findEntity('Enemy')).toBe(enemy);
|
||||
});
|
||||
|
||||
test('提取后实体的 scene 引用应为 null', () => {
|
||||
const player = scene.createEntity('Player').setPersistent();
|
||||
|
||||
const extracted = scene.extractPersistentEntities();
|
||||
|
||||
expect(extracted[0].scene).toBeNull();
|
||||
});
|
||||
|
||||
test('提取后实体的组件数据应保留', () => {
|
||||
const player = scene.createEntity('Player').setPersistent();
|
||||
player.addComponent(new PositionComponent(100, 200));
|
||||
player.addComponent(new PlayerComponent('Hero', 999));
|
||||
|
||||
const extracted = scene.extractPersistentEntities();
|
||||
|
||||
// 组件数据应保留(虽然 scene 为 null,组件缓存仍有效)
|
||||
expect(extracted[0].components.length).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Scene.receiveMigratedEntities', () => {
|
||||
test('应将迁移的实体添加到新场景', () => {
|
||||
const sourceScene = new Scene();
|
||||
const targetScene = new Scene();
|
||||
|
||||
// 在源场景创建持久化实体
|
||||
const player = sourceScene.createEntity('Player').setPersistent();
|
||||
player.addComponent(new PositionComponent(100, 200));
|
||||
player.addComponent(new PlayerComponent('Hero', 500));
|
||||
|
||||
// 提取并迁移
|
||||
const extracted = sourceScene.extractPersistentEntities();
|
||||
targetScene.receiveMigratedEntities(extracted);
|
||||
|
||||
// 验证实体已迁移
|
||||
expect(targetScene.entities.count).toBe(1);
|
||||
expect(targetScene.findEntity('Player')).toBe(player);
|
||||
expect(player.scene).toBe(targetScene);
|
||||
});
|
||||
|
||||
test('迁移后组件数据应完整保留', () => {
|
||||
const sourceScene = new Scene();
|
||||
const targetScene = new Scene();
|
||||
|
||||
const player = sourceScene.createEntity('Player').setPersistent();
|
||||
player.addComponent(new PositionComponent(100, 200));
|
||||
player.addComponent(new PlayerComponent('Hero', 999));
|
||||
|
||||
const extracted = sourceScene.extractPersistentEntities();
|
||||
targetScene.receiveMigratedEntities(extracted);
|
||||
|
||||
// 验证组件数据
|
||||
const migratedPlayer = targetScene.findEntity('Player')!;
|
||||
const position = migratedPlayer.getComponent(PositionComponent);
|
||||
const playerComp = migratedPlayer.getComponent(PlayerComponent);
|
||||
|
||||
expect(position).not.toBeNull();
|
||||
expect(position!.x).toBe(100);
|
||||
expect(position!.y).toBe(200);
|
||||
expect(playerComp).not.toBeNull();
|
||||
expect(playerComp!.name).toBe('Hero');
|
||||
expect(playerComp!.score).toBe(999);
|
||||
});
|
||||
|
||||
test('迁移后实体应能被查询系统找到', () => {
|
||||
const sourceScene = new Scene();
|
||||
const targetScene = new Scene();
|
||||
|
||||
const player = sourceScene.createEntity('Player').setPersistent();
|
||||
player.addComponent(new PositionComponent(100, 200));
|
||||
|
||||
const extracted = sourceScene.extractPersistentEntities();
|
||||
targetScene.receiveMigratedEntities(extracted);
|
||||
|
||||
// 通过查询系统查找
|
||||
const result = targetScene.queryAll(PositionComponent);
|
||||
expect(result.entities.length).toBe(1);
|
||||
expect(result.entities[0]).toBe(player);
|
||||
});
|
||||
});
|
||||
|
||||
describe('SceneManager 场景切换迁移', () => {
|
||||
let sceneManager: SceneManager;
|
||||
|
||||
beforeEach(() => {
|
||||
sceneManager = new SceneManager();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sceneManager.destroy();
|
||||
});
|
||||
|
||||
test('场景切换时应自动迁移持久化实体', () => {
|
||||
// 设置初始场景
|
||||
const scene1 = new TestScene();
|
||||
sceneManager.setScene(scene1);
|
||||
|
||||
// 创建持久化实体和普通实体
|
||||
const player = scene1.createEntity('Player').setPersistent();
|
||||
player.addComponent(new PositionComponent(100, 200));
|
||||
player.addComponent(new PlayerComponent('Hero', 500));
|
||||
|
||||
const enemy = scene1.createEntity('Enemy');
|
||||
enemy.addComponent(new EnemyComponent('boss'));
|
||||
|
||||
expect(scene1.entities.count).toBe(2);
|
||||
|
||||
// 切换到新场景
|
||||
const scene2 = new TestScene();
|
||||
sceneManager.setScene(scene2);
|
||||
|
||||
// 验证:player 应迁移到新场景,enemy 应被销毁
|
||||
expect(scene2.entities.count).toBe(1);
|
||||
expect(scene2.findEntity('Player')).toBe(player);
|
||||
expect(scene2.findEntity('Enemy')).toBeNull();
|
||||
expect(player.scene).toBe(scene2);
|
||||
});
|
||||
|
||||
test('迁移后组件状态应保持不变', () => {
|
||||
const scene1 = new TestScene();
|
||||
sceneManager.setScene(scene1);
|
||||
|
||||
const player = scene1.createEntity('Player').setPersistent();
|
||||
player.addComponent(new PositionComponent(100, 200));
|
||||
const playerComp = player.addComponent(new PlayerComponent('Hero', 500));
|
||||
|
||||
// 修改组件状态
|
||||
playerComp.score = 999;
|
||||
|
||||
// 切换场景
|
||||
const scene2 = new TestScene();
|
||||
sceneManager.setScene(scene2);
|
||||
|
||||
// 验证组件状态
|
||||
const migratedPlayer = scene2.findEntity('Player')!;
|
||||
const position = migratedPlayer.getComponent(PositionComponent);
|
||||
const migratedPlayerComp = migratedPlayer.getComponent(PlayerComponent);
|
||||
|
||||
expect(position!.x).toBe(100);
|
||||
expect(position!.y).toBe(200);
|
||||
expect(migratedPlayerComp!.score).toBe(999);
|
||||
});
|
||||
|
||||
test('多个持久化实体应全部迁移', () => {
|
||||
const scene1 = new TestScene();
|
||||
sceneManager.setScene(scene1);
|
||||
|
||||
const player = scene1.createEntity('Player').setPersistent();
|
||||
const audioManager = scene1.createEntity('AudioManager').setPersistent();
|
||||
const gameState = scene1.createEntity('GameState').setPersistent();
|
||||
const enemy = scene1.createEntity('Enemy'); // 普通实体
|
||||
|
||||
expect(scene1.entities.count).toBe(4);
|
||||
|
||||
const scene2 = new TestScene();
|
||||
sceneManager.setScene(scene2);
|
||||
|
||||
expect(scene2.entities.count).toBe(3);
|
||||
expect(scene2.findEntity('Player')).toBe(player);
|
||||
expect(scene2.findEntity('AudioManager')).toBe(audioManager);
|
||||
expect(scene2.findEntity('GameState')).toBe(gameState);
|
||||
expect(scene2.findEntity('Enemy')).toBeNull();
|
||||
});
|
||||
|
||||
test('没有持久化实体时场景切换应正常工作', () => {
|
||||
const scene1 = new TestScene();
|
||||
sceneManager.setScene(scene1);
|
||||
|
||||
scene1.createEntity('Enemy1');
|
||||
scene1.createEntity('Enemy2');
|
||||
|
||||
const scene2 = new TestScene();
|
||||
sceneManager.setScene(scene2);
|
||||
|
||||
expect(scene2.entities.count).toBe(0);
|
||||
});
|
||||
|
||||
test('延迟场景切换应正确迁移持久化实体', () => {
|
||||
const scene1 = new TestScene();
|
||||
sceneManager.setScene(scene1);
|
||||
|
||||
const player = scene1.createEntity('Player').setPersistent();
|
||||
player.addComponent(new PlayerComponent('Hero', 100));
|
||||
|
||||
// 延迟加载
|
||||
const scene2 = new TestScene();
|
||||
sceneManager.loadScene(scene2);
|
||||
|
||||
// 此时还未切换
|
||||
expect(sceneManager.currentScene).toBe(scene1);
|
||||
expect(scene1.findEntity('Player')).toBe(player);
|
||||
|
||||
// 触发更新,执行延迟切换
|
||||
sceneManager.update();
|
||||
|
||||
// 验证迁移
|
||||
expect(sceneManager.currentScene).toBe(scene2);
|
||||
expect(scene2.findEntity('Player')).toBe(player);
|
||||
expect(player.scene).toBe(scene2);
|
||||
});
|
||||
|
||||
test('连续场景切换应正确迁移持久化实体', () => {
|
||||
const scene1 = new TestScene();
|
||||
sceneManager.setScene(scene1);
|
||||
|
||||
const player = scene1.createEntity('Player').setPersistent();
|
||||
|
||||
// 第一次切换
|
||||
const scene2 = new TestScene();
|
||||
sceneManager.setScene(scene2);
|
||||
expect(scene2.findEntity('Player')).toBe(player);
|
||||
|
||||
// 第二次切换
|
||||
const scene3 = new TestScene();
|
||||
sceneManager.setScene(scene3);
|
||||
expect(scene3.findEntity('Player')).toBe(player);
|
||||
|
||||
// 第三次切换
|
||||
const scene4 = new TestScene();
|
||||
sceneManager.setScene(scene4);
|
||||
expect(scene4.findEntity('Player')).toBe(player);
|
||||
expect(player.scene).toBe(scene4);
|
||||
});
|
||||
});
|
||||
|
||||
describe('边界情况', () => {
|
||||
test('实体销毁后不应被迁移', () => {
|
||||
const sceneManager = new SceneManager();
|
||||
const scene1 = new TestScene();
|
||||
sceneManager.setScene(scene1);
|
||||
|
||||
const player = scene1.createEntity('Player').setPersistent();
|
||||
player.destroy();
|
||||
|
||||
const scene2 = new TestScene();
|
||||
sceneManager.setScene(scene2);
|
||||
|
||||
expect(scene2.entities.count).toBe(0);
|
||||
sceneManager.destroy();
|
||||
});
|
||||
|
||||
test('动态切换持久化状态应生效', () => {
|
||||
const sceneManager = new SceneManager();
|
||||
const scene1 = new TestScene();
|
||||
sceneManager.setScene(scene1);
|
||||
|
||||
const entity = scene1.createEntity('DynamicEntity');
|
||||
expect(entity.isPersistent).toBe(false);
|
||||
|
||||
// 动态设为持久化
|
||||
entity.setPersistent();
|
||||
expect(entity.isPersistent).toBe(true);
|
||||
|
||||
const scene2 = new TestScene();
|
||||
sceneManager.setScene(scene2);
|
||||
|
||||
expect(scene2.findEntity('DynamicEntity')).toBe(entity);
|
||||
sceneManager.destroy();
|
||||
});
|
||||
|
||||
test('动态取消持久化状态应生效', () => {
|
||||
const sceneManager = new SceneManager();
|
||||
const scene1 = new TestScene();
|
||||
sceneManager.setScene(scene1);
|
||||
|
||||
const entity = scene1.createEntity('DynamicEntity').setPersistent();
|
||||
|
||||
// 动态取消持久化
|
||||
entity.setSceneLocal();
|
||||
|
||||
const scene2 = new TestScene();
|
||||
sceneManager.setScene(scene2);
|
||||
|
||||
expect(scene2.findEntity('DynamicEntity')).toBeNull();
|
||||
sceneManager.destroy();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user