mirror of
https://github.com/Gongxh0901/kunpolibrary
synced 2025-11-05 05:45:48 +00:00
first commit
This commit is contained in:
112
src/ecmodule/Component.ts
Normal file
112
src/ecmodule/Component.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import { ComponentManager } from "./ComponentManager";
|
||||
import { Entity } from "./Entity";
|
||||
import { ObjectBase } from "./ObjectBase";
|
||||
|
||||
export abstract class Component extends ObjectBase {
|
||||
/** 组件名 */
|
||||
public name: string;
|
||||
|
||||
/** 组件类型 */
|
||||
public type: number;
|
||||
|
||||
/** 是否需要更新 */
|
||||
public needUpdate: boolean;
|
||||
|
||||
/** 所属实体 */
|
||||
public entity: Entity;
|
||||
|
||||
/** 所属组件管理器 */
|
||||
public componentManager: ComponentManager;
|
||||
|
||||
/** 是否需要销毁 */
|
||||
public _needDestroy: boolean;
|
||||
|
||||
/** 更新ID */
|
||||
public _updateId: number = -1;
|
||||
|
||||
/** 是否更新中 */
|
||||
public get _updating(): boolean {
|
||||
return this._updateId != -1;
|
||||
}
|
||||
|
||||
/** 生命周期函数 添加到实体 */
|
||||
public _add(): void {
|
||||
this.onAdd();
|
||||
}
|
||||
|
||||
/** 生命周期函数 销毁 */
|
||||
public _destroy(): void {
|
||||
this.onDestroy();
|
||||
}
|
||||
|
||||
/** 生命周期函数 添加到实体后 在这个函数中可以获取其他组件 */
|
||||
public _enter(): void {
|
||||
// 自动开启更新
|
||||
if (this.needUpdate) {
|
||||
this.componentManager.startUpdateComponent(this);
|
||||
}
|
||||
this.onEnter();
|
||||
}
|
||||
|
||||
/** 生命周期函数 从实体中移除 */
|
||||
public _remove(): void {
|
||||
this.stopUpdate();
|
||||
this.onRemove();
|
||||
this.componentManager._destroyComponent(this);
|
||||
}
|
||||
|
||||
/** 更新 */
|
||||
public _update(dt: number): void {
|
||||
this.onUpdate(dt);
|
||||
}
|
||||
|
||||
/** 开启更新 */
|
||||
public startUpdate(): void {
|
||||
if (!this.needUpdate) {
|
||||
this.needUpdate = true;
|
||||
this.componentManager?.startUpdateComponent(this);
|
||||
}
|
||||
}
|
||||
|
||||
/** 停止更新 */
|
||||
public stopUpdate(): void {
|
||||
if (this.needUpdate) {
|
||||
this.needUpdate = false;
|
||||
this.componentManager?.stopUpdateComponent(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组件
|
||||
* @param {number} componentType 组件类型
|
||||
* @returns {T}
|
||||
*/
|
||||
public getComponent<T extends Component>(componentType: number): T {
|
||||
return this.entity.getComponent<T>(componentType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除自己
|
||||
*/
|
||||
public destroySelf(): void {
|
||||
this.entity.removeComponent(this.type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 被添加到实体 对应onDestroy
|
||||
*/
|
||||
protected onAdd(): void { }
|
||||
|
||||
/**
|
||||
* 组件被销毁 对应onAdd
|
||||
*/
|
||||
protected onDestroy(): void { }
|
||||
|
||||
protected onUpdate(dt: number): void { }
|
||||
|
||||
/** 可在此方法获取实体其他组件 */
|
||||
protected abstract onEnter(): void;
|
||||
|
||||
/** 从实体中删除 */
|
||||
protected abstract onRemove(): void;
|
||||
}
|
||||
254
src/ecmodule/ComponentManager.ts
Normal file
254
src/ecmodule/ComponentManager.ts
Normal file
@@ -0,0 +1,254 @@
|
||||
import { Component } from "./Component";
|
||||
import { ComponentPool } from "./ComponentPool";
|
||||
|
||||
/**
|
||||
* 组件更新信息
|
||||
*
|
||||
* @export
|
||||
* @class ComponentUpdate
|
||||
*/
|
||||
export class ComponentUpdate {
|
||||
/** 组件更新类型 */
|
||||
public componentType: number;
|
||||
|
||||
/** 组件更新列表 */
|
||||
private readonly _components: Component[] = [];
|
||||
|
||||
/** create constructor */
|
||||
public constructor(componentType: number) {
|
||||
this.componentType = componentType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加要更新的组件
|
||||
* @param component 组件
|
||||
*/
|
||||
public addComponent(component: Component): void {
|
||||
this._components.push(component);
|
||||
component._updateId = this._components.length - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除要更新的组件
|
||||
* @param {Component} component 组件
|
||||
*/
|
||||
public removeComponent(component: Component): void {
|
||||
const components = this._components;
|
||||
const finalUpdateID = components.length - 1;
|
||||
const updateID = component._updateId;
|
||||
|
||||
component._updateId = -1;
|
||||
/** 最后一个和当前要删除的不是同一个,交换位置 */
|
||||
if (finalUpdateID != updateID) {
|
||||
const finalComponent = components[finalUpdateID];
|
||||
// #EC_DEBUG_BEGIN
|
||||
if (finalComponent._updateId != finalUpdateID) {
|
||||
throw new Error(`组件(${finalComponent.toString()})更新ID(${finalUpdateID})与存储更新ID(${finalComponent._updateId})不一致`);
|
||||
}
|
||||
// #EC_DEBUG_END
|
||||
finalComponent._updateId = updateID;
|
||||
components[updateID] = finalComponent;
|
||||
}
|
||||
components.pop();
|
||||
}
|
||||
|
||||
/** 更新 */
|
||||
public _update(dt: number): void {
|
||||
const components = this._components;
|
||||
const componentCount = components.length;
|
||||
|
||||
if (componentCount > 0) {
|
||||
for (let i = 0; i < componentCount; ++i) {
|
||||
const component = components[i];
|
||||
|
||||
if (component.needUpdate && component._updating) {
|
||||
component._update(dt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ComponentManager {
|
||||
/**
|
||||
* 组件池
|
||||
* @type {ComponentPool}
|
||||
*/
|
||||
protected componentPool: ComponentPool;
|
||||
|
||||
/** 更新组件池 */
|
||||
protected readonly updatingComponents: ComponentUpdate[] = [];
|
||||
protected readonly componentUpdateOrderList: number[] = [];
|
||||
|
||||
/** 新添加的或者新停止更新的组件池 */
|
||||
private readonly _toUpdateComponents: Component[] = [];
|
||||
private readonly _toStopComponents: Component[] = [];
|
||||
|
||||
/** 当前更新的组件类型 */
|
||||
private _currentUpdateComponentType: number = -1;
|
||||
|
||||
/**
|
||||
*Creates an instance of ComponentManager.
|
||||
* @param {ComponentPool} componentPool 组件池
|
||||
* @param {number[]} componentUpdateOrderList 组件更新顺序
|
||||
*/
|
||||
constructor(componentPool: ComponentPool, componentUpdateOrderList: number[]) {
|
||||
this.componentPool = componentPool;
|
||||
this._toUpdateComponents.length = 0;
|
||||
this._toStopComponents.length = 0;
|
||||
for (const componentType of componentUpdateOrderList) {
|
||||
this._addComponentUpdateOrder(componentType);
|
||||
}
|
||||
}
|
||||
|
||||
public destroy(): void {
|
||||
this.componentPool.clear();
|
||||
this.updatingComponents.length = 0;
|
||||
this.componentUpdateOrderList.length = 0;
|
||||
this._toUpdateComponents.length = 0;
|
||||
this._toStopComponents.length = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建组件
|
||||
* @template T
|
||||
* @param {string} componentName 组件名
|
||||
* @returns {T} 创建的组件
|
||||
*/
|
||||
public createComponent<T extends Component>(componentName: string): T {
|
||||
const component = this.componentPool.get(componentName) as T;
|
||||
// component._enable = true;
|
||||
// component.needDestroy = false;
|
||||
component.componentManager = this;
|
||||
return component;
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始更新组件
|
||||
* @param {Component} component 组件
|
||||
*/
|
||||
public startUpdateComponent(component: Component): void {
|
||||
if (component._updating) {
|
||||
return;
|
||||
}
|
||||
if (this._currentUpdateComponentType != component.type) {
|
||||
this._addComponentToUpdateList(component);
|
||||
return;
|
||||
}
|
||||
this._toUpdateComponents.push(component);
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止更新组件
|
||||
* @param {Component} component 组件
|
||||
*/
|
||||
public stopUpdateComponent(component: Component): void {
|
||||
if (!component._updating) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._currentUpdateComponentType != component.type) {
|
||||
this._removeComponentToUpdateList(component);
|
||||
return;
|
||||
}
|
||||
|
||||
this._toStopComponents.push(component);
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁组件(内部使用)
|
||||
* @param {Component} component
|
||||
*/
|
||||
public _destroyComponent(component: Component): void {
|
||||
if (!component._updating) {
|
||||
component._destroy();
|
||||
this.componentPool.recycle(component);
|
||||
} else {
|
||||
component._needDestroy = true;
|
||||
}
|
||||
}
|
||||
|
||||
/** 更新所有组件(内部使用) */
|
||||
public _update(dt: number): void {
|
||||
this._updateAllComponents(dt);
|
||||
this._currentUpdateComponentType = -1;
|
||||
|
||||
this._clearStopComponents();
|
||||
this._addUpdateComponents();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加组件更新顺序,先添加的先更新
|
||||
* @param {number} componentType 组件类型
|
||||
*/
|
||||
private _addComponentUpdateOrder(componentType: number): ComponentManager {
|
||||
this.componentUpdateOrderList.push(componentType);
|
||||
const updatingComponents = this.updatingComponents;
|
||||
for (let i = updatingComponents.length; i <= componentType; ++i) {
|
||||
updatingComponents.push(null);
|
||||
}
|
||||
if (updatingComponents[componentType]) {
|
||||
throw new Error(`组件类型(${componentType}:${this.componentPool.className(componentType)})已经添加到更新列表`);
|
||||
}
|
||||
updatingComponents[componentType] = new ComponentUpdate(componentType);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 添加组件到组件更新列表 */
|
||||
private _addComponentToUpdateList(component: Component): void {
|
||||
if (component.type >= this.updatingComponents.length || !this.updatingComponents[component.type]) {
|
||||
throw new Error(`组件(${component.constructor.name})没有添加到组件更新列表,请使用addComponentUpdateOrder添加更新`);
|
||||
}
|
||||
this.updatingComponents[component.type].addComponent(component);
|
||||
}
|
||||
|
||||
/** 组件更新列表中删除组件 */
|
||||
private _removeComponentToUpdateList(component: Component): void {
|
||||
this.updatingComponents[component.type].removeComponent(component);
|
||||
}
|
||||
|
||||
/** 更新所有组件 */
|
||||
private _updateAllComponents(dt: number): void {
|
||||
// 按优先级更新所有组件
|
||||
const updateList = this.componentUpdateOrderList;
|
||||
const updatingComponents = this.updatingComponents;
|
||||
let componentType: number;
|
||||
|
||||
for (let i = 0, l = updateList.length; i < l; ++i) {
|
||||
componentType = updateList[i];
|
||||
this._currentUpdateComponentType = componentType;
|
||||
updatingComponents[componentType]._update(dt);
|
||||
}
|
||||
}
|
||||
|
||||
private _clearStopComponents(): void {
|
||||
const toStopComponents = this._toStopComponents;
|
||||
const l = toStopComponents.length;
|
||||
if (l > 0) {
|
||||
for (let i = 0; i < l; ++i) {
|
||||
const component = toStopComponents[i];
|
||||
if (!component.needUpdate && component._updating) {
|
||||
this._removeComponentToUpdateList(component);
|
||||
if (component._needDestroy) {
|
||||
this._destroyComponent(component);
|
||||
}
|
||||
}
|
||||
}
|
||||
toStopComponents.length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private _addUpdateComponents(): void {
|
||||
const toUpdateComponents = this._toUpdateComponents;
|
||||
const l = toUpdateComponents.length;
|
||||
if (l > 0) {
|
||||
for (let i = 0; i < l; ++i) {
|
||||
const component = toUpdateComponents[i];
|
||||
if (component.needUpdate && !component._updating) {
|
||||
this._addComponentToUpdateList(component);
|
||||
}
|
||||
}
|
||||
toUpdateComponents.length = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
84
src/ecmodule/ComponentPool.ts
Normal file
84
src/ecmodule/ComponentPool.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { Component } from "./Component";
|
||||
import { ObjectBase } from "./ObjectBase";
|
||||
import { ObjectFactory } from "./ObjectFactory";
|
||||
|
||||
export class ComponentPool {
|
||||
/** 组件对象类型到组件类型转换 */
|
||||
private readonly _objectTypeToComponentType: number[] = new Array<number>(128);
|
||||
private _pools: Map<number, ObjectFactory> = new Map();
|
||||
private _nameToObjectType: Map<string, number> = new Map();
|
||||
/**
|
||||
* 注册组件
|
||||
* @param {number} componentObjectType 组件对象类型
|
||||
* @param {number} componentType 组件类型
|
||||
* @param {string} name 组件名称
|
||||
* @param {new () => Component} ctor 构造函数
|
||||
*/
|
||||
public register(componentObjectType: number, componentType: number, name: string, ctor: new () => ObjectBase): void {
|
||||
if (this._pools.has(componentObjectType)) {
|
||||
throw new Error(`组件(${name})已注册, 不允许重复注册`);
|
||||
}
|
||||
this._pools.set(componentObjectType, new ObjectFactory(componentObjectType, 128, name, ctor));
|
||||
this._nameToObjectType.set(name, componentObjectType);
|
||||
|
||||
const objectTypeToComponentType = this._objectTypeToComponentType;
|
||||
for (let i = objectTypeToComponentType.length; i <= componentObjectType; ++i) {
|
||||
objectTypeToComponentType.push(i);
|
||||
}
|
||||
objectTypeToComponentType[componentObjectType] = componentType;
|
||||
}
|
||||
|
||||
public getObjectTypeByName(componentName: string): number {
|
||||
return this._nameToObjectType.get(componentName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建组件
|
||||
* @param {number} componentName 组件名
|
||||
* @returns {T} 创建的组件
|
||||
*/
|
||||
public get<T extends Component>(componentName: string): T {
|
||||
let objectType = this.getObjectTypeByName(componentName);
|
||||
|
||||
const factory = this._pools.get(objectType);
|
||||
if (!factory) {
|
||||
throw new Error(`组件(${componentName})未注册,使用组件装饰器 ecclass 注册组件`);
|
||||
}
|
||||
const component = factory.allocate() as T;
|
||||
component.name = factory.name;
|
||||
component.type = this._objectTypeToComponentType[objectType];
|
||||
return component;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过组件对象类型获取组件类名
|
||||
* @param {number} componentObjectType 组件类型
|
||||
* @returns {string}
|
||||
*/
|
||||
public className(componentObjectType: number): string {
|
||||
const factory = this._pools.get(componentObjectType);
|
||||
if (!factory) {
|
||||
throw new Error(
|
||||
`组件(${componentObjectType})没有注册,使用ComponentPool.register(componentObjectType, componentType, componentClass)注册组件`
|
||||
);
|
||||
}
|
||||
return factory.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 回收组件
|
||||
* @param {BaseComponent} component 要回收的组件
|
||||
* @memberof ComponentPool
|
||||
*/
|
||||
public recycle(component: Component): void {
|
||||
const objectFactory = this._pools.get(component.objectType);
|
||||
objectFactory.recycle(component);
|
||||
}
|
||||
|
||||
/** 清理缓存 */
|
||||
public clear(): void {
|
||||
for (const factory of this._pools.values()) {
|
||||
factory._clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
109
src/ecmodule/ECDataHelper.ts
Normal file
109
src/ecmodule/ECDataHelper.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
import { color, size, v2, v3 } from "cc";
|
||||
import { _ecdecorator, ComponentPool, warn } from "../kunpocc";
|
||||
import { Component } from "./Component";
|
||||
|
||||
/**
|
||||
* @Author: Gongxh
|
||||
* @Date: 2025-01-24
|
||||
* @Description:
|
||||
*/
|
||||
export class ECDataHelper {
|
||||
/** 组件池 */
|
||||
public static _componentPool: ComponentPool = new ComponentPool();
|
||||
/** 注册所有组件 */
|
||||
public static registerComponents(): void {
|
||||
let index = 0;
|
||||
let maps = _ecdecorator.getComponentMaps();
|
||||
maps.forEach((info: _ecdecorator.ECComponentInfo, ctor: any) => {
|
||||
this._componentPool.register(index++, info.componentType, info.name, ctor);
|
||||
});
|
||||
}
|
||||
|
||||
public static getComponentPool(): ComponentPool {
|
||||
return this._componentPool;
|
||||
}
|
||||
|
||||
/** 解析组件数据 */
|
||||
public static parse(component: Component, data: Record<string, any>): void {
|
||||
const maps = _ecdecorator.getComponentMaps();
|
||||
const ctor = component.constructor;
|
||||
if (!maps.has(ctor)) {
|
||||
return;
|
||||
}
|
||||
const info = maps.get(ctor);
|
||||
for (const property in data) {
|
||||
let propInfo = info.props[property];
|
||||
if (!propInfo) {
|
||||
warn(`组件 ${component.name} 属性 ${property} 未注册`);
|
||||
continue;
|
||||
}
|
||||
let value = data[property];
|
||||
(component as any)[property] = this.getPropValue(propInfo, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static getPropValue(propInfo: _ecdecorator.ECPropInfo, value: any): any {
|
||||
switch (propInfo.type) {
|
||||
case "int":
|
||||
if (typeof value === "number") {
|
||||
return value;
|
||||
}
|
||||
return propInfo.defaultValue || 0;
|
||||
case "float":
|
||||
if (typeof value === "number") {
|
||||
return value;
|
||||
}
|
||||
return propInfo.defaultValue || 0;
|
||||
case "boolean":
|
||||
if (typeof value === "boolean") {
|
||||
return value;
|
||||
}
|
||||
return propInfo.defaultValue || false;
|
||||
case "size":
|
||||
if (typeof value === "object" && typeof value.width === "number" && typeof value.height === "number") {
|
||||
return size(value.width, value.height);
|
||||
}
|
||||
return propInfo.defaultValue || size(0, 0);
|
||||
case "vec2":
|
||||
if (typeof value === "object" && typeof value.x === "number" && typeof value.y === "number") {
|
||||
return v2(value.x, value.y);
|
||||
}
|
||||
return propInfo.defaultValue || v2(0, 0);
|
||||
case "vec3":
|
||||
if (typeof value === "object" && typeof value.x === "number" && typeof value.y === "number" && typeof value.z === "number") {
|
||||
return v3(value.x, value.y, value.z);
|
||||
}
|
||||
return propInfo.defaultValue || v3(0, 0, 0);
|
||||
case "color":
|
||||
if (typeof value === "object" && typeof value[0] === "number" && typeof value[1] === "number" && typeof value[2] === "number") {
|
||||
return color(value[0], value[1], value[2], typeof value[3] === "number" ? value[3] : 255);
|
||||
}
|
||||
return propInfo.defaultValue || color(255, 255, 255, 255);
|
||||
case "asset":
|
||||
case "spriteframe":
|
||||
case "prefab":
|
||||
case "jsonAsset":
|
||||
case "particle":
|
||||
case "animation":
|
||||
case "audio":
|
||||
case "skeleton":
|
||||
case "entity":
|
||||
return typeof value === "string" ? value : (propInfo.defaultValue || "");
|
||||
case "enum":
|
||||
return value;
|
||||
case "array":
|
||||
if (Array.isArray(value)) {
|
||||
return value;
|
||||
}
|
||||
return propInfo.defaultValue || [];
|
||||
case "object":
|
||||
if (typeof value === "object") {
|
||||
return value;
|
||||
}
|
||||
return propInfo.defaultValue || {};
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
140
src/ecmodule/ECDecorator.ts
Normal file
140
src/ecmodule/ECDecorator.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* @Author: Gongxh
|
||||
* @Date: 2025-01-14
|
||||
* @Description: 实体组件装饰器
|
||||
*/
|
||||
|
||||
import { Color, Size, Vec2, Vec3 } from "cc";
|
||||
import { ObjectHelper } from "../tool/helper/ObjectHelper";
|
||||
|
||||
|
||||
export namespace _ecdecorator {
|
||||
const ECPropMeta = "__ecpropmeta__"
|
||||
|
||||
type ECPropType = "int" | "float" | "string" | "boolean" | "size" | "vec2" | "vec3" | "color" | "asset" | "spriteframe" | "jsonAsset" | "particle" | "animation" | "audio" | "prefab" | "skeleton" | "enum" | "array" | "object" | "entity";
|
||||
|
||||
interface ECPropInfoBase {
|
||||
/** 属性默认值 */
|
||||
defaultValue?: any,
|
||||
/** 编辑器中的显示名称 */
|
||||
displayName?: string,
|
||||
/** 编辑器中的提示 */
|
||||
tips?: string
|
||||
}
|
||||
|
||||
interface ECPropInfoNumber extends ECPropInfoBase {
|
||||
/** 属性类型 */
|
||||
type: "int" | "float";
|
||||
/** 默认值:0 */
|
||||
defaultValue?: number;
|
||||
}
|
||||
|
||||
interface ECPropInfoBoolean extends ECPropInfoBase {
|
||||
/** 属性类型 */
|
||||
type: "boolean";
|
||||
/** 默认值:false */
|
||||
defaultValue?: boolean;
|
||||
}
|
||||
|
||||
interface ECPropInfoSize extends ECPropInfoBase {
|
||||
/** 属性类型 */
|
||||
type: "size";
|
||||
/** 默认值:Size(0,0) */
|
||||
defaultValue?: Size;
|
||||
}
|
||||
|
||||
interface ECPropInfoVec extends ECPropInfoBase {
|
||||
/** 属性类型 */
|
||||
type: "vec2" | "vec3";
|
||||
/** 默认值: Vec2(0,0) | Vec3(0,0,0) */
|
||||
defaultValue?: Vec2 | Vec3;
|
||||
}
|
||||
|
||||
interface ECPropInfoString extends ECPropInfoBase {
|
||||
/** 属性类型 */
|
||||
type: "string" | "asset" | "spriteframe" | "jsonAsset" | "particle" | "animation" | "audio" | "prefab" | "skeleton" | "entity";
|
||||
/** 默认值: "" */
|
||||
defaultValue?: string;
|
||||
}
|
||||
|
||||
interface ECPropInfoColor extends ECPropInfoBase {
|
||||
/** 属性类型 */
|
||||
type: "color";
|
||||
/** 默认值:Color(255, 255, 255, 255) */
|
||||
defaultValue?: Color;
|
||||
}
|
||||
|
||||
interface ECPropInfoArray extends ECPropInfoBase {
|
||||
/** 属性类型 */
|
||||
type: "array";
|
||||
/** 类型格式 当类型是复合类型enum、array、object时必须 */
|
||||
format: ECPropType | ECPropInfo;
|
||||
}
|
||||
|
||||
interface ECPropInfoObject extends ECPropInfoBase {
|
||||
/** 属性类型 */
|
||||
type: "object";
|
||||
/** 类型格式 当类型是复合类型enum、array、object时必须 */
|
||||
format: Record<string, ECPropType> | Record<string, ECPropInfo>;
|
||||
}
|
||||
|
||||
interface ECPropInfoEnum extends ECPropInfoBase {
|
||||
type: "enum";
|
||||
/** 枚举值 */
|
||||
format: object;
|
||||
/** 默认值 */
|
||||
defaultValue?: string | number;
|
||||
}
|
||||
|
||||
export type ECPropInfo = ECPropInfoNumber | ECPropInfoBoolean | ECPropInfoSize | ECPropInfoVec | ECPropInfoString | ECPropInfoColor | ECPropInfoArray | ECPropInfoObject | ECPropInfoEnum;
|
||||
|
||||
/**
|
||||
* 组件注册数据结构
|
||||
*/
|
||||
export interface ECComponentInfo {
|
||||
/** 组件名 */
|
||||
name: string;
|
||||
/** 组件类型 */
|
||||
componentType: number;
|
||||
/** 组件描述 */
|
||||
describe: string;
|
||||
/** 属性 */
|
||||
props: Record<string, ECPropInfo>;
|
||||
}
|
||||
/** 用来存储组件注册信息 */
|
||||
const eclassMap: Map<any, ECComponentInfo> = new Map();
|
||||
|
||||
/** 获取组件注册信息 */
|
||||
export function getComponentMaps(): Map<any, ECComponentInfo> {
|
||||
return eclassMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 实体组件装饰器
|
||||
* @param {string} res.describe 组件组描述
|
||||
*/
|
||||
export function ecclass(name: string, componentType: number, res?: { describe?: string }): Function {
|
||||
/** target 类的构造函数 */
|
||||
return function (ctor: any): void {
|
||||
// console.log(`组件装饰器 组件【${name}】属性:`, JSON.stringify(ctor[ECPropMeta]));
|
||||
eclassMap.set(ctor, {
|
||||
name: name,
|
||||
componentType: componentType,
|
||||
props: ctor[ECPropMeta],
|
||||
describe: res?.describe || name
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/** 组件属性装饰器 */
|
||||
export function ecprop(options: ECPropInfo): any {
|
||||
return function (target: any, propName: any): void {
|
||||
ObjectHelper.getObjectProp(target.constructor, ECPropMeta)[propName] = options;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let _global = globalThis || window || global;
|
||||
(_global as any)["getKunpoRegisterECMaps"] = function () {
|
||||
return _ecdecorator.getComponentMaps() as any;
|
||||
};
|
||||
162
src/ecmodule/ECManager.ts
Normal file
162
src/ecmodule/ECManager.ts
Normal file
@@ -0,0 +1,162 @@
|
||||
/**
|
||||
* @Author: Gongxh
|
||||
* @Date: 2025-01-14
|
||||
* @Description: 实体组件管理对外接口
|
||||
*/
|
||||
|
||||
import { Node } from "cc";
|
||||
import { ECDataHelper } from "./ECDataHelper";
|
||||
import { Entity } from "./Entity";
|
||||
import { EntityManager } from "./EntityManager";
|
||||
|
||||
interface IEntityConfig {
|
||||
[componentName: string]: Record<string, any>
|
||||
}
|
||||
|
||||
interface IWorldConfig {
|
||||
/** 实体管理器 */
|
||||
world: EntityManager;
|
||||
/** 世界节点 */
|
||||
worldNode: Node;
|
||||
}
|
||||
|
||||
export class ECManager {
|
||||
/** 实体管理器 */
|
||||
private static _worlds: Map<string, IWorldConfig> = new Map();
|
||||
/** 实体配置信息 */
|
||||
private static _entityList: { [name: string]: Record<string, any> } = {};
|
||||
|
||||
/** 注册所有组件 如果GameEntry因分包导致,组件的代码注册晚于 CocosEntry的 onInit函数, 则需要在合适的时机手动调用此方法 */
|
||||
public static registerComponents(): void {
|
||||
ECDataHelper.registerComponents();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建EC世界 创建EC世界前必须先注册组件
|
||||
* @param {string} worldName 名称
|
||||
* @param {Node} node 世界节点
|
||||
* @param {number[]} componentUpdateOrderList 组件更新顺序列表 (只传需要更新的组件列表)
|
||||
* @param {number} [maxCapacityInPool=128] 实体池最大容量,多余的实体不会缓存
|
||||
* @param {number} [preloadEntityCount=32] 预加载Entity数量
|
||||
*/
|
||||
public static createECWorld(worldName: string, node: Node, componentUpdateOrderList: number[], maxCapacityInPool = 128, preloadEntityCount = 32): EntityManager {
|
||||
if (this._worlds.has(worldName)) {
|
||||
throw new Error(`ECWorld ${worldName} already exists`);
|
||||
}
|
||||
const entityManager = new EntityManager(worldName, ECDataHelper.getComponentPool(), componentUpdateOrderList, maxCapacityInPool, preloadEntityCount);
|
||||
this._worlds.set(worldName, { world: entityManager, worldNode: node });
|
||||
return entityManager;
|
||||
}
|
||||
|
||||
/** 获取EC世界 */
|
||||
public static getECWorld(worldName: string): EntityManager {
|
||||
if (!this._worlds.has(worldName)) {
|
||||
throw new Error(`ECWorld ${worldName} not found`);
|
||||
}
|
||||
const entityManager = this._worlds.get(worldName).world;
|
||||
if (!entityManager) {
|
||||
throw new Error(`ECWorld ${worldName} is null`);
|
||||
}
|
||||
return entityManager;
|
||||
}
|
||||
|
||||
/** 获取EC世界节点 */
|
||||
public static getECWorldNode(worldName: string): Node {
|
||||
if (!this._worlds.has(worldName)) {
|
||||
throw new Error(`ECWorld ${worldName} not found`);
|
||||
}
|
||||
const node = this._worlds.get(worldName).worldNode;
|
||||
if (!node) {
|
||||
throw new Error(`ECWorld ${worldName} is null`);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
/** 销毁EC世界 */
|
||||
public static destroyECWorld(worldName: string): void {
|
||||
let entityManager = this.getECWorld(worldName);
|
||||
if (entityManager) {
|
||||
entityManager.destroy();
|
||||
this._worlds.delete(worldName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册配置表中的实体信息
|
||||
* @param config 实体配置信息,格式为 {实体名: {组件名: 组件数据}}
|
||||
*/
|
||||
public static registerEntityConfig(config: { [entityName: string]: IEntityConfig }): void {
|
||||
// 遍历并注册每个实体的配置
|
||||
for (const entityName in config) {
|
||||
this._entityList[entityName] = config[entityName];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加实体信息 (如果已经存在, 则数据组合)
|
||||
* 如果存在编辑器编辑不了的数据 用来给编辑器导出的实体信息 添加扩展数据
|
||||
* @param name 实体名
|
||||
* @param info 实体信息
|
||||
*/
|
||||
public static addEntityInfo(name: string, info: IEntityConfig): void {
|
||||
if (this._entityList[name]) {
|
||||
this._entityList[name] = Object.assign(this._entityList[name], info);
|
||||
} else {
|
||||
this._entityList[name] = info;
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取实体配置信息 */
|
||||
public static getEntityInfo(name: string): Record<string, any> {
|
||||
if (!this._entityList[name]) {
|
||||
throw new Error(`Entity ${name} info not found, please register it first`);
|
||||
}
|
||||
return this._entityList[name];
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建实体
|
||||
* @param worldName 实体管理器名称
|
||||
* @param name 实体名字
|
||||
* @returns {kunpo.Entity} 实体
|
||||
*/
|
||||
public static createEntity(worldName: string, name: string): Entity {
|
||||
let info = this.getEntityInfo(name);
|
||||
let world = this.getECWorld(worldName);
|
||||
let entity = world.createEntity(name);
|
||||
info && this._addComponentToEntity(world, entity, info);
|
||||
|
||||
world.addEntity(entity);
|
||||
return entity;
|
||||
}
|
||||
|
||||
private static _addComponentToEntity(world: EntityManager, entity: Entity, componentsData: Record<string, any>): void {
|
||||
for (const componentName in componentsData) {
|
||||
let component = world.createComponent(componentName);
|
||||
ECDataHelper.parse(component, componentsData[componentName]);
|
||||
entity.addComponent(component);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁实体
|
||||
* @param worldName 世界名称
|
||||
* @param entity 实体
|
||||
*/
|
||||
public static destroyEntity(worldName: string, entity: Entity): void {
|
||||
if (!entity || !entity.id) {
|
||||
return;
|
||||
}
|
||||
this.destroyEntityById(worldName, entity.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁实体
|
||||
* @param worldName 世界名称
|
||||
* @param entityId 实体ID
|
||||
*/
|
||||
public static destroyEntityById(worldName: string, entityId: number): void {
|
||||
let world = this.getECWorld(worldName);
|
||||
world.destroyEntityById(entityId);
|
||||
}
|
||||
}
|
||||
79
src/ecmodule/ECType.ts
Normal file
79
src/ecmodule/ECType.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* @type {&} AND,按位与处理两个长度相同的二进制数,两个相应的二进位都为 1,该位的结果值才为 1,否则为 0
|
||||
* @type {|} OR,按位或处理两个长度相同的二进制数,两个相应的二进位中只要有一个为 1,该位的结果值为 1
|
||||
* @type {~} 取反,取反是一元运算符,对一个二进制数的每一位执行逻辑反操作。使数字 1 成为 0,0 成为 1
|
||||
* @type {^} 异或,按位异或运算,对等长二进制模式按位或二进制数的每一位执行逻辑异按位或操作。操作的结果是如果某位不同则该位为 1,否则该位为 0
|
||||
* @type {<<} 左移,把 << 左边的运算数的各二进位全部左移若干位,由 << 右边的数指定移动的位数,高位丢弃,低位补0; 将一个值左移一个位置相当于将其乘以2,移位两个位置相当于乘以4,依此类推。
|
||||
* @type {>>} 右移,把 >> 左边的运算数的各二进位全部右移若干位,>> 右边的数指定移动的位数
|
||||
* @type {>>>} 无符号右移,与有符号右移位类似,除了左边一律使用0 补位
|
||||
*/
|
||||
import { Stack } from "../tool/DataStruct/Stack";
|
||||
|
||||
export const EntityIndexBits = 16;
|
||||
export const EntityIndexMask = (1 << EntityIndexBits) - 1;
|
||||
export const MaxEntityCount = 1 << EntityIndexBits;
|
||||
export type EntityId = number;
|
||||
|
||||
/**
|
||||
* 2进制转10进制 (不支持小数和负数)
|
||||
* @param {number} bitNumber 二进制数
|
||||
* @return {number} 十进制数
|
||||
*/
|
||||
export function bit2Decimal(bitNumber: number): number {
|
||||
let bitString = String(bitNumber);
|
||||
let len = bitString.length;
|
||||
let index = len - 1;
|
||||
let result: number = 0;
|
||||
do {
|
||||
result += Number(bitString[index]) * Math.pow(2, len - index - 1);
|
||||
index--;
|
||||
} while (index >= 0);
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* 10进制转2进制 (不支持小数和负数)
|
||||
* @param {number} num 十进制数
|
||||
* @return {number} 二进制数
|
||||
*/
|
||||
export function decimal2Bit(num: number): number {
|
||||
let stack = new Stack<number>();
|
||||
let dividend: number = Math.floor(num);
|
||||
let remainder: number;
|
||||
do {
|
||||
remainder = dividend % 2;
|
||||
stack.push(remainder);
|
||||
dividend = Math.floor(dividend / 2);
|
||||
} while (dividend > 0);
|
||||
let result = "";
|
||||
while (!stack.isEmpty()) {
|
||||
result += stack.pop().toString();
|
||||
}
|
||||
return Number(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过实体id获取实体index
|
||||
* @param id 实体id
|
||||
*/
|
||||
export function getEntityIndex(id: EntityId): number {
|
||||
return id & EntityIndexMask;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过实体id获取实体版本
|
||||
* @param id
|
||||
*/
|
||||
export function getEntityVersion(id: EntityId): number {
|
||||
return id >>> EntityIndexBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* 实体描述
|
||||
* @param id 实体id
|
||||
*/
|
||||
export function entityIdString(id: EntityId): string {
|
||||
return `${getEntityIndex(id)}:${getEntityVersion(id)}`;
|
||||
}
|
||||
// console.log("-------->", EntityIndexBits); 16
|
||||
// console.log("-------->", EntityIndexMask); 65535
|
||||
// console.log("-------->", MaxEntityCount); 65536
|
||||
277
src/ecmodule/Entity.ts
Normal file
277
src/ecmodule/Entity.ts
Normal file
@@ -0,0 +1,277 @@
|
||||
import { Component } from "./Component";
|
||||
import { EntityId } from "./ECType";
|
||||
import { EntityManager } from "./EntityManager";
|
||||
|
||||
export class Entity {
|
||||
/**
|
||||
* 实体名称
|
||||
* @type {String}
|
||||
*/
|
||||
public name: string;
|
||||
|
||||
/**
|
||||
* 实体ID
|
||||
* @type {EntityId}
|
||||
*/
|
||||
public id: EntityId;
|
||||
|
||||
/**
|
||||
* 实体标识
|
||||
* @type {Set<number>}
|
||||
* @memberof Entity
|
||||
*/
|
||||
public tags: Set<number>;
|
||||
|
||||
/**
|
||||
* 实体状态
|
||||
* @type {Map<number, number>}
|
||||
* @memberof Entity
|
||||
*/
|
||||
public states: Map<number, number>;
|
||||
/**
|
||||
* 是否被激活 (添加到实体管理器时激活)
|
||||
* @type {boolean}
|
||||
*/
|
||||
public active: boolean = false;
|
||||
|
||||
/**
|
||||
* 所属实体管理器 (实体创建后直接赋值)
|
||||
* @private
|
||||
* @type {EntityManager}
|
||||
*/
|
||||
public entityManager: EntityManager;
|
||||
|
||||
/**
|
||||
* 所有组件
|
||||
* @type {Map<number, Component>}
|
||||
* @type {number} 组件类型
|
||||
* @type {Component} 组件
|
||||
*/
|
||||
public readonly components: Map<number, Component> = new Map();
|
||||
|
||||
/**
|
||||
* 实体被添加到EntityManager
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
public _add(): void {
|
||||
this.active = true;
|
||||
for (const component of this.components.values()) {
|
||||
component._enter();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 实体销毁,不要手动调用
|
||||
* @memberof Entity
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
public _destroy(): void {
|
||||
this.removeAllComponents();
|
||||
this.tags && this.tags.clear();
|
||||
this.states && this.states.clear();
|
||||
this.active = false;
|
||||
this.entityManager = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加标签
|
||||
* @param {number[]} ...tags 标签除了表示Entity,还可以通过EntityManager获取指定标签的Entity
|
||||
*/
|
||||
public addTag(...tag: number[]): void {
|
||||
let tags = this.tags;
|
||||
if (!tags) {
|
||||
tags = this.tags = new Set<number>();
|
||||
}
|
||||
for (let i = 0; i < tag.length; i++) {
|
||||
tags.add(tag[i]);
|
||||
this.active && this.entityManager && this.entityManager._addEntityTag(this.id, tag[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除标签
|
||||
* @param {number} tag 删除的标签
|
||||
*/
|
||||
public removeTag(tag: number): void {
|
||||
if (this.tags) {
|
||||
this.tags.delete(tag);
|
||||
this.active && this.entityManager && this.entityManager._removeEntityTagById(this.id, tag);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否包含标签
|
||||
* @param {number} tag 标签
|
||||
* @returns {boolean} 是否包含
|
||||
*/
|
||||
public hasTag(...tag: number[]): boolean {
|
||||
let tags = this.tags;
|
||||
if (!tags) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < tag.length; i++) {
|
||||
if (tags.has(tag[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组件
|
||||
* @param {number} componentType 组件类型
|
||||
* @returns {T}
|
||||
*/
|
||||
public getComponent<T extends Component>(componentType: number): T {
|
||||
return this.components.get(componentType) as T;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加组件
|
||||
* @param {Component} component 组件
|
||||
*/
|
||||
public addComponent(component: Component): void {
|
||||
if (this.hasComponent(component.type)) {
|
||||
throw new Error(`组件{${component.constructor.name}类型:${component.type})已经存在,不允许添加同一类型组件`);
|
||||
}
|
||||
this.components.set(component.type, component);
|
||||
component.entity = this;
|
||||
component._add();
|
||||
|
||||
if (this.active) {
|
||||
component._enter();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除组件
|
||||
* @param {number} componentType 组件类型
|
||||
*/
|
||||
public removeComponent(componentType: number): void {
|
||||
const component = this.components.get(componentType);
|
||||
|
||||
if (component) {
|
||||
this.components.delete(componentType);
|
||||
component._remove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除所有组件
|
||||
*/
|
||||
public removeAllComponents(): void {
|
||||
for (const component of this.components.values()) {
|
||||
component._remove();
|
||||
}
|
||||
this.components.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否包含组件
|
||||
* @param {number} componentType 组件类型
|
||||
* @returns {boolean} 是否包含组件
|
||||
*/
|
||||
public hasComponent(componentType: number): boolean {
|
||||
return this.components.has(componentType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁自己
|
||||
*/
|
||||
public destroy(): void {
|
||||
this.entityManager.destroyEntityById(this.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加监听
|
||||
* @param eventName 监听的消息名
|
||||
* @param callback 回调
|
||||
* @param entityId 实体ID
|
||||
* @param once 是否单次监听
|
||||
*/
|
||||
public addEvent(eventName: string, callback: (...args: any[]) => void, once: boolean = false): void {
|
||||
this.entityManager && this.entityManager._addEvent(eventName, callback, this, once);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
* @param eventName 消息名
|
||||
* @param entityId 实体ID
|
||||
* @param args 发送参数
|
||||
*/
|
||||
public sendListener(eventName: string, ...args: any[]): void {
|
||||
this.entityManager && this.entityManager._sendEvent(eventName, this, ...args);
|
||||
}
|
||||
|
||||
public removeListener(eventName: string, callback?: (...args: any[]) => void): void {
|
||||
this.entityManager && this.entityManager._removeEvent(eventName, this, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加状态
|
||||
* 状态采用计数方式,对状态处理时需要保证addState和removeState成对存在
|
||||
* @param {number} state 状态类型
|
||||
* @memberof Entity
|
||||
*/
|
||||
public addState(state: number): void {
|
||||
let states = this.states;
|
||||
if (!states) {
|
||||
states = this.states = new Map<number, number>();
|
||||
}
|
||||
states.set(state, (states.get(state) || 0) + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除状态
|
||||
*
|
||||
* @param {number} state 状态类型
|
||||
* @returns {boolean} 如果计数为0或状态不存在,则返回true
|
||||
* @memberof Entity
|
||||
*/
|
||||
public removeState(state: number): boolean {
|
||||
const states = this.states;
|
||||
if (!states) {
|
||||
return false;
|
||||
}
|
||||
let stateCount = states.get(state);
|
||||
if (stateCount) {
|
||||
// 处理状态计数,为0则删除状态
|
||||
--stateCount;
|
||||
if (stateCount == 0) {
|
||||
states.delete(state);
|
||||
return true;
|
||||
}
|
||||
|
||||
states.set(state, stateCount);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否包含指定状态
|
||||
* @param {number} state 状态
|
||||
* @returns {boolean}
|
||||
* @memberof Entity
|
||||
*/
|
||||
public hasState(state: number): boolean {
|
||||
return this.states && this.states.has(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除状态
|
||||
* @param {number} state 状态
|
||||
* @memberof Entity
|
||||
*/
|
||||
public clearState(state: number): void {
|
||||
this.states && this.states.delete(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除所有状态
|
||||
* @memberof Entity
|
||||
*/
|
||||
public clearAllStates(): void {
|
||||
this.states && this.states.clear();
|
||||
}
|
||||
}
|
||||
420
src/ecmodule/EntityManager.ts
Normal file
420
src/ecmodule/EntityManager.ts
Normal file
@@ -0,0 +1,420 @@
|
||||
import { EventManager } from "../event/EventManager";
|
||||
import { warn } from "../tool/log";
|
||||
import { Component } from "./Component";
|
||||
import { ComponentManager } from "./ComponentManager";
|
||||
import { ComponentPool } from "./ComponentPool";
|
||||
import { EntityId, entityIdString, EntityIndexBits, getEntityIndex, getEntityVersion, MaxEntityCount } from "./ECType";
|
||||
import { Entity } from "./Entity";
|
||||
|
||||
export class EntityManager {
|
||||
/**
|
||||
* 名称
|
||||
* @type {string}
|
||||
*/
|
||||
public name: string;
|
||||
|
||||
/**
|
||||
* 单例实体
|
||||
* @type {Entity}
|
||||
*/
|
||||
public readonly insEntity: Entity = new Entity();
|
||||
|
||||
/**
|
||||
* 单例实体激活状态
|
||||
* @type {boolean}
|
||||
*/
|
||||
public insActive: boolean = false;
|
||||
|
||||
/**
|
||||
* 组件管理
|
||||
* @type {ComponentManager}
|
||||
*/
|
||||
public componentManager: ComponentManager;
|
||||
|
||||
/**
|
||||
* 普通实体事件容器
|
||||
* @type {EventManager}
|
||||
*/
|
||||
private _eventManager: EventManager;
|
||||
|
||||
/**
|
||||
* 单例实体消息监听容器
|
||||
* @type {EventManager}
|
||||
*/
|
||||
private _insEventManager: EventManager;
|
||||
|
||||
/** 实体池 */
|
||||
private readonly _entityPool: Entity[] = [];
|
||||
/** tag标记池 */
|
||||
private readonly _tagToEntity: Map<number, Set<EntityId>> = new Map<number, Set<EntityId>>();
|
||||
/** 实体回收池 */
|
||||
private _recyclePool: Entity[] = [];
|
||||
/** 实体回收池最大容量 */
|
||||
private _maxCapacityInPool: number;
|
||||
/** 实体回收版本 */
|
||||
private _entityVersion: number[] = [];
|
||||
/** 回收实体ID */
|
||||
private _recycleEntityIds: EntityId[] = [];
|
||||
/** 世界是否删除 */
|
||||
private _isDestroyed: boolean;
|
||||
/** 是否正在更新 */
|
||||
private _updating: boolean;
|
||||
/**
|
||||
* 实体池最大容量,回收的多余的实体不会缓存
|
||||
* @param {string} name 名称
|
||||
* @param {ComponentPool} componentPool 组件池
|
||||
* @param {ComponentPool} componentUpdateOrderList 组件更新顺序
|
||||
* @param {number} [maxCapacityInPool=128] 实体回收池最大容量
|
||||
* @param {number} [preloadEntityCount=32] 预加载Entity数量
|
||||
*/
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
constructor(name: string, componentPool: ComponentPool, componentUpdateOrderList: number[], maxCapacityInPool: number = 128, preloadEntityCount: number = 32) {
|
||||
this.name = name;
|
||||
if (preloadEntityCount >= MaxEntityCount) {
|
||||
throw new Error(`预加载超出实体最大数量:${preloadEntityCount} >= max(${MaxEntityCount})`);
|
||||
}
|
||||
// 占位
|
||||
this._entityPool.push(null);
|
||||
this._entityVersion.push(1);
|
||||
this._maxCapacityInPool = maxCapacityInPool;
|
||||
// 预创建
|
||||
for (let i = 0; i < preloadEntityCount; ++i) {
|
||||
this._recyclePool.push(new Entity());
|
||||
}
|
||||
// 组件管理器
|
||||
this.componentManager = new ComponentManager(componentPool, componentUpdateOrderList);
|
||||
this.insEntity.entityManager = this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加实体标签(内部使用)
|
||||
* @param {EntityId} entityId 实体Id
|
||||
* @param {number} tag 标签
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
public _addEntityTag(entityId: EntityId, tag: number): void {
|
||||
this._validateEntityById(entityId);
|
||||
let entitiesByTag = this._tagToEntity.get(tag);
|
||||
if (!entitiesByTag) {
|
||||
entitiesByTag = new Set<EntityId>();
|
||||
this._tagToEntity.set(tag, entitiesByTag);
|
||||
}
|
||||
entitiesByTag.add(entityId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除实体Tag(内部使用)
|
||||
* @param {Entity} entity 实体
|
||||
* @param {number} tag 标签
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
public _removeEntityTag(entity: Entity, tag: number): void {
|
||||
this._removeEntityTagById(entity.id, tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过实体ID删除实体Tag(内部使用)
|
||||
* @param {EntityId} entityId 实体Id
|
||||
* @param {number} tag 标签
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
public _removeEntityTagById(entityId: EntityId, tag: number): void {
|
||||
this._validateEntityById(entityId);
|
||||
const entitiesByTag = this._tagToEntity.get(tag);
|
||||
if (entitiesByTag) {
|
||||
entitiesByTag.delete(entityId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建实体
|
||||
* @returns {Entity} 实体
|
||||
*/
|
||||
public createEntity(name: string): Entity {
|
||||
const entity = this._recyclePool.pop() || new Entity();
|
||||
entity.id = 0;
|
||||
entity.name = name;
|
||||
entity.entityManager = this;
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加实体
|
||||
* @param {Entity} entity 要添加的实体
|
||||
*/
|
||||
public addEntity(entity: Entity): void {
|
||||
if (this.exists(entity.id)) {
|
||||
throw new Error(`实体(${entityIdString(entity.id)})已经添加到EntityManager`);
|
||||
}
|
||||
// 分配实体Id
|
||||
if (this._recycleEntityIds.length > 0) {
|
||||
const newIndex = this._recycleEntityIds.pop();
|
||||
this._entityPool[newIndex] = entity;
|
||||
entity.id = (this._entityVersion[newIndex] << EntityIndexBits) | newIndex;
|
||||
} else {
|
||||
this._entityPool.push(entity);
|
||||
this._entityVersion.push(1);
|
||||
entity.id = MaxEntityCount | (this._entityPool.length - 1);
|
||||
}
|
||||
this._addEntityToTag(entity);
|
||||
entity._add();
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁实体
|
||||
* @param {Entity} entity 要删除的实体
|
||||
*/
|
||||
public destroyEntity(entity: Entity): void {
|
||||
this.destroyEntityById(entity.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁指定ID实体
|
||||
* @param {EntityId} entityId 实体Id
|
||||
*/
|
||||
public destroyEntityById(entityId: EntityId): void {
|
||||
const entity = this.getEntity(entityId);
|
||||
if (!entity) {
|
||||
warn(`实体(${entityIdString(entityId)})已经被销毁`);
|
||||
return;
|
||||
}
|
||||
this._recycleEntity(entity);
|
||||
this._eventManager && this._eventManager.removeList(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁所有实体
|
||||
* @param {boolean} ignoreSingletonEntity 是否忽略单例实体
|
||||
*/
|
||||
public destroyAllEntities(ignoreSingletonEntity: boolean): void {
|
||||
const entities = this._entityPool;
|
||||
for (let i = 1, len = entities.length; i < len; ++i) {
|
||||
if (entities[i]) {
|
||||
this._destroyEntity(entities[i]);
|
||||
}
|
||||
}
|
||||
this._recycleEntityIds.length = 0;
|
||||
this._entityPool.length = 0;
|
||||
this._entityVersion.length = 0;
|
||||
this._tagToEntity.clear();
|
||||
|
||||
// 占位
|
||||
this._entityPool.push(null);
|
||||
this._entityVersion.push(1);
|
||||
this._eventManager && this._eventManager.destroyAll();
|
||||
|
||||
// 销毁单例实体组件
|
||||
if (!ignoreSingletonEntity) {
|
||||
this.insEntity._destroy();
|
||||
this.insActive = false;
|
||||
this._insEventManager && this._insEventManager.destroyAll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过实体ID获取实体
|
||||
* @param {EntityId} entityId 实体Id
|
||||
* @returns {(Entity | null)} 实体
|
||||
*/
|
||||
public getEntity(entityId: EntityId): Entity | null {
|
||||
const index = getEntityIndex(entityId);
|
||||
if (index <= 0 || index >= this._entityPool.length) {
|
||||
return null;
|
||||
}
|
||||
if (this._entityVersion[index] == getEntityVersion(entityId)) {
|
||||
return this._entityPool[index];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定标签的实体
|
||||
* @param {number} tag 标签
|
||||
* @returns {Entity[]} 返回的实体池
|
||||
*/
|
||||
public getEntitiesByTag(tag: number): Entity[] {
|
||||
let buffer: Entity[] = [];
|
||||
const entitiesByTag = this._tagToEntity.get(tag);
|
||||
if (entitiesByTag) {
|
||||
for (const entityId of entitiesByTag) {
|
||||
const entity = this.getEntity(entityId);
|
||||
entity && buffer.push(entity);
|
||||
}
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据实体ID判断实体是否存在
|
||||
* @param {EntityId} entityId 实体Id
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public exists(entityId: EntityId): boolean {
|
||||
const index = getEntityIndex(entityId);
|
||||
if (index <= 0 || index >= this._entityPool.length) {
|
||||
return false;
|
||||
}
|
||||
const entity = this._entityPool[index];
|
||||
return entity && this._entityVersion[index] == getEntityVersion(entityId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建组件
|
||||
* @template T 组件类型
|
||||
* @param {string} componentName 组件名
|
||||
* @returns {T} 创建的组件
|
||||
*/
|
||||
public createComponent<T extends Component>(componentName: string): T {
|
||||
return this.componentManager.createComponent<T>(componentName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加单例组件
|
||||
* @param component
|
||||
*/
|
||||
public addSingleton(component: Component): void {
|
||||
this.insEntity.addComponent(component);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单例组件
|
||||
*/
|
||||
public getSingleton<T extends Component>(componentType: number): T {
|
||||
return this.insEntity.getComponent<T>(componentType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除单例组件
|
||||
*/
|
||||
public removeSingleton(componentType: number): void {
|
||||
this.insEntity.removeComponent(componentType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否存在对应的单例组件
|
||||
*/
|
||||
public hasSingleton(componentType: number): boolean {
|
||||
return this.insEntity.hasComponent(componentType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 激活单例组件
|
||||
*/
|
||||
public activeSingleton(): void {
|
||||
const insEntity = this.insEntity;
|
||||
if (this.insActive) {
|
||||
throw new Error("单例实体已经被激活");
|
||||
}
|
||||
this.insActive = true;
|
||||
insEntity.id = -1;
|
||||
insEntity._add();
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁EntityManager
|
||||
*/
|
||||
public destroy(): void {
|
||||
if (this._isDestroyed) {
|
||||
return;
|
||||
}
|
||||
if (this._updating) {
|
||||
throw new Error("请勿在更新时销毁EntityManager");
|
||||
}
|
||||
this.destroyAllEntities(false);
|
||||
this.componentManager.destroy();
|
||||
this._isDestroyed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加消息监听 (内部使用)
|
||||
* @param eventName 消息名
|
||||
* @param callback 事件回调
|
||||
* @param entityId 实体ID
|
||||
* @param once 是否单次事件
|
||||
*/
|
||||
public _addEvent(eventName: string, callback: (...args: any[]) => void, entity: Entity, once: boolean = false): void {
|
||||
if (entity == this.insEntity) {
|
||||
this._insEventManager = this._insEventManager ? this._insEventManager : new EventManager();
|
||||
this._insEventManager._addEvent(eventName, callback, once, entity);
|
||||
return;
|
||||
}
|
||||
this._eventManager = this._eventManager ? this._eventManager : new EventManager();
|
||||
this._eventManager._addEvent(eventName, callback, once, entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息 (内部使用)
|
||||
* @param eventName 消息名
|
||||
* @param entityId 实体ID
|
||||
* @param args 发送参数
|
||||
*/
|
||||
public _sendEvent(eventName: string, entity: Entity, ...args: any[]): void {
|
||||
if (entity == this.insEntity) {
|
||||
this._insEventManager && this._insEventManager.send(eventName, entity, ...args);
|
||||
return;
|
||||
}
|
||||
this._eventManager && this._eventManager.send(eventName, entity, ...args);
|
||||
}
|
||||
|
||||
public _removeEvent(eventName: string, entity: Entity, callback?: (...args: any[]) => void): void {
|
||||
if (entity == this.insEntity) {
|
||||
this._insEventManager && this._insEventManager.remove(eventName, callback, entity);
|
||||
return;
|
||||
}
|
||||
this._eventManager && this._eventManager.remove(eventName, callback, entity);
|
||||
}
|
||||
|
||||
/** 更新 */
|
||||
public update(dt: number): void {
|
||||
this._updating = true;
|
||||
this.componentManager._update(dt);
|
||||
this._updating = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 回收Entity
|
||||
* @param {Entity} entity 要回收的Entity
|
||||
*/
|
||||
private _recycleEntity(entity: Entity): void {
|
||||
// 回收实体Id
|
||||
const entityIndex = getEntityIndex(entity.id);
|
||||
this._recycleEntityIds.push(entityIndex);
|
||||
this._entityPool[entityIndex] = null;
|
||||
++this._entityVersion[entityIndex];
|
||||
|
||||
this._destroyEntity(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁实体
|
||||
* @param {Entity} entity
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/member-ordering
|
||||
private _destroyEntity(entity: Entity): void {
|
||||
entity._destroy();
|
||||
if (this._recyclePool.length < this._maxCapacityInPool) {
|
||||
this._recyclePool.push(entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 实体根据tag添加到tag列表中
|
||||
* @param entity
|
||||
*/
|
||||
private _addEntityToTag(entity: Entity): void {
|
||||
const tags = entity.tags;
|
||||
if (!tags || tags.size == 0) {
|
||||
return;
|
||||
}
|
||||
const entityId = entity.id;
|
||||
for (const tag of tags.values()) {
|
||||
this._addEntityTag(entityId, tag);
|
||||
}
|
||||
}
|
||||
|
||||
private _validateEntityById(entityId: EntityId): void {
|
||||
if (!this.exists(entityId)) {
|
||||
throw new Error(`实体(${entityId})不存在`);
|
||||
}
|
||||
}
|
||||
}
|
||||
17
src/ecmodule/ObjectBase.ts
Normal file
17
src/ecmodule/ObjectBase.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
export class ObjectBase {
|
||||
/** 是否被回收 */
|
||||
public recycled: boolean;
|
||||
|
||||
/** 对象类型 */
|
||||
public objectType: number;
|
||||
|
||||
/** 回收 */
|
||||
public _recycle(): void {
|
||||
this.recycled = true;
|
||||
}
|
||||
|
||||
/** 重新利用 */
|
||||
public _reuse(): void {
|
||||
this.recycled = false;
|
||||
}
|
||||
}
|
||||
64
src/ecmodule/ObjectFactory.ts
Normal file
64
src/ecmodule/ObjectFactory.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { ObjectBase } from "./ObjectBase";
|
||||
|
||||
export class ObjectFactory {
|
||||
/** 对象类 */
|
||||
private _ctor: new () => ObjectBase;
|
||||
/** 对象名称 */
|
||||
private _name: string;
|
||||
/** 对象类型 */
|
||||
private _objectType: number;
|
||||
/** 最大容量 */
|
||||
private _maxCapacity: number;
|
||||
/** 对象池 */
|
||||
private _stack: ObjectBase[] = [];
|
||||
|
||||
constructor(objectType: number, capacity: number, name: string, objectClass: new () => ObjectBase) {
|
||||
this._objectType = objectType;
|
||||
this._maxCapacity = capacity;
|
||||
this._name = name;
|
||||
this._ctor = objectClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象名称
|
||||
* @returns {string} 对象名称
|
||||
*/
|
||||
public get name(): string {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象
|
||||
* @returns {T} 返回的组件
|
||||
*/
|
||||
public allocate<T extends ObjectBase>(): T {
|
||||
if (this._stack.length == 0) {
|
||||
const ret = new this._ctor() as T;
|
||||
ret.objectType = this._objectType;
|
||||
return ret;
|
||||
}
|
||||
const ret = this._stack.pop() as T;
|
||||
ret._reuse();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 回收对象
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public recycle(ret: ObjectBase): boolean {
|
||||
if (ret.recycled) {
|
||||
throw new Error(`对象(${ret.constructor.name})已经被回收了`);
|
||||
}
|
||||
if (this._maxCapacity > 0 && this._stack.length < this._maxCapacity) {
|
||||
ret._recycle();
|
||||
this._stack.push(ret);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public _clear(): void {
|
||||
this._stack.length = 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user