重构 Component基类违反ECS纯粹性问题
This commit is contained in:
@@ -1,21 +1,30 @@
|
|||||||
import type { IComponent } from '../Types';
|
import type { IComponent } from '../Types';
|
||||||
import type { Entity } from './Entity';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 游戏组件基类
|
* 游戏组件基类
|
||||||
*
|
*
|
||||||
* ECS架构中的组件(Component),用于实现具体的游戏功能。
|
* ECS架构中的组件(Component)应该是纯数据容器。
|
||||||
* 组件包含数据和行为,可以被添加到实体上以扩展实体的功能。
|
* 所有游戏逻辑应该在 EntitySystem 中实现,而不是在组件内部。
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
|
* 推荐做法:纯数据组件
|
||||||
* ```typescript
|
* ```typescript
|
||||||
* class HealthComponent extends Component {
|
* class HealthComponent extends Component {
|
||||||
* public health: number = 100;
|
* public health: number = 100;
|
||||||
|
* public maxHealth: number = 100;
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
*
|
*
|
||||||
* public takeDamage(damage: number): void {
|
* @example
|
||||||
* this.health -= damage;
|
* 推荐做法:在 System 中处理逻辑
|
||||||
* if (this.health <= 0) {
|
* ```typescript
|
||||||
* this.entity.destroy();
|
* class HealthSystem extends EntitySystem {
|
||||||
|
* process(entities: Entity[]): void {
|
||||||
|
* for (const entity of entities) {
|
||||||
|
* const health = entity.getComponent(HealthComponent);
|
||||||
|
* if (health && health.health <= 0) {
|
||||||
|
* entity.destroy();
|
||||||
|
* }
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
@@ -36,29 +45,6 @@ export abstract class Component implements IComponent {
|
|||||||
*/
|
*/
|
||||||
public readonly id: number;
|
public readonly id: number;
|
||||||
|
|
||||||
/**
|
|
||||||
* 组件所属的实体
|
|
||||||
*
|
|
||||||
* 指向拥有此组件的实体实例。
|
|
||||||
*/
|
|
||||||
public entity!: Entity;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 组件启用状态
|
|
||||||
*
|
|
||||||
* 控制组件是否参与更新循环。
|
|
||||||
*/
|
|
||||||
private _enabled: boolean = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新顺序
|
|
||||||
*
|
|
||||||
* 决定组件在更新循环中的执行顺序。
|
|
||||||
*
|
|
||||||
* @see EntitySystem
|
|
||||||
*/
|
|
||||||
private _updateOrder: number = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建组件实例
|
* 创建组件实例
|
||||||
*
|
*
|
||||||
@@ -68,63 +54,14 @@ export abstract class Component implements IComponent {
|
|||||||
this.id = Component._idGenerator++;
|
this.id = Component._idGenerator++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取组件启用状态
|
|
||||||
*
|
|
||||||
* 组件的实际启用状态取决于自身状态和所属实体的状态。
|
|
||||||
*
|
|
||||||
* @deprecated 不符合ECS架构规范,建议自己实现DisabledComponent标记组件替代
|
|
||||||
* @returns 如果组件和所属实体都启用则返回true
|
|
||||||
*/
|
|
||||||
public get enabled(): boolean {
|
|
||||||
return this.entity ? this.entity.enabled && this._enabled : this._enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置组件启用状态
|
|
||||||
*
|
|
||||||
* 当状态改变时会触发相应的生命周期回调。
|
|
||||||
*
|
|
||||||
* @deprecated 不符合ECS架构规范,建议自己实现DisabledComponent标记组件替代
|
|
||||||
* @param value - 新的启用状态
|
|
||||||
*/
|
|
||||||
public set enabled(value: boolean) {
|
|
||||||
if (this._enabled !== value) {
|
|
||||||
this._enabled = value;
|
|
||||||
if (this._enabled) {
|
|
||||||
this.onEnabled();
|
|
||||||
} else {
|
|
||||||
this.onDisabled();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取更新顺序
|
|
||||||
*
|
|
||||||
* @deprecated 不符合ECS架构规范,更新顺序应该由EntitySystem管理
|
|
||||||
* @see EntitySystem
|
|
||||||
* @returns 组件的更新顺序值
|
|
||||||
*/
|
|
||||||
public get updateOrder(): number {
|
|
||||||
return this._updateOrder;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置更新顺序
|
|
||||||
*
|
|
||||||
* @deprecated 不符合ECS架构规范,更新顺序应该由EntitySystem管理
|
|
||||||
* @see EntitySystem
|
|
||||||
* @param value - 新的更新顺序值
|
|
||||||
*/
|
|
||||||
public set updateOrder(value: number) {
|
|
||||||
this._updateOrder = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 组件添加到实体时的回调
|
* 组件添加到实体时的回调
|
||||||
*
|
*
|
||||||
* 当组件被添加到实体时调用,可以在此方法中进行初始化操作。
|
* 当组件被添加到实体时调用,可以在此方法中进行初始化操作。
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
* 这是一个生命周期钩子,用于组件的初始化逻辑。
|
||||||
|
* 虽然保留此方法,但建议将复杂的初始化逻辑放在 System 中处理。
|
||||||
*/
|
*/
|
||||||
public onAddedToEntity(): void {
|
public onAddedToEntity(): void {
|
||||||
}
|
}
|
||||||
@@ -133,32 +70,12 @@ export abstract class Component implements IComponent {
|
|||||||
* 组件从实体移除时的回调
|
* 组件从实体移除时的回调
|
||||||
*
|
*
|
||||||
* 当组件从实体中移除时调用,可以在此方法中进行清理操作。
|
* 当组件从实体中移除时调用,可以在此方法中进行清理操作。
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
* 这是一个生命周期钩子,用于组件的清理逻辑。
|
||||||
|
* 虽然保留此方法,但建议将复杂的清理逻辑放在 System 中处理。
|
||||||
*/
|
*/
|
||||||
public onRemovedFromEntity(): void {
|
public onRemovedFromEntity(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 组件启用时的回调
|
|
||||||
*
|
|
||||||
* 当组件被启用时调用。
|
|
||||||
*/
|
|
||||||
public onEnabled(): void {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 组件禁用时的回调
|
|
||||||
*
|
|
||||||
* 当组件被禁用时调用。
|
|
||||||
*/
|
|
||||||
public onDisabled(): void {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新组件
|
|
||||||
*
|
|
||||||
* @deprecated 不符合ECS架构规范,建议使用EntitySystem来处理更新逻辑
|
|
||||||
*/
|
|
||||||
public update(): void {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -105,11 +105,6 @@ export class Entity {
|
|||||||
*/
|
*/
|
||||||
public scene: IScene | null = null;
|
public scene: IScene | null = null;
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新间隔
|
|
||||||
*/
|
|
||||||
public updateInterval: number = 1;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 销毁状态标志
|
* 销毁状态标志
|
||||||
*/
|
*/
|
||||||
@@ -334,8 +329,6 @@ export class Entity {
|
|||||||
|
|
||||||
const typeId = ComponentRegistry.getBitIndex(componentType);
|
const typeId = ComponentRegistry.getBitIndex(componentType);
|
||||||
|
|
||||||
component.entity = this;
|
|
||||||
|
|
||||||
this._componentsByTypeId[typeId] = component;
|
this._componentsByTypeId[typeId] = component;
|
||||||
|
|
||||||
const denseIndex = this.components.length;
|
const denseIndex = this.components.length;
|
||||||
@@ -529,8 +522,6 @@ export class Entity {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
component.entity = null as any;
|
|
||||||
|
|
||||||
// 通知所有相关的QuerySystem组件已变动
|
// 通知所有相关的QuerySystem组件已变动
|
||||||
Entity.notifyQuerySystems(this);
|
Entity.notifyQuerySystems(this);
|
||||||
}
|
}
|
||||||
@@ -568,8 +559,6 @@ export class Entity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
component.onRemovedFromEntity();
|
component.onRemovedFromEntity();
|
||||||
|
|
||||||
component.entity = null as any;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.components.length = 0;
|
this.components.length = 0;
|
||||||
@@ -833,26 +822,6 @@ export class Entity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新实体
|
|
||||||
*
|
|
||||||
* 调用所有组件的更新方法,并更新子实体。
|
|
||||||
*/
|
|
||||||
public update(): void {
|
|
||||||
if (!this.activeInHierarchy || this._isDestroyed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const component of this.components) {
|
|
||||||
if (component.enabled) {
|
|
||||||
component.update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const child of this._children) {
|
|
||||||
child.update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 销毁实体
|
* 销毁实体
|
||||||
|
|||||||
@@ -152,13 +152,4 @@ export interface ISceneConfig {
|
|||||||
* 场景名称
|
* 场景名称
|
||||||
*/
|
*/
|
||||||
name?: string;
|
name?: string;
|
||||||
/**
|
|
||||||
* 调试配置
|
|
||||||
*/
|
|
||||||
debug?: boolean;
|
|
||||||
/**
|
|
||||||
* 是否启用实体直接更新
|
|
||||||
* @default false
|
|
||||||
*/
|
|
||||||
enableEntityDirectUpdate?: boolean;
|
|
||||||
}
|
}
|
||||||
@@ -95,11 +95,6 @@ export class Scene implements IScene {
|
|||||||
this.name = config.name;
|
this.name = config.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 配置实体直接更新选项
|
|
||||||
if (config?.enableEntityDirectUpdate !== undefined) {
|
|
||||||
this.entities.setEnableEntityDirectUpdate(config.enableEntityDirectUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Entity.eventBus) {
|
if (!Entity.eventBus) {
|
||||||
Entity.eventBus = new EventBus(false);
|
Entity.eventBus = new EventBus(false);
|
||||||
}
|
}
|
||||||
@@ -180,16 +175,13 @@ export class Scene implements IScene {
|
|||||||
* 更新场景
|
* 更新场景
|
||||||
*/
|
*/
|
||||||
public update() {
|
public update() {
|
||||||
// 更新实体列表
|
// 更新实体列表(处理延迟操作)
|
||||||
this.entities.updateLists();
|
this.entities.updateLists();
|
||||||
|
|
||||||
// 更新实体处理器
|
// 更新实体处理器
|
||||||
if (this.entityProcessors != null)
|
if (this.entityProcessors != null)
|
||||||
this.entityProcessors.update();
|
this.entityProcessors.update();
|
||||||
|
|
||||||
// 更新实体
|
|
||||||
this.entities.update();
|
|
||||||
|
|
||||||
// 更新实体处理器后处理
|
// 更新实体处理器后处理
|
||||||
if (this.entityProcessors != null)
|
if (this.entityProcessors != null)
|
||||||
this.entityProcessors.lateUpdate();
|
this.entityProcessors.lateUpdate();
|
||||||
|
|||||||
@@ -16,10 +16,6 @@ export class EntityList {
|
|||||||
// 延迟操作队列
|
// 延迟操作队列
|
||||||
private _entitiesToAdd: Entity[] = [];
|
private _entitiesToAdd: Entity[] = [];
|
||||||
private _entitiesToRemove: Entity[] = [];
|
private _entitiesToRemove: Entity[] = [];
|
||||||
private _isUpdating = false;
|
|
||||||
|
|
||||||
// 是否启用实体直接更新
|
|
||||||
private _enableEntityDirectUpdate = false;
|
|
||||||
|
|
||||||
public get count(): number {
|
public get count(): number {
|
||||||
return this.buffer.length;
|
return this.buffer.length;
|
||||||
@@ -30,24 +26,12 @@ export class EntityList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置是否启用实体直接更新
|
* 添加实体
|
||||||
*/
|
|
||||||
public setEnableEntityDirectUpdate(enabled: boolean): void {
|
|
||||||
this._enableEntityDirectUpdate = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 添加实体(立即添加或延迟添加)
|
|
||||||
* @param entity 要添加的实体
|
* @param entity 要添加的实体
|
||||||
*/
|
*/
|
||||||
public add(entity: Entity): void {
|
public add(entity: Entity): void {
|
||||||
if (this._isUpdating) {
|
|
||||||
// 如果正在更新中,延迟添加
|
|
||||||
this._entitiesToAdd.push(entity);
|
|
||||||
} else {
|
|
||||||
this.addImmediate(entity);
|
this.addImmediate(entity);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 立即添加实体
|
* 立即添加实体
|
||||||
@@ -67,17 +51,12 @@ export class EntityList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 移除实体(立即移除或延迟移除)
|
* 移除实体
|
||||||
* @param entity 要移除的实体
|
* @param entity 要移除的实体
|
||||||
*/
|
*/
|
||||||
public remove(entity: Entity): void {
|
public remove(entity: Entity): void {
|
||||||
if (this._isUpdating) {
|
|
||||||
// 如果正在更新中,延迟移除
|
|
||||||
this._entitiesToRemove.push(entity);
|
|
||||||
} else {
|
|
||||||
this.removeImmediate(entity);
|
this.removeImmediate(entity);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 立即移除实体
|
* 立即移除实体
|
||||||
@@ -147,25 +126,11 @@ export class EntityList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新实体列表和实体
|
* 更新实体列表
|
||||||
|
*
|
||||||
|
* 处理延迟操作(添加/删除实体)
|
||||||
*/
|
*/
|
||||||
public update(): void {
|
public update(): void {
|
||||||
this._isUpdating = true;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 只有启用实体直接更新时才遍历更新实体
|
|
||||||
if (this._enableEntityDirectUpdate) {
|
|
||||||
for (let i = 0; i < this.buffer.length; i++) {
|
|
||||||
const entity = this.buffer[i];
|
|
||||||
if (entity.enabled && !entity.isDestroyed) {
|
|
||||||
entity.update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
this._isUpdating = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理延迟操作
|
// 处理延迟操作
|
||||||
this.updateLists();
|
this.updateLists();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,31 +5,19 @@
|
|||||||
/**
|
/**
|
||||||
* 组件接口
|
* 组件接口
|
||||||
*
|
*
|
||||||
* 定义组件的基本契约,所有组件都应该实现此接口
|
* 定义组件的基本契约。
|
||||||
|
* 在 ECS 架构中,组件应该是纯数据容器,不包含业务逻辑。
|
||||||
*/
|
*/
|
||||||
export interface IComponent {
|
export interface IComponent {
|
||||||
/** 组件唯一标识符 */
|
/** 组件唯一标识符 */
|
||||||
readonly id: number;
|
readonly id: number;
|
||||||
/** 组件所属的实体ID */
|
/** 组件所属的实体ID */
|
||||||
entityId?: string | number;
|
entityId?: string | number;
|
||||||
/** 组件启用状态 */
|
|
||||||
enabled: boolean;
|
|
||||||
/** 更新顺序 */
|
|
||||||
updateOrder: number;
|
|
||||||
|
|
||||||
/** 组件添加到实体时的回调 */
|
/** 组件添加到实体时的回调 */
|
||||||
onAddedToEntity(): void;
|
onAddedToEntity(): void;
|
||||||
/** 组件从实体移除时的回调 */
|
/** 组件从实体移除时的回调 */
|
||||||
onRemovedFromEntity(): void;
|
onRemovedFromEntity(): void;
|
||||||
/** 组件启用时的回调 */
|
|
||||||
onEnabled(): void;
|
|
||||||
/** 组件禁用时的回调 */
|
|
||||||
onDisabled(): void;
|
|
||||||
/**
|
|
||||||
* 更新组件
|
|
||||||
* @deprecated 不符合ECS架构规范,建议使用EntitySystem来处理更新逻辑
|
|
||||||
*/
|
|
||||||
update(): void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -6,9 +6,6 @@ class TestComponent extends Component {
|
|||||||
public value: number = 100;
|
public value: number = 100;
|
||||||
public onAddedCalled = false;
|
public onAddedCalled = false;
|
||||||
public onRemovedCalled = false;
|
public onRemovedCalled = false;
|
||||||
public onEnabledCalled = false;
|
|
||||||
public onDisabledCalled = false;
|
|
||||||
public updateCalled = false;
|
|
||||||
|
|
||||||
public override onAddedToEntity(): void {
|
public override onAddedToEntity(): void {
|
||||||
this.onAddedCalled = true;
|
this.onAddedCalled = true;
|
||||||
@@ -17,18 +14,6 @@ class TestComponent extends Component {
|
|||||||
public override onRemovedFromEntity(): void {
|
public override onRemovedFromEntity(): void {
|
||||||
this.onRemovedCalled = true;
|
this.onRemovedCalled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override onEnabled(): void {
|
|
||||||
this.onEnabledCalled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override onDisabled(): void {
|
|
||||||
this.onDisabledCalled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override update(): void {
|
|
||||||
this.updateCalled = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class AnotherTestComponent extends Component {
|
class AnotherTestComponent extends Component {
|
||||||
@@ -73,83 +58,6 @@ describe('Component - 组件基类测试', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('启用状态管理', () => {
|
|
||||||
test('组件默认应该是启用的', () => {
|
|
||||||
expect(component.enabled).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('设置组件禁用状态应该工作', () => {
|
|
||||||
component.enabled = false;
|
|
||||||
expect(component.enabled).toBe(false);
|
|
||||||
expect(component.onDisabledCalled).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('重新启用组件应该工作', () => {
|
|
||||||
component.enabled = false;
|
|
||||||
component.onDisabledCalled = false;
|
|
||||||
component.onEnabledCalled = false;
|
|
||||||
|
|
||||||
component.enabled = true;
|
|
||||||
expect(component.enabled).toBe(true);
|
|
||||||
expect(component.onEnabledCalled).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('设置相同的状态不应该触发回调', () => {
|
|
||||||
component.enabled = true; // 已经是true
|
|
||||||
expect(component.onEnabledCalled).toBe(false);
|
|
||||||
|
|
||||||
component.enabled = false;
|
|
||||||
component.onDisabledCalled = false;
|
|
||||||
|
|
||||||
component.enabled = false; // 已经是false
|
|
||||||
expect(component.onDisabledCalled).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('组件启用状态应该受实体状态影响', () => {
|
|
||||||
entity.addComponent(component);
|
|
||||||
expect(component.enabled).toBe(true);
|
|
||||||
|
|
||||||
// 禁用实体应该让组件表现为禁用
|
|
||||||
entity.enabled = false;
|
|
||||||
expect(component.enabled).toBe(false);
|
|
||||||
|
|
||||||
// 重新启用实体
|
|
||||||
entity.enabled = true;
|
|
||||||
expect(component.enabled).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('组件自身禁用时即使实体启用也应该是禁用的', () => {
|
|
||||||
entity.addComponent(component);
|
|
||||||
|
|
||||||
component.enabled = false;
|
|
||||||
entity.enabled = true;
|
|
||||||
|
|
||||||
expect(component.enabled).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('没有实体时组件状态应该只取决于自身', () => {
|
|
||||||
// 组件还没有添加到实体
|
|
||||||
expect(component.enabled).toBe(true);
|
|
||||||
|
|
||||||
component.enabled = false;
|
|
||||||
expect(component.enabled).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('更新顺序', () => {
|
|
||||||
test('组件默认更新顺序应该是0', () => {
|
|
||||||
expect(component.updateOrder).toBe(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('应该能够设置更新顺序', () => {
|
|
||||||
component.updateOrder = 10;
|
|
||||||
expect(component.updateOrder).toBe(10);
|
|
||||||
|
|
||||||
component.updateOrder = -5;
|
|
||||||
expect(component.updateOrder).toBe(-5);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('生命周期回调', () => {
|
describe('生命周期回调', () => {
|
||||||
test('添加到实体时应该调用onAddedToEntity', () => {
|
test('添加到实体时应该调用onAddedToEntity', () => {
|
||||||
expect(component.onAddedCalled).toBe(false);
|
expect(component.onAddedCalled).toBe(false);
|
||||||
@@ -166,30 +74,6 @@ describe('Component - 组件基类测试', () => {
|
|||||||
expect(component.onRemovedCalled).toBe(true);
|
expect(component.onRemovedCalled).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('启用时应该调用onEnabled', () => {
|
|
||||||
component.enabled = false;
|
|
||||||
component.onEnabledCalled = false;
|
|
||||||
|
|
||||||
component.enabled = true;
|
|
||||||
expect(component.onEnabledCalled).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('禁用时应该调用onDisabled', () => {
|
|
||||||
expect(component.onDisabledCalled).toBe(false);
|
|
||||||
|
|
||||||
component.enabled = false;
|
|
||||||
expect(component.onDisabledCalled).toBe(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('更新方法', () => {
|
|
||||||
test('update方法应该可以被调用', () => {
|
|
||||||
expect(component.updateCalled).toBe(false);
|
|
||||||
|
|
||||||
component.update();
|
|
||||||
expect(component.updateCalled).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('基类的默认生命周期方法应该安全调用', () => {
|
test('基类的默认生命周期方法应该安全调用', () => {
|
||||||
const baseComponent = new (class extends Component {})();
|
const baseComponent = new (class extends Component {})();
|
||||||
|
|
||||||
@@ -197,51 +81,32 @@ describe('Component - 组件基类测试', () => {
|
|||||||
expect(() => {
|
expect(() => {
|
||||||
baseComponent.onAddedToEntity();
|
baseComponent.onAddedToEntity();
|
||||||
baseComponent.onRemovedFromEntity();
|
baseComponent.onRemovedFromEntity();
|
||||||
baseComponent.onEnabled();
|
|
||||||
baseComponent.onDisabled();
|
|
||||||
baseComponent.update();
|
|
||||||
}).not.toThrow();
|
}).not.toThrow();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('实体关联', () => {
|
describe('实体-组件关系', () => {
|
||||||
test('组件应该能够访问其所属的实体', () => {
|
test('组件可以被添加到实体', () => {
|
||||||
entity.addComponent(component);
|
|
||||||
expect(component.entity).toBe(entity);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('组件移除后entity引用行为', () => {
|
|
||||||
entity.addComponent(component);
|
|
||||||
expect(component.entity).toBe(entity);
|
|
||||||
|
|
||||||
entity.removeComponent(component);
|
|
||||||
// 移除后entity引用可能被清空,这是正常行为
|
|
||||||
// 具体行为取决于实现,这里只测试不会抛出异常
|
|
||||||
expect(() => {
|
expect(() => {
|
||||||
const _ = component.entity;
|
entity.addComponent(component);
|
||||||
}).not.toThrow();
|
}).not.toThrow();
|
||||||
|
|
||||||
|
expect(entity.hasComponent(TestComponent)).toBe(true);
|
||||||
|
expect(entity.getComponent(TestComponent)).toBe(component);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('组件可以从实体移除', () => {
|
||||||
|
entity.addComponent(component);
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
entity.removeComponent(component);
|
||||||
|
}).not.toThrow();
|
||||||
|
|
||||||
|
expect(entity.hasComponent(TestComponent)).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('边界情况', () => {
|
describe('边界情况', () => {
|
||||||
test('多次启用禁用应该工作正常', () => {
|
|
||||||
for (let i = 0; i < 10; i++) {
|
|
||||||
component.enabled = false;
|
|
||||||
expect(component.enabled).toBe(false);
|
|
||||||
|
|
||||||
component.enabled = true;
|
|
||||||
expect(component.enabled).toBe(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test('极端更新顺序值应该被接受', () => {
|
|
||||||
component.updateOrder = 999999;
|
|
||||||
expect(component.updateOrder).toBe(999999);
|
|
||||||
|
|
||||||
component.updateOrder = -999999;
|
|
||||||
expect(component.updateOrder).toBe(-999999);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('大量组件创建应该有不同的ID', () => {
|
test('大量组件创建应该有不同的ID', () => {
|
||||||
const components: Component[] = [];
|
const components: Component[] = [];
|
||||||
const count = 1000;
|
const count = 1000;
|
||||||
@@ -254,6 +119,16 @@ describe('Component - 组件基类测试', () => {
|
|||||||
const ids = new Set(components.map(c => c.id));
|
const ids = new Set(components.map(c => c.id));
|
||||||
expect(ids.size).toBe(count);
|
expect(ids.size).toBe(count);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('组件应该是纯数据容器', () => {
|
||||||
|
// 验证组件只有数据字段
|
||||||
|
const comp = new TestComponent();
|
||||||
|
expect(comp.value).toBe(100);
|
||||||
|
|
||||||
|
// 可以修改数据
|
||||||
|
comp.value = 200;
|
||||||
|
expect(comp.value).toBe(200);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('继承和多态', () => {
|
describe('继承和多态', () => {
|
||||||
@@ -269,9 +144,10 @@ describe('Component - 组件基类测试', () => {
|
|||||||
|
|
||||||
test('组件应该能够重写基类方法', () => {
|
test('组件应该能够重写基类方法', () => {
|
||||||
const test = new TestComponent();
|
const test = new TestComponent();
|
||||||
test.update();
|
|
||||||
|
|
||||||
expect(test.updateCalled).toBe(true);
|
// 重写生命周期方法应该工作
|
||||||
|
entity.addComponent(test);
|
||||||
|
expect(test.onAddedCalled).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -68,7 +68,7 @@ describe('Entity - 组件缓存优化测试', () => {
|
|||||||
expect(addedComponent).toBe(position);
|
expect(addedComponent).toBe(position);
|
||||||
expect(entity.components.length).toBe(1);
|
expect(entity.components.length).toBe(1);
|
||||||
expect(entity.components[0]).toBe(position);
|
expect(entity.components[0]).toBe(position);
|
||||||
expect(position.entity).toBe(entity);
|
expect(entity.hasComponent(TestPositionComponent)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('应该能够获取组件', () => {
|
test('应该能够获取组件', () => {
|
||||||
@@ -96,7 +96,7 @@ describe('Entity - 组件缓存优化测试', () => {
|
|||||||
entity.removeComponent(position);
|
entity.removeComponent(position);
|
||||||
expect(entity.components.length).toBe(0);
|
expect(entity.components.length).toBe(0);
|
||||||
expect(entity.hasComponent(TestPositionComponent)).toBe(false);
|
expect(entity.hasComponent(TestPositionComponent)).toBe(false);
|
||||||
expect(position.entity).toBeNull();
|
expect(entity.getComponent(TestPositionComponent)).toBeNull();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user