diff --git a/CHANGELOG.md b/CHANGELOG.md index f8a751f..945009f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,4 +21,20 @@ ## 1.0.35 - 修复未配置GameEntiry中的ecConfig时报错的问题 ## 1.0.38 -- 修复适配器设计尺寸设置错误的问题 \ No newline at end of file +- 修复适配器设计尺寸设置错误的问题 + +## 1.1.0 模块拆分 +- 拆分资源管理模块,使用 `npm install kunpocc-assets` 安装 + * 仓库地址: https://github.com/Gongxh0901/kunpocc-assets + +- 拆分ec模块,使用 `npm install kunpocc-ec` 安装 + * 仓库地址: https://github.com/Gongxh0901/kunpo-ec + +- 拆分ecs模块,使用 `npm install kunpocc-ecs` 安装 + * 仓库地址: https://github.com/Gongxh0901/kunpo-esc + +- 拆分四叉树模块,使用 `npm install kunpocc-quadtree` 安装 + * 仓库地址: https://github.com/Gongxh0901/kunpo-quadtree + +- 拆分行为树模块,使用 `npm install kunpocc-behaviortree` 安装 + * 仓库地址: https://github.com/Gongxh0901/kunpocc-behaviortree \ No newline at end of file diff --git a/README.md b/README.md index 148b8b7..ee5b16e 100644 --- a/README.md +++ b/README.md @@ -40,21 +40,22 @@ npm set registry https://npm.aliyun.com 2. [项目配置](./docs/Basic.md) 3. [UI模块](./docs/UI.md) -4. [实体组件模块](./docs/EC.md) -5. [网络模块](./docs/HTTP.md) -6. [四叉树](./docs/QuadTree.md) -7. [行为树](./docs/BehaviorTree.md) -8. [资源管理](./docs/Asset.md) -9. [条件显示节点 (一般用于UI上的红点)](./docs/Condition.md) -10. [全局事件](./docs/Event.md) -11. [全局计时器](./docs/Timer.md) -12. [平台工具](./docs/Platform.md) -13. [屏幕尺寸](./docs/Screen.md) -14. [小工具](./docs/Tools.md) -15. [时间](./docs/Time.md) -16. [socket网络模块](./docs/Socket.md) -17. [小游戏接口封装](./docs/MiniGame.md) -18. [热更新](./docs/HotUpdate.md) +4. [ec模块](https://github.com/Gongxh0901/kunpo-ec) +5. [ecs模块](https://github.com/Gongxh0901/kunpo-esc) +6. [网络模块](./docs/HTTP.md) +7. [四叉树](https://github.com/Gongxh0901/kunpo-quadtree) +8. [行为树](https://github.com/Gongxh0901/kunpocc-behaviortree) +9. [资源管理](https://github.com/Gongxh0901/kunpocc-assets) +10. [条件显示节点 (一般用于UI上的红点)](./docs/Condition.md) +11. [全局事件](./docs/Event.md) +12. [全局计时器](./docs/Timer.md) +13. [平台工具](./docs/Platform.md) +14. [屏幕尺寸](./docs/Screen.md) +15. [小工具](./docs/Tools.md) +16. [时间](./docs/Time.md) +17. [socket网络模块](./docs/Socket.md) +18. [小游戏接口封装](./docs/MiniGame.md) +19. [热更新](./docs/HotUpdate.md) ## 类型支持 该库完全使用 TypeScript 编写,提供完整的类型定义文件。 diff --git a/docs/Asset.md b/docs/Asset.md deleted file mode 100644 index 85cc457..0000000 --- a/docs/Asset.md +++ /dev/null @@ -1,102 +0,0 @@ -## 资源加载 -> !!! 注意:资源加载多次和一次效果一样 - -### 特点 - * 可通过路径或者uuid获取资源 - - * 只适合手动管理资源,单无论加载多少次,卸载一次后删除 - - * 可根据 `new kunpo.AssetLoader("batchName")` 传入的 `batchName`批量卸载资源 - - > 比如进入战斗时,创建了多个new kunpo.AssetLoader("batchName") 来加载资源,传入的batchName相同 - > - > 等退出战斗后,可以通过 AssetPool.releaseBatchAssets("batchName") 一键释放所有等于batchName的资源 - -### 使用 -```typescript - let paths: kunpo.IAssetConfig[] = [ - { path: "ui/manual", type: cc.Asset }, - { path: "prefab", type: cc.Prefab }, - { path: "icon", type: cc.SpriteFrame }, - { path: "texture/6101/spriteFrame", type: cc.SpriteFrame, isFile: true }, - { path: "pet", type: cc.SpriteFrame, bundle: "bundle_res" }, - ]; - let loader = new kunpo.AssetLoader("batchName"); - loader.start({ - configs: paths, - complete: () => { - console.log("加载完成"); - }, - fail: (msg: string, err: Error) => { - console.log("加载失败", msg, err); - }, - progress: (percent: number) => { - console.log("加载进度", percent); - } - }); -``` - -### 接口 -#### *资源加载器* - -```typescript -interface IAssetConfig { - /** 资源类型 */ - type: typeof Asset; - /** 资源路径 */ - path: string; - /** 是否是单个文件 默认是文件夹 */ - isFile?: boolean; - /** 资源包名 默认 resources */ - bundle?: string; -} - -/** - * 开始加载资源 - * @param {IAssetConfig[]} res.configs 资源配置 - * @param {number} res.parallel 并行加载数量 默认 10 - * @param {number} res.retry 失败重试次数 默认 3 - * @param {Function} res.complete 加载完成回调 - * @param {Function} res.progress 加载进度回调 - * @param {Function} res.fail 加载失败回调 - */ -public start(res: { configs: IAssetConfig[], parallel?: number, retry?: number, complete: () => void, fail: (msg: string, err: Error) => void, progress?: (percent: number) => void }): void - -/** 重试 重新加载失败的资源 */ -public retry(): void -``` - -#### *资源池* - -```typescript -/** 资源是否已加载 */ -public static has(path: string, bundlename: string = "resources"): boolean - -/** 获取资源 */ -public static get(path: string, bundlename: string = "resources"): T - -/** 按 uuid 判断资源是否已加载 */ -public static hasUUID(uuid: string): boolean - -/** 按 uuid 获取资源 */ -public static getByUUID(uuid: string): T - -/** 按资源路径释放资源 */ -public static releasePath(path: string, bundlename: string = "resources"): void - -/** 按 bundle 和 文件夹释放资源 */ -public static releaseDir(dir: string, bundlename: string = "resources", asset: typeof Asset): Promise - -/** 按 uuid 释放资源 */ -public static releaseUUID(uuid: string): void - -/** 释放所有加载的资源 */ -public static releaseAll(): void - -/** - * 按资源加载批次释放资源 - * @param batchName 资源加载批次名 对应 AssetLoader 实例化时传入的 name - */ -public static releaseBatchAssets(batchName: string): void; -``` - diff --git a/docs/BehaviorTree.md b/docs/BehaviorTree.md deleted file mode 100644 index 1b15e04..0000000 --- a/docs/BehaviorTree.md +++ /dev/null @@ -1,141 +0,0 @@ -## 行为树 - -> 行为树是一种强大的 AI 决策系统,用于实现复杂的游戏 AI 行为。 - -#### 基本概念 - -1. 节点状态 -```typescript -enum Status { - SUCCESS, // 成功 - FAILURE, // 失败 - RUNNING // 运行中 -} -``` - -2. 节点类型 -- **动作节点 (Action)**:执行具体行为的叶子节点 -- **组合节点 (Composite)**:控制子节点执行顺序的节点 -- **条件节点 (Condition)**:判断条件的节点 -- **装饰节点 (Decorator)**:修饰其他节点行为的节点 - -#### 使用示例 - -```typescript -import { - BehaviorTree, - Sequence, - Selector, - Parallel, - Success, - Failure, - WaitTime, - Agent, - Blackboard -} from 'kunpocc'; - -// 1. 创建行为树 -const tree = new BehaviorTree( - new Sequence( // 顺序节点:按顺序执行所有子节点 - new WaitTime(2), // 等待2秒 - new Selector( // 选择节点:选择一个可执行的子节点 - new Success(() => { - console.log("执行成功动作"); - }), - new Failure(() => { - console.log("执行失败动作"); - }) - ) - ) -); - -// 2. 创建代理和黑板 -const agent = new Agent(); // AI代理 -const blackboard = new Blackboard(); // 共享数据黑板 - -// 3. 执行行为树 -tree.tick(agent, blackboard); -``` - -#### 常用节点 - -1. 组合节点 - - ```typescript - // 顺序节点:按顺序执行所有子节点,直到遇到失败或运行中的节点 - new Sequence(childNode1, childNode2, childNode3); - - // 选择节点:选择第一个成功或运行中的子节点 - new Selector(childNode1, childNode2, childNode3); - - // 并行节点:同时执行所有子节点 - new Parallel(childNode1, childNode2, childNode3); - - // 记忆顺序节点:记住上次执行的位置 - new MemSequence(childNode1, childNode2, childNode3); - - // 记忆选择节点:记住上次执行的位置 - new MemSelector(childNode1, childNode2, childNode3); - - // 随机选择节点:随机选择一个子节点执行 - new RandomSelector(childNode1, childNode2, childNode3); - ``` - -2. 动作节点 - - ```typescript - // 成功节点 - new Success(() => { - // 执行动作 - }); - - // 失败节点 - new Failure(() => { - // 执行动作 - }); - - // 运行中节点 - new Running(() => { - // 持续执行的动作 - }); - - // 等待节点 - new WaitTime(2); // 等待2秒 - new WaitTicks(5); // 等待5个tick - ``` - -3. 使用黑板共享数据 - - ```typescript - // 在节点中使用黑板 - class CustomAction extends Action { - tick(ticker: Ticker): Status { - // 获取数据 - const data = ticker.blackboard.get("key"); - - // 设置数据 - ticker.blackboard.set("key", "value"); - - return Status.SUCCESS; - } - } - ``` - - -#### 注意事项 - -1. 节点状态说明: - - `SUCCESS`:节点执行成功 - - `FAILURE`:节点执行失败 - - `RUNNING`:节点正在执行中 -2. 组合节点特性: - - `Sequence`:所有子节点返回 SUCCESS 才返回 SUCCESS - - `Selector`:任一子节点返回 SUCCESS 就返回 SUCCESS - - `Parallel`:并行执行所有子节点 - - `MemSequence/MemSelector`:会记住上次执行位置 -3. 性能优化: - - 使用黑板共享数据,避免重复计算 - - 合理使用记忆节点,减少重复执行 - - 控制行为树的深度,避免过于复杂 - - diff --git a/docs/EC.md b/docs/EC.md deleted file mode 100644 index 0c0a97e..0000000 --- a/docs/EC.md +++ /dev/null @@ -1,524 +0,0 @@ -## 实体组件模块 -> 实体组件系统是一种用于游戏开发的架构模式,它将游戏对象(实体)的数据(组件)和行为分离。 - -### 特点 - * 不同实体上的组件更新顺序管理(`只根据注册的组件更新顺序更新,跟实体无关`) - * 灵活的EC装饰器 (配合插件 `kunpo-ec` 使用,配置实体组件信息,一键导出) - * 支持多世界(多战斗场景,互不影响) - * 区分数据组件和逻辑组件,只更新逻辑组件 - -### 插件链接 - -* **kunpo-ec**: [https://store.cocos.com/app/detail/7311](https://store.cocos.com/app/detail/7311) - -### 使用 - -#### *creator插件`kunpo-ec`* -> `kunpo-cc`可以方便创建、配置、导出实体,操作界面如下图: - -![image-entity-editor](../image/image-entity-editor.png#pic_left) - -#### *使用* - -1. 组件类型声明 - - ```typescript - /** - * @Author: gongxh - * @Date: 2025-01-23 - * @Description: 组件枚举 - */ - - import { cc } from "../header"; - - /** 数据组件类型 */ - enum DataComponentType { - Health, - Transform, - RootNode, - LimitMove, - /** 渲染组件 (多个) */ - Render, - } - - /** 逻辑组件类型 (组件更新数据从上到下) */ - export enum SystemComponentType { - Move = 100000, - ScreenRebound, - - /** 位置更新系统 */ - PositionUpdateSystem = 120000, - } - - export const ComponentType = { - ...DataComponentType, - ...SystemComponentType - }; - export type ComponentType = DataComponentType | SystemComponentType; - - /** 自定义组件更新顺序列表 */ - export const componentUpdateOrderList = cc.Enum.getList(cc.Enum(SystemComponentType)).map(item => item.value).sort((a, b) => a - b); - ``` - -2. 编写组件脚本 - - ```typescript - import { AnimationClip, Asset, AudioClip, Color, Enum, JsonAsset, ParticleAsset, Prefab, Size, Skeleton, SpriteFrame, Vec2, Vec3 } from "cc"; - import { _ecdecorator, Component } from "kunpocc"; - import { ComponentType } from "../../ComponentTypes"; - const { ecclass, ecprop } = _ecdecorator; - - enum HealthType { - HP = 1, - Max = 2, - Current = 3 - } - - // 注册组件 (必须) - @ecclass("Health", ComponentType.Health, { describe: "血量组件" }) - export class Health extends Component { - // 注册组件属性 (可选: 使用kunpo-ec插件则必须注册) - @ecprop({ type: "entity", defaultValue: "", displayName: "实体", tips: "实体" }) - private testentity: string = ""; - - @ecprop({ type: "array", format: "entity", displayName: "实体数组", tips: "实体数组" }) - private testentityarray: string[] = []; - - @ecprop({ type: 'int', defaultValue: 0, displayName: "血量", tips: "当前血量提示" }) - private hp: number = 0; - - @ecprop({ type: 'float', defaultValue: 0, displayName: "最大血量", tips: "最大血量提示" }) - private maxHp: number = 0; - - @ecprop({ type: 'string', defaultValue: "", displayName: "字符串", tips: "字符串提示" }) - private string: string = ""; - - @ecprop({ type: 'boolean', defaultValue: false, displayName: "布尔值", tips: "布尔值提示" }) - private bool: boolean = true; - - @ecprop({ type: "enum", format: Enum(HealthType), defaultValue: HealthType.Current, displayName: "枚举", tips: "枚举提示" }) - private hpeunm: HealthType = HealthType.Current; - - @ecprop({ type: "spriteframe", displayName: "精灵帧" }) - private spriteFrame: SpriteFrame; - - @ecprop({ type: "asset", displayName: "资源" }) - private asset: Asset; - - @ecprop({ type: "prefab", displayName: "预制体" }) - private prefab: Prefab; - - @ecprop({ type: "skeleton", displayName: "骨骼动画" }) - private skeleton: Skeleton; - - @ecprop({ type: "particle", displayName: "粒子" }) - private particle: ParticleAsset; - - @ecprop({ type: "animation", displayName: "动画" }) - private animation: AnimationClip; - - @ecprop({ type: "audio", displayName: "音频" }) - private audio: AudioClip; - - @ecprop({ type: "jsonAsset", displayName: "json资源" }) - private jsonAsset: JsonAsset; - - @ecprop({ - type: "object", format: { - hp1: { - type: "object", - format: { - hp: "int", - max: "int" - } - }, - hp2: { - type: "object", - format: { - hp: "int", - max: "int" - } - }, - }, - }) - private obj: { hp1: { hp: number, max: number }, hp2: { hp: number, max: number } }; - - @ecprop({ - type: "array", format: "int", - }) - private arr: number[]; - - @ecprop({ - type: "array", format: { type: "object", format: { hp: "int", max: "int" } } - }) - private arrobj: { hp: number, max: number }[]; - - @ecprop({ type: "vec2", displayName: "向量2" }) - private vec2: Vec2; - - @ecprop({ type: "vec3", displayName: "向量3" }) - private vec3: Vec3; - - @ecprop({ type: "color", defaultValue: Color.RED, displayName: "颜色" }) - private color: Color; - - @ecprop({ type: "size", displayName: "尺寸" }) - private size: Size; - - protected onAdd(): void { - // 设置组件是否更新,只有需要更新的组件才设置 - this.needUpdate = true; - } - - protected onEnter(): void { - // 可在此获取同实体上的其他组件 - let transform = this.getComponent(ComponentType.Transform); - /** 获取单例组件 */ - let signleton = this.entity.entityManager.getSingleton(ComponentType.XXXX); - } - - protected onRemove(): void { - // 清理组件数据 - } - } - ``` - -3. 创建ec世界,并设置更新 - - ```typescript - /** - * @Author: Gongxh - * @Date: 2025-01-16 - * @Description: 战斗界面 - */ - - import { ECManager } from "kunpocc"; - import { componentUpdateOrderList } from "../../ec/ComponentTypes"; - import { cc, fgui, kunpo } from "../../header"; - const { uiclass, uiprop, uiclick } = kunpo._uidecorator; - - @uiclass("Window", "Game", "GameWindow") - export class GameWindow extends kunpo.Window { - @uiprop container: fgui.GComponent; - public onInit() { - console.log("GameWindow onInit"); - this.adapterType = kunpo.AdapterType.Full; - this.type = kunpo.WindowType.CloseAll; - this.bgAlpha = 0; - } - - protected onShow() { - console.log("GameWindow onShow"); - /** 创建一个ec世界的节点 */ - let node = new cc.Node(); - this.container.node.addChild(node); - - /** - * 创建一个ec世界 - * 参数1: 世界名称 - * 参数2: 世界节点 - * 参数3: 组件更新顺序列表 - * 参数4: 实体池的最大缓存数量,多余的不会被缓存,根据需要调整 - * 参数5: 预创建的实体数量,根据需要调整 - */ - kunpo.log("需要更新的组件", componentUpdateOrderList); - ECManager.createECWorld("world", node, componentUpdateOrderList, 100, 10); - } - - protected onClose() { - /** 退出游戏时 销毁ec世界 */ - ECManager.destroyECWorld("world"); - } - - @uiclick - private onBack(): void { - kunpo.WindowManager.showWindow("HomeWindow"); - } - - @uiclick - private onCreateEntity(): void { - /** 创建一个实体 */ - ECManager.createEntity("world", "entity1"); - } - - protected onUpdate(dt: number): void { - /** 更新ec世界 */ - ECManager.getECWorld("world").update(dt); - } - } - ``` - -#### 重点接口 - -注:详细说明查看声明文件 `kunpocc.d.ts` - -1. 总管理器 `ECManager` - - ```typescript - /**注册所有组件 如果GameEntry因分包导致,组件的代码注册晚于CocosEntry的onInit函数, 则需要在合适的时机手动调用此方法*/ - public static registerComponents(): void - - /** - * 创建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 - - /** 获取EC世界 */ - public static getECWorld(worldName: string): EntityManager - - /** 获取EC世界节点 */ - public static getECWorldNode(worldName: string): Node - - /** 销毁EC世界 */ - public static destroyECWorld(worldName: string): void - - /** - * 注册配置表中的实体信息 - * 如果在GameEntry中配置了ecConfig,则此方法会自动调用 - * @param config 实体配置信息,格式为 {实体名: {组件名: 组件数据}} - */ - public static registerEntityConfig(config: { [entityName: string]: IEntityConfig }): void - - /** - * 添加实体信息 (如果已经存在, 则数据组合) - * 如果存在编辑器编辑不了的数据 用来给编辑器导出的实体信息 添加扩展数据 - * @param name 实体名 - * @param info 实体信息 - */ - public static addEntityInfo(name: string, info: IEntityConfig): void - - /** 获取实体配置信息 */ - public static getEntityInfo(name: string): Record - - /** - * 创建实体 - * @param worldName 实体管理器名称 - * @param name 实体名字 - * @returns {kunpo.Entity} 实体 - */ - public static createEntity(worldName: string, name: string): Entity - - /** - * 销毁实体 - * @param worldName 世界名称 - * @param entity 实体 - */ - public static destroyEntity(worldName: string, entity: Entity): void - - /** - * 通过实体ID销毁实体 - * @param worldName 世界名称 - * @param entityId 实体ID - */ - public static destroyEntityById(worldName: string, entityId: number): void - ``` - -2. 实体管理器 (创建的world)`EntityManager ` - - ```typescript - /** - * 通过实体ID获取实体 - * @param {EntityId} entityId 实体Id - * @returns {(Entity | null)} 实体 - */ - public getEntity(entityId: EntityId): Entity | null - - /** - * 获取指定标签的实体 - * @param {number} tag 标签 - * @returns {Entity[]} 返回的实体池 - */ - public getEntitiesByTag(tag: number): Entity[] - - /** - * 根据实体ID判断实体是否存在 - * @param {EntityId} entityId 实体Id - * @returns {boolean} - */ - public exists(entityId: EntityId): boolean - - /** 添加单例组件 */ - public addSingleton(component: Component): void - - /** 获取单例组件 */ - public getSingleton(componentType: number): T - - /** 删除单例组件 */ - public removeSingleton(componentType: number): void - - /** 是否存在对应的单例组件 */ - public hasSingleton(componentType: number): boolean - - /** 激活单例组件 */ - public activeSingleton(): void - - - /** 更新 需要外部调用 */ - public update(dt: number): void - ``` - -3. 实体 `Entity` - - ```typescript - /** 实体名称 */ - public name: string; - - /** 实体ID */ - public id: EntityId; - - /** 实体标识 */ - public tags: Set; - - /** 实体状态 */ - public states: Map; - - /** 是否被激活 (添加到实体管理器时激活) */ - public active: boolean = false; - - /** 所属实体管理器 (实体创建后直接赋值) */ - public entityManager: EntityManager; - - /** 所有组件 */ - public readonly components: Map = new Map(); - - /** 添加标签 标签除了表示Entity,还可以通过EntityManager获取指定标签的Entity */ - public addTag(...tag: number[]): void - - /** 删除标签 */ - public removeTag(tag: number): void - - /** 是否包含标签 */ - public hasTag(...tag: number[]): boolean - - /** 获取组件 */ - public getComponent(componentType: number): T - - /** 添加组件 */ - public addComponent(component: Component): void - - /** 删除组件 */ - public removeComponent(componentType: number): void - - /** 删除所有组件 */ - public removeAllComponents(): void - - /** - * 是否包含组件 - * @param {number} componentType 组件类型 - */ - public hasComponent(componentType: number): boolean - - /** 销毁自己 */ - 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 - - /** - * 发送消息 - * @param eventName 消息名 - * @param entityId 实体ID - * @param args 发送参数 - */ - public sendListener(eventName: string, ...args: any[]): void - - /** 删除监听 */ - public removeListener(eventName: string, callback?: (...args: any[]) => void): void - - /** - * 添加状态 - * 状态采用计数方式,对状态处理时需要保证addState和removeState成对存在 - * @param {number} state 状态类型 - */ - public addState(state: number): void - - /** - * 删除状态 - * @param {number} state 状态类型 - * @returns {boolean} 如果计数为0或状态不存在,则返回true - */ - public removeState(state: number): boolean - - /** 是否包含指定状态 */ - public hasState(state: number): boolean - - /** 清除状态 */ - public clearState(state: number): void - - /** 清除所有状态 */ - public clearAllStates(): void - ``` - - - -4. 组件 `Component` - - ```typescript - /** 组件名 */ - public name: string; - - /** 组件类型 */ - public type: number; - - /** 是否需要更新 */ - public needUpdate: boolean; - - /** 所属实体 */ - public entity: Entity; - - /** 所属组件管理器 */ - public componentManager: ComponentManager; - - /** - * 获取同实体上的组件 - * @param {number} componentType 组件类型 - */ - public getComponent(componentType: number): T - - /** 删除自己 */ - public destroySelf(): void - - /** - * 生命周期函数 - * 被添加到实体 对应onDestroy - */ - protected onAdd(): void - - /** - * 生命周期函数 - * 组件被销毁 对应onAdd - */ - protected onDestroy(): void - - /** - * 生命周期函数 - * 可在此方法获取实体其他组件 - */ - protected abstract onEnter(): void; - - /** - * 生命周期函数 - * 从实体中删除前执行的函数 在此函数中清理初始化的数据 - */ - protected abstract onRemove(): void; - - /** - * 更新函数 - */ - protected onUpdate(dt: number): void - ``` - - \ No newline at end of file diff --git a/docs/QuadTree.md b/docs/QuadTree.md deleted file mode 100644 index 5a7118c..0000000 --- a/docs/QuadTree.md +++ /dev/null @@ -1,132 +0,0 @@ -## 四叉树 -> 四叉树是一种通过空间划分来进行高效碰撞查询的数据结构。 - -#### 基本概念 - -1. 形状类型 - - ```typescript - import { QuadTree, Box, Circle, Polygon } from 'kunpocc'; - - // 1. 矩形 - const box = new Box(x, y, width, height, tag); - - // 2. 圆形 - const circle = new Circle(x, y, radius, tag); - - // 3. 多边形 - const points = [v2(x1, y1), v2(x2, y2), v2(x3, y3)]; - const polygon = new Polygon(points, tag); - ``` - -2. 配置参数 - - ```typescript - // 四叉树配置 - const QTConfig = { - MAX_SHAPES: 12, // 每个节点最大形状数量 - MAX_LEVELS: 5, // 最大深度 - } - ``` - - -#### 使用示例 - -1. 创建和初始化 - - ```typescript - import { QuadTree, Box, rect } from 'kunpocc'; - - // 创建四叉树(参数:区域范围,层级,绘制组件) - const bounds = rect(0, 0, 800, 600); // x, y, width, height - const quadTree = new QuadTree(bounds); - - // 添加形状 - const player = new Box(100, 100, 50, 50, 1); // 玩家碰撞体,tag=1 - const enemy = new Circle(200, 200, 25, 2); // 敌人碰撞体,tag=2 - quadTree.insert(player); - quadTree.insert(enemy); - ``` - -2. 碰撞检测 - - ```typescript - // 检测指定形状与特定标签的碰撞 - const collisions = quadTree.collide(player, 2); // 检测玩家与 tag=2 的形状碰撞 - if (collisions.length > 0) { - console.log('发生碰撞!'); - for (const target of collisions) { - // 处理碰撞逻辑 - } - } - ``` - -3. 动态更新 - - ```typescript - // 在游戏循环中更新四叉树 - function update() { - // 更新形状位置 - player.position = v2(newX, newY); - enemy.position = v2(newX, newY); - - // 更新四叉树 - quadTree.update(); - - // 检测碰撞 - const collisions = quadTree.collide(player, 2); - } - ``` - -4. 清理 - - ```typescript - // 清理四叉树 - quadTree.clear(); - ``` - - -#### 形状操作 - -1. 位置和缩放 - - ```typescript - // 设置位置 - shape.position = v2(x, y); - - // 设置缩放 - shape.scale = 1.5; - - // 获取包围盒 - const boundingBox = shape.getBoundingBox(); - ``` - -2. 特定形状操作 - - ```typescript - // 矩形重置 - box.resetPoints(x, y, width, height); - - // 圆形半径 - circle.radius = newRadius; - - // 多边形顶点 - polygon.points = newPoints; - ``` - - -#### 性能优化建议 - -1. 合理设置配置参数: - - `MAX_SHAPES`:较小的值会导致更频繁的分裂,较大的值会降低查询效率 - - `MAX_LEVELS`:控制树的最大深度,防止过度分割 - -2. 碰撞检测优化: - - 使用合适的标签系统,只检测需要的碰撞 - - 根据游戏需求选择合适的形状(圆形计算最快) - - 避免使用过于复杂的多边形 - -3. 更新策略: - - 仅在必要时更新四叉树 - - 对于静态物体,可以使用单独的四叉树 - - 动态物体频繁更新时,考虑使用更大的边界范围 \ No newline at end of file diff --git a/image/image-entity-editor.png b/image/image-entity-editor.png deleted file mode 100644 index 9261b12..0000000 Binary files a/image/image-entity-editor.png and /dev/null differ diff --git a/package.json b/package.json index 73757d8..af0bdd3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "kunpocc", - "version": "1.0.38", + "version": "1.1.3", "description": "基于creator3.0+的kunpocc库", "main": "./dist/kunpocc.cjs", "module": "./dist/kunpocc.mjs", @@ -28,7 +28,10 @@ "dist/kunpocc.mjs", "dist/kunpocc.min.cjs", "dist/kunpocc.min.mjs", - "dist/kunpocc.d.ts" + "dist/kunpocc.d.ts", + "libs/lib.ali.api.d.ts", + "libs/lib.bytedance.api.d.ts", + "libs/lib.wx.api.d.ts" ], "author": "gongxh", "license": "ISC", diff --git a/src/asset/AssetLoader.ts b/src/asset/AssetLoader.ts deleted file mode 100644 index 9035ea5..0000000 --- a/src/asset/AssetLoader.ts +++ /dev/null @@ -1,336 +0,0 @@ -/** - * @Author: Gongxh - * @Date: 2025-02-11 - * @Description: 资源加载器 - */ - -import { Asset, AssetManager, resources } from "cc"; -import { log } from "../tool/log"; -import { MathTool } from "../tool/Math"; -import { AssetPool } from "./AssetPool"; -import { AssetUtils } from "./AssetUtils"; - -export interface IAssetConfig { - /** 资源类型 */ - type: typeof Asset; - /** 资源路径 */ - path: string; - /** 是否是单个文件 默认是文件夹 */ - isFile?: boolean; - /** 资源包名 默认 resources */ - bundle?: string; -} - -/** - * 资源加载的状态类型 - * @internal - */ -enum StateType { - Error, - Wait, - Loading, - Finish, -} - -export class AssetLoader { - /** - * 资源加载器名称 - * @internal - */ - private _name: string = ""; - /** - * 资源总数 - * @internal - */ - private _total: number = 0; - /** - * 最大并行加载数量 - * @internal - */ - private _maxParallel: number = 10; - /** - * 当前并行加载数量 - * @internal - */ - private _parallel: number = 0; - /** - * 失败重试次数 - * @internal - */ - private _maxRetry: number = 3; - /** - * 失败重试次数 - * @internal - */ - private _retry: number = 0; - - /** - * 获取资源数量是否成功 - * @internal - */ - private _initSuccess: boolean = false; - - /** - * 加载进度回调 - * @internal - */ - private _progress: (percent: number) => void; - - /** - * 加载完成回调 - * @internal - */ - private _complete: () => void; - - /** - * 加载失败回调 - * @internal - */ - private _fail: (msg: string, err: Error) => void; - - /** - * 资源配置 - * @internal - */ - private _configs: IAssetConfig[] = []; - /** - * 资源加载项 - * @internal - */ - private _items: { type: typeof Asset, bundle: string, path: string, isFile?: boolean, status: StateType, count: number }[] = []; - /** - * 加载完成数量 - * @internal - */ - private _completeCounts: Map = new Map(); - - constructor(batchName?: string) { - this._name = batchName || ""; - } - - /** - * 开始加载资源 - * @param {IAssetConfig[]} res.configs 资源配置 - * @param {number} res.parallel 并行加载数量 默认 10 - * @param {number} res.retry 失败重试次数 默认 3 - * @param {Function} res.complete 加载完成回调 - * @param {Function} res.progress 加载进度回调 - * @param {Function} res.fail 加载失败回调 - */ - public start(res: { configs: IAssetConfig[], parallel?: number, retry?: number, complete: () => void, fail: (msg: string, err: Error) => void, progress?: (percent: number) => void }): void { - this._configs = res.configs; - this._maxParallel = res.parallel || 10; - this._maxRetry = res.retry || 3; - this._complete = res.complete; - this._progress = res.progress; - this._fail = res.fail; - - this._total = 0; - this._initSuccess = false; - this._items.length = 0; - - let initCount = res.configs.length; - for (const item of res.configs) { - let bundlename = item.bundle || "resources"; - let count = 0; - if (bundlename == "resources") { - count = AssetUtils.getResourceCount(item.path, item.type); - this._total += count; - - this._items.push({ type: item.type, bundle: item.bundle || "resources", path: item.path, isFile: item.isFile || false, status: StateType.Wait, count: count }) - initCount--; - - initCount <= 0 && this.initSuccess(); - } else { - AssetUtils.loadBundle(bundlename).then((bundle: AssetManager.Bundle) => { - count = AssetUtils.getResourceCount(item.path, item.type, bundle); - this._total += count; - - this._items.push({ type: item.type, bundle: item.bundle || "resources", path: item.path, isFile: item.isFile || false, status: StateType.Wait, count: count }) - initCount--; - - initCount <= 0 && this.initSuccess(); - }).catch((err: Error) => { - if (this._retry < this._maxRetry) { - this.retryStart(); - } else { - this._fail(`加载资源包[${bundlename}]失败`, err); - } - }); - } - } - } - - /** 重试 (重新加载失败的资源) */ - public retry(): void { - this._parallel = 0; - this._retry = 0; - if (!this._initSuccess) { - this.retryStart(); - } else { - this.retryLoad(); - } - } - - /** - * 重试开始 - * @internal - */ - private retryStart(): void { - this._retry++; - this.start({ - configs: this._configs, - parallel: this._maxParallel, - retry: this._maxRetry, - complete: this._complete, - fail: this._fail, - progress: this._progress - }); - } - - /** - * 重试加载资源 - * @internal - */ - private retryLoad(): void { - this._retry++; - let count = this.resetErrorItem(); - let maxLoad = Math.min(count, this._maxParallel); - for (let i = 0; i < maxLoad; i++) { - this.loadNext(); - } - } - - /** - * 初始化成功后,开始批量加载资源 - * @internal - */ - private initSuccess(): void { - this._initSuccess = true; - this._parallel = 0; - let maxLoad = Math.min(this._items.length, this._maxParallel); - for (let i = 0; i < maxLoad; i++) { - this.loadNext(); - } - } - - /** - * 加载下一个资源 - * @internal - */ - private loadNext(): void { - // 找到第一个等待中的资源 - let index = this._items.findIndex(item => item.status == StateType.Wait); - if (index > -1) { - this.loadItem(index); - } else if (!this._items.some(item => item.status != StateType.Finish)) { - // 所有资源全部完成了 - this._complete(); - } else if (this._parallel <= 0 && this._retry < this._maxRetry) { - this.retryLoad(); - } - } - - /** - * 重置失败资源状态为等待中 - * @internal - */ - private resetErrorItem(): number { - let count = 0; - for (const item of this._items) { - if (item.status == StateType.Error) { - item.status = StateType.Wait; - count++; - } - } - return count; - } - - /** - * 加载资源 - * @internal - */ - private loadItem(index: number): void { - let item = this._items[index]; - item.status = StateType.Loading; - this._parallel++; - if (item.bundle == "resources") { - if (item.isFile) { - this.loadFile(index, resources); - } else { - this.loadDir(index, resources); - } - } else { - AssetUtils.loadBundle(item.bundle).then((bundle: AssetManager.Bundle) => { - if (item.isFile) { - this.loadFile(index, bundle); - } else { - this.loadDir(index, bundle); - } - }).catch((err: Error) => { - log(`load bundle error, bundle:${item.bundle}, filename:${item.path}`); - item.status = StateType.Error; - }); - } - } - - /** - * 加载资源 - * @internal - */ - private loadDir(index: number, bundle: AssetManager.Bundle): void { - let item = this._items[index]; - bundle.loadDir(item.path, item.type, (finish: number, total: number) => { - if (total > 0 && finish > 0) { - this._completeCounts.set(`${item.bundle}:${item.path}`, finish); - this._progress && this.updateProgress(); - } - }, (error: Error, assets: Array) => { - this._parallel--; - if (error) { - log(`load dir error, bundle:${item.bundle}, dir:${item.path}`); - item.status = StateType.Error; - } else { - item.status = StateType.Finish; - this._completeCounts.set(`${item.bundle}:${item.path}`, assets.length); - AssetPool.add(assets, bundle, this._name); - } - this._progress && this.updateProgress(); - this.loadNext(); - }); - } - - /** - * 加载资源 - * @internal - */ - private loadFile(index: number, bundle: AssetManager.Bundle): void { - let item = this._items[index]; - bundle.load(item.path, item.type, (error: Error, asset: Asset) => { - this._parallel--; - if (error) { - log(`load file error, bundle:${item.bundle}, filename:${item.path}`); - item.status = StateType.Error; - } else { - item.status = StateType.Finish; - this._completeCounts.set(`${item.bundle}:${item.path}`, 1); - AssetPool.add(asset, bundle, this._name); - } - - this._progress && this.updateProgress(); - this.loadNext(); - }); - } - - /** - * 更新进度 - * @internal - */ - private updateProgress(): void { - let value = 0; - for (const count of this._completeCounts.values()) { - value += count; - } - this._progress(MathTool.clampf(value / this._total, 0, 1)); - } -} diff --git a/src/asset/AssetPool.ts b/src/asset/AssetPool.ts deleted file mode 100644 index 3e356db..0000000 --- a/src/asset/AssetPool.ts +++ /dev/null @@ -1,170 +0,0 @@ -/** - * @Author: Gongxh - * @Date: 2025-02-11 - * @Description: 资源池 - */ - -import { Asset, AssetManager, resources } from "cc"; -import { log } from "../tool/log"; -import { AssetUtils } from "./AssetUtils"; - -export class AssetPool { - /** - * 资源名对应的资源 - * @internal - */ - private static _assets: { [path: string]: Asset } = {}; - /** - * uuid 对应的资源名 - * @internal - */ - private static _uuidToName: Map = new Map(); - /** - * 资源加载批次对应的资源名 - * @internal - */ - private static _batchAssetNames: Map = new Map(); - - /** 批量添加资源 */ - public static add(asset: Asset[] | Asset, bundle: AssetManager.Bundle = resources, batchName: string = ""): void { - if (Array.isArray(asset)) { - for (const item of asset) { - this.add(item, bundle, batchName); - } - } else { - let uuid = asset.uuid || asset._uuid; - if (this._uuidToName.has(uuid)) { - return; - } - // 增加引用计数 - asset.addRef(); - let info = bundle.getAssetInfo(uuid); - let key = this.getKey(info.path, bundle.name); - // log(`>>>uuid:${uuid}, path:${info.path}`); - this._uuidToName.set(uuid, key); - this._assets[key] = asset; - - if (batchName) { - let names = this._batchAssetNames.get(batchName) || []; - names.push(key); - this._batchAssetNames.set(batchName, names); - } - } - } - - public static has(path: string, bundlename: string = "resources"): boolean { - let key = this.getKey(path, bundlename); - if (!this._assets[key]) { - return false; - } - return true; - } - - public static get(path: string, bundlename: string = "resources"): T { - let key = this.getKey(path, bundlename); - if (!this._assets[key]) { - log(`获取资源失败: 资源 bundle:${bundlename}, path:${path} 未加载`); - } - return this._assets[key] as T; - } - - /** 按 uuid 判断资源是否存在 */ - public static hasUUID(uuid: string): boolean { - if (!this._uuidToName.has(uuid)) { - return false; - } - return true; - } - - /** 按 uuid 获取资源 */ - public static getByUUID(uuid: string): T { - if (!this._uuidToName.has(uuid)) { - log(`获取资源失败: 资源 uuid:${uuid} 未加载`); - } - let key = this._uuidToName.get(uuid); - return this._assets[key] as T; - } - - /** - * 按资源加载批次释放资源 - * @param batchName 资源加载批次名 对应 AssetLoader 实例化时传入的 name - */ - public static releaseBatchAssets(batchName: string): void { - if (!this._batchAssetNames.has(batchName)) { - return; - } - let names = this._batchAssetNames.get(batchName); - for (const name of names) { - this.release(name); - } - this._batchAssetNames.delete(batchName); - } - - /** 按资源路径释放资源 */ - public static releasePath(path: string, bundlename: string = "resources"): void { - let key = this.getKey(path, bundlename); - this.release(key); - } - - /** 按 bundle 和 文件夹释放资源 */ - public static releaseDir(dir: string, bundlename: string = "resources", asset: typeof Asset): Promise { - return new Promise((resolve, reject) => { - if (bundlename == "resources") { - let uuids = AssetUtils.getUUIDs(dir, asset, resources); - for (const uuid of uuids) { - this.releaseUUID(uuid); - } - resolve(true); - } else { - AssetUtils.loadBundle(bundlename).then((bundle: AssetManager.Bundle) => { - let uuids = AssetUtils.getUUIDs(dir, asset, bundle); - for (const uuid of uuids) { - this.releaseUUID(uuid); - } - resolve(true); - }).catch((err: Error) => { - reject(false); - }); - } - }); - } - - /** 按 uuid 释放资源 */ - public static releaseUUID(uuid: string): void { - if (this._uuidToName.has(uuid)) { - let key = this._uuidToName.get(uuid); - this.release(key); - } - } - - /** 释放所有加载的资源 */ - public static releaseAll(): void { - for (const key in this._assets) { - this._assets[key].decRef(); - } - this._assets = {}; - this._uuidToName.clear(); - this._batchAssetNames.clear(); - } - - /** - * 按key释放资源 - * @internal - */ - private static release(key: string): void { - if (this._assets[key]) { - this._uuidToName.delete(this._assets[key].uuid); - - this._assets[key].decRef(); - delete this._assets[key]; - } - } - - /** - * 获取资源 key - * @internal - */ - private static getKey(path: string, bundlename: string = "resources"): string { - return `${bundlename}:${path}`; - } -} diff --git a/src/asset/AssetUtils.ts b/src/asset/AssetUtils.ts deleted file mode 100644 index 040be2e..0000000 --- a/src/asset/AssetUtils.ts +++ /dev/null @@ -1,52 +0,0 @@ -/** - * @Author: Gongxh - * @Date: 2025-02-11 - * @Description: 资源工具类 - */ - -import { Asset, AssetManager, assetManager, resources } from "cc"; - - -export class AssetUtils { - /** 获取资源数量 */ - public static getResourceCount(dir: string, type: typeof Asset, bundle: AssetManager.Bundle = resources): number { - dir = assetManager.utils.normalize(dir); - if (dir[dir.length - 1] === "/") { - dir = dir.slice(0, -1); - } - let list = bundle.getDirWithPath(dir, type); - return list.length; - } - - /** 获取资源名称 */ - public static getUUIDs(dir: string, type: typeof Asset, bundle: AssetManager.Bundle = resources): string[] { - let uuids: string[] = []; - let path = assetManager.utils.normalize(dir); - if (path[path.length - 1] === "/") { - path = path.slice(0, -1); - } - let list = bundle.getDirWithPath(path, type); - for (const asset of list) { - uuids.push(asset.uuid); - } - return uuids; - } - - /** 加载 bundle */ - public static loadBundle(bundlename: string): Promise { - return new Promise((resolve, reject) => { - let bundle = assetManager.getBundle(bundlename); - if (bundle) { - resolve(bundle); - } else { - assetManager.loadBundle(bundlename, (err: Error, bundle: AssetManager.Bundle) => { - if (err) { - reject(err); - } else { - resolve(bundle); - } - }); - } - }); - } -} diff --git a/src/behaviortree/Agent.ts b/src/behaviortree/Agent.ts deleted file mode 100644 index 96d0c1e..0000000 --- a/src/behaviortree/Agent.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { BehaviorTree } from "./BehaviorTree"; -import { Blackboard } from "./Blackboard"; -import { Ticker } from "./Ticker"; - -/** 代理 */ -export class Agent { - /** 行为树 */ - public tree: BehaviorTree; - /** 黑板 */ - public blackboard: Blackboard; - /** 更新器 */ - public ticker: Ticker; - /** - * constructor - * @param subject // 主体 - * @param tree 行为树 - */ - constructor(subject: any, tree: BehaviorTree) { - this.tree = tree; - this.blackboard = new Blackboard(); - this.ticker = new Ticker(subject, this.blackboard, tree); - } - - /** - * 执行 - */ - public tick(): void { - this.tree.tick(this, this.blackboard, this.ticker); - if (this.blackboard.interrupt) { - this.blackboard.interrupt = false; - - let ticker = this.ticker; - ticker.openNodes.length = 0; - ticker.nodeCount = 0; - - this.blackboard.clear(); - } - } - - /** - * 打断行为树,重新开始执行(如果当前在节点中,下一帧才会清理) - */ - public interruptBTree(): void { - if (!this.blackboard.interruptDefend) { - this.blackboard.interrupt = true; - } - } -} \ No newline at end of file diff --git a/src/behaviortree/BTNode/Action.ts b/src/behaviortree/BTNode/Action.ts deleted file mode 100644 index b87b878..0000000 --- a/src/behaviortree/BTNode/Action.ts +++ /dev/null @@ -1,189 +0,0 @@ -import { Status } from "../header"; -import { Ticker } from "../Ticker"; -import { BaseNode } from "./BaseNode"; - -/** - * 动作节点 - * 没有子节点 - */ -export abstract class Action extends BaseNode { - constructor() { - super(); - } -} - -/** - * 失败节点(无子节点) - * 直接返回FAILURE - */ -export class Failure extends Action { - /** 执行函数 @internal */ - private _func: () => void; - constructor(func: () => void) { - super(); - this._func = func; - } - - /** - * 执行 - * @param {Ticker} ticker - * @returns {Status} - */ - public tick(ticker: Ticker): Status { - this._func(); - return Status.FAILURE; - } -} - -/** - * 逻辑节点,一直执行 (无子节点) - * 直接返回RUNING - */ -export class Running extends Action { - /** 执行函数 @internal */ - private _func: () => void; - constructor(func: () => void) { - super(); - this._func = func; - } - - /** - * 执行 - * @param {Ticker} ticker - * @returns {Status} - */ - public tick(ticker: Ticker): Status { - this._func(); - return Status.RUNNING; - } -} - -/** - * 成功节点 无子节点 - * 直接返回SUCCESS - */ -export class Success extends Action { - /** 执行函数 @internal */ - private _func: () => void; - constructor(func: () => void) { - super(); - this._func = func; - } - - /** - * 执行 - * @param {Ticker} ticker - * @returns {Status} - */ - public tick(ticker: Ticker): Status { - this._func(); - return Status.SUCCESS; - } -} -/** - * 次数等待节点(无子节点) - * 次数内,返回RUNING - * 超次,返回SUCCESS - */ -export class WaitTicks extends Action { - /** 最大次数 @internal */ - private _maxTicks: number; - /** 经过的次数 @internal */ - private _elapsedTicks: number; - constructor(maxTicks: number = 0) { - super(); - this._maxTicks = maxTicks; - this._elapsedTicks = 0; - } - - /** - * 打开 - * @param {Ticker} ticker - */ - public open(ticker: Ticker): void { - this._elapsedTicks = 0; - } - - /** - * 执行 - * @param {Ticker} ticker - * @returns {Status} - */ - public tick(ticker: Ticker): Status { - if (++this._elapsedTicks >= this._maxTicks) { - this._elapsedTicks = 0; - return Status.SUCCESS; - } - return Status.RUNNING; - } -} - -/** - * 时间等待节点(无子节点) - * 时间到后返回SUCCESS,否则返回RUNING - */ -export class WaitTime extends Action { - /** 等待时间(毫秒 ms) @internal */ - private _duration: number; - constructor(duration: number = 0) { - super(); - this._duration = duration * 1000; - } - - /** - * 打开 - * @param {Ticker} ticker - */ - public open(ticker: Ticker): void { - let startTime = new Date().getTime(); - ticker.blackboard.set("startTime", startTime, ticker.tree.id, this.id); - } - - /** - * 执行 - * @param {Ticker} ticker - * @returns {Status} - */ - public tick(ticker: Ticker): Status { - let currTime = new Date().getTime(); - let startTime = ticker.blackboard.get("startTime", ticker.tree.id, this.id); - if (currTime - startTime >= this._duration) { - return Status.SUCCESS; - } - return Status.RUNNING; - } -} - -/** - * 行为树防止被打断节点 - * 直接返回 SUCCESS - * 和 InterruptDefendCancel 必须成对出现 - */ -export class InterruptDefend extends Action { - /** - * 执行 - * @param {Ticker} ticker - * @returns {Status} - */ - public tick(ticker: Ticker): Status { - ticker.blackboard.interruptDefend = true; - return Status.SUCCESS; - } -} - -/** - * 行为树被打断取消节点 - * 直接返回 SUCCESS - * 和 InterruptDefend 必须成对出现 - */ -export class InterruptDefendCancel extends Action { - /** - * 执行 - * @param {Ticker} ticker - * @returns {Status} - */ - public tick(ticker: Ticker): Status { - ticker.blackboard.interruptDefend = false; - return Status.SUCCESS; - } -} \ No newline at end of file diff --git a/src/behaviortree/BTNode/BaseNode.ts b/src/behaviortree/BTNode/BaseNode.ts deleted file mode 100644 index 7244fa9..0000000 --- a/src/behaviortree/BTNode/BaseNode.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { createUUID, Status } from "../header"; -import { Ticker } from "../Ticker"; - -/** - * 基础节点 - * 所有节点全部继承自 BaseNode - */ -export abstract class BaseNode { - /** 唯一标识 */ - public id: string; - /** 子节点 */ - public children: BaseNode[]; - - /** - * 创建 - * @param children 子节点列表 - */ - constructor(children?: BaseNode[]) { - this.id = createUUID(); - this.children = []; - if (!children) { - return; - } - for (let i = 0; i < children.length; i++) { - this.children.push(children[i]); - } - } - - /** - * 执行节点 - * @param ticker 更新器 - * @returns {Status} 状态 - */ - public _execute(ticker: Ticker): Status { - /* ENTER */ - this._enter(ticker); - if (!ticker.blackboard.get("isOpen", ticker.tree.id, this.id)) { - this._open(ticker); - } - let status = this._tick(ticker); - if (status !== Status.RUNNING) { - this._close(ticker); - } - this._exit(ticker); - return status; - } - - /** - * 进入节点 - * @param ticker 更新器 - * @internal - */ - public _enter(ticker: Ticker): void { - ticker.enterNode(this); - this.enter(ticker); - } - - /** - * 打开节点 - * @param ticker 更新器 - * @internal - */ - public _open(ticker: Ticker): void { - ticker.openNode(this); - ticker.blackboard.set("isOpen", true, ticker.tree.id, this.id); - this.open(ticker); - } - - /** - * 更新节点 - * @param ticker 更新器 - * @internal - */ - public _tick(ticker: Ticker): Status { - ticker.tickNode(this); - return this.tick(ticker); - } - - /** - * 关闭节点 - * @param ticker 更新器 - * @internal - */ - public _close(ticker: Ticker): void { - ticker.closeNode(this); - ticker.blackboard.set("isOpen", false, ticker.tree.id, this.id); - this.close(ticker); - } - - /** - * 退出节点 - * @param ticker 更新器 - * @internal - */ - public _exit(ticker: Ticker): void { - ticker.exitNode(this); - this.exit(ticker); - } - - enter(ticker: Ticker): void { - - } - open(ticker: Ticker): void { - - } - close(ticker: Ticker): void { - - } - exit(ticker: Ticker): void { - - } - abstract tick(ticker: Ticker): Status; -} \ No newline at end of file diff --git a/src/behaviortree/BTNode/Composite.ts b/src/behaviortree/BTNode/Composite.ts deleted file mode 100644 index f59c94c..0000000 --- a/src/behaviortree/BTNode/Composite.ts +++ /dev/null @@ -1,206 +0,0 @@ -import { Status } from "../header"; -import { Ticker } from "../Ticker"; -import { BaseNode } from "./BaseNode"; - -/** - * 可以包含多个节点的集合装饰器基类 - * - */ -export abstract class Composite extends BaseNode { - constructor(...children: BaseNode[]) { - super(children); - } -} - -/** - * 记忆选择节点 - * 选择不为 FAILURE 的节点 - * 任意一个Child Node返回不为 FAILURE, 本Node向自己的Parent Node也返回Child Node状态 - */ -export class MemSelector extends Composite { - /** - * 打开 - * @param {Ticker} ticker - */ - public open(ticker: Ticker): void { - super.open(ticker); - ticker.blackboard.set("runningChild", 0, ticker.tree.id, this.id); - } - - /** - * 执行 - * @param {Ticker} ticker - * @returns {Status} - */ - public tick(ticker: Ticker): Status { - let childIndex = ticker.blackboard.get("runningChild", ticker.tree.id, this.id) as number; - - for (let i = childIndex; i < this.children.length; i++) { - let status = this.children[i]._execute(ticker); - - if (status !== Status.FAILURE) { - if (status === Status.RUNNING) { - ticker.blackboard.set("runningChild", i, ticker.tree.id, this.id); - } - return status; - } - } - - return Status.FAILURE; - } -} - -/** - * 记忆顺序节点 - * 如果上次执行到 RUNING 的节点, 下次进入节点后, 直接从 RUNING 节点开始 - * 遇到 RUNING 或者 FAILURE 停止迭代 - * 任意一个Child Node返回不为 SUCCESS, 本Node向自己的Parent Node也返回Child Node状态 - * 所有节点都返回 SUCCESS, 本节点才返回 SUCCESS - */ -export class MemSequence extends Composite { - /** - * 打开 - * @param {Ticker} ticker - */ - public open(ticker: Ticker): void { - super.open(ticker); - ticker.blackboard.set("runningChild", 0, ticker.tree.id, this.id); - } - - /** - * 执行 - * @param {Ticker} ticker - * @returns {Status} - */ - public tick(ticker: Ticker): Status { - let childIndex = ticker.blackboard.get("runningChild", ticker.tree.id, this.id) as number; - for (let i = childIndex; i < this.children.length; i++) { - let status = this.children[i]._execute(ticker); - if (status !== Status.SUCCESS) { - if (status === Status.RUNNING) { - ticker.blackboard.set("runningChild", i, ticker.tree.id, this.id); - } - return status; - } - } - return Status.SUCCESS; - } -} - -/** - * 随机选择节点 - * 从Child Node中随机选择一个执行 - */ -export class RandomSelector extends Composite { - /** - * 执行 - * @param {Ticker} ticker - * @returns {Status} - */ - public tick(ticker: Ticker): Status { - let childIndex = (Math.random() * this.children.length) | 0; - let child = this.children[childIndex]; - let status = child._execute(ticker); - - return status; - } -} - -/** - * 选择节点,选择不为 FAILURE 的节点 - * 当执行本类型Node时,它将从begin到end迭代执行自己的Child Node: - * 如遇到一个Child Node执行后返回 SUCCESS 或者 RUNING,那停止迭代,本Node向自己的Parent Node也返回 SUCCESS 或 RUNING - */ -export class Selector extends Composite { - /** - * 执行 - * @param {Ticker} ticker - * @returns {Status} - */ - public tick(ticker: Ticker): Status { - for (let i = 0; i < this.children.length; i++) { - let status = this.children[i]._execute(ticker); - if (status !== Status.FAILURE) { - return status; - } - } - return Status.FAILURE; - } -} - -/** - * 顺序节点 - * 当执行本类型Node时,它将从begin到end迭代执行自己的Child Node: - * 遇到 FAILURE 或 RUNING, 那停止迭代,返回FAILURE 或 RUNING - * 所有节点都返回 SUCCESS, 本节点才返回 SUCCESS - */ -export class Sequence extends Composite { - /** - * 执行 - * @param {Ticker} ticker - * @returns {Status} - */ - public tick(ticker: Ticker): Status { - for (let i = 0; i < this.children.length; i++) { - let status = this.children[i]._execute(ticker); - if (status !== Status.SUCCESS) { - return status; - } - } - return Status.SUCCESS; - } -} - -/** - * 并行节点 每次进入全部重新执行一遍 - * 当执行本类型Node时,它将从begin到end迭代执行自己的Child Node: - * 1. 当存在Child Node执行后返回 FAILURE, 本节点返回 FAILURE - * 2. 当存在Child Node执行后返回 RUNING, 本节点返回 RUNING - * 所有节点都返回 SUCCESS, 本节点才返回 SUCCESS - */ -export class Parallel extends Composite { - /** - * 执行 - * @param {Ticker} ticker - * @returns {Status} - */ - public tick(ticker: Ticker): Status { - let result = Status.SUCCESS; - for (let i = 0; i < this.children.length; i++) { - let status = this.children[i]._execute(ticker); - if (status == Status.FAILURE) { - result = Status.FAILURE; - } else if (result == Status.SUCCESS && status == Status.RUNNING) { - result = Status.RUNNING; - } - } - return result; - } -} - -/** - * 并行节点 每次进入全部重新执行一遍 - * 当执行本类型Node时,它将从begin到end迭代执行自己的Child Node: - * 1. 当存在Child Node执行后返回 FAILURE, 本节点返回 FAILURE - * 2. 任意 Child Node 返回 SUCCESS, 本节点返回 SUCCESS - * 否则返回 RUNNING - */ -export class ParallelAnySuccess extends Composite { - /** - * 执行 - * @param {Ticker} ticker - * @returns {Status} - */ - public tick(ticker: Ticker): Status { - let result = Status.RUNNING; - for (let i = 0; i < this.children.length; i++) { - let status = this.children[i]._execute(ticker); - if (status == Status.FAILURE) { - result = Status.FAILURE; - } else if (result == Status.RUNNING && status == Status.SUCCESS) { - result = Status.SUCCESS; - } - } - return result; - } -} \ No newline at end of file diff --git a/src/behaviortree/BTNode/Condition.ts b/src/behaviortree/BTNode/Condition.ts deleted file mode 100644 index 9302397..0000000 --- a/src/behaviortree/BTNode/Condition.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Status } from "../header"; -import { Ticker } from "../Ticker"; -import { Action } from "./Action"; - -/** - * 条件节点 - */ -export class Condition extends Action { - /** 执行函数 @internal */ - private _func: (subject: any) => boolean = null; - constructor(func: (subject: any) => boolean) { - super(); - this._func = func; - } - - /** - * 执行 - * @param {Ticker} ticker - * @returns {Status} - */ - public tick(ticker: Ticker): Status { - return this._func(ticker.subject) ? Status.SUCCESS : Status.FAILURE; - } -} \ No newline at end of file diff --git a/src/behaviortree/BTNode/Decorator.ts b/src/behaviortree/BTNode/Decorator.ts deleted file mode 100644 index 72ebcf9..0000000 --- a/src/behaviortree/BTNode/Decorator.ts +++ /dev/null @@ -1,367 +0,0 @@ -import { Status } from "../header"; -import { Ticker } from "../Ticker"; -import { BaseNode } from "./BaseNode"; - -/** - * 修饰节点基类 - * 只能包含一个子节点 - */ -export abstract class Decorator extends BaseNode { - constructor(child: BaseNode) { - super([child]); - } -} - -/** - * 失败节点 - * 必须且只能包含一个子节点 - * 直接返回 FAILURE - * @extends Decorator - */ -export class Failer extends Decorator { - /** - * 执行 - * @param {Ticker} ticker - * @returns {Status} - */ - public tick(ticker: Ticker): Status { - if (this.children.length !== 1) { - throw new Error("(Failer)节点必须包含一个子节点"); - } - let child = this.children[0]; - child._execute(ticker); - return Status.FAILURE; - } -} - -/** - * 结果反转节点 - * 必须且只能包含一个子节点 - * 第一个Child Node节点, 返回 FAILURE, 本Node向自己的Parent Node也返回 SUCCESS - * 第一个Child Node节点, 返回 SUCCESS, 本Node向自己的Parent Node也返回 FAILURE - */ -export class Inverter extends Decorator { - /** - * 执行 - * @param {Ticker} ticker - * @returns {Status} - */ - public tick(ticker: Ticker): Status { - if (this.children.length !== 1) { - throw new Error("(Inverter)节点必须包含一个子节点"); - } - let child = this.children[0]; - let status = child._execute(ticker); - if (status === Status.SUCCESS) { - status = Status.FAILURE; - } else if (status === Status.FAILURE) { - status = Status.SUCCESS; - } - return status; - } -} - -/** - * 次数限制节点 - * 必须且只能包含一个子节点 - * 次数限制内, 根据Child Node的结果, 本Node向自己的Parent Node也返回相同的结果 - * 次数超过后, 直接返回 FAILURE - */ -export class LimiterTicks extends Decorator { - /** 最大次数 @internal */ - private _maxTicks: number; - /** 当前执行过的次数 @internal */ - private _elapsedTicks: number; - - /** - * 创建 - * @param maxTicks 最大次数 - * @param child 子节点 - */ - constructor(maxTicks: number, child: BaseNode) { - super(child); - this._maxTicks = maxTicks; - this._elapsedTicks = 0; - } - - /** - * 打开 - * @param {Ticker} ticker - */ - public open(ticker: Ticker): void { - super.open(ticker); - this._elapsedTicks = 0; - } - - /** - * 执行 - * @param {Ticker} ticker - * @returns {Status} - */ - public tick(ticker: Ticker): Status { - if (this.children.length !== 1) { - throw new Error("(LimiterTicks)节点必须包含一个子节点"); - } - let child = this.children[0]; - if (++this._elapsedTicks > this._maxTicks) { - this._elapsedTicks = 0; - return Status.FAILURE; - } - return child._execute(ticker); - } -} - -/** - * 时间限制节点 - * 只能包含一个子节点 - * 规定时间内, 根据Child Node的结果, 本Node向自己的Parent Node也返回相同的结果 - * 超时后, 直接返回 FAILURE - */ -export class LimiterTime extends Decorator { - /** 最大时间 (毫秒 ms) @internal */ - private _maxTime: number; - - /** - * 时间限制节点 - * @param maxTime 最大时间 (微秒ms) - * @param child 子节点 - */ - constructor(maxTime: number, child: BaseNode) { - super(child); - this._maxTime = maxTime * 1000; - } - - /** - * 打开 - * @param {Ticker} ticker - */ - public open(ticker: Ticker): void { - super.open(ticker); - let startTime = new Date().getTime(); - ticker.blackboard.set("startTime", startTime, ticker.tree.id, this.id); - } - - /** - * 执行 - * @param {Ticker} ticker - * @returns {Status} - */ - public tick(ticker: Ticker): Status { - if (this.children.length !== 1) { - throw new Error("(LimiterTime)节点必须包含一个子节点"); - } - - let child = this.children[0]; - let currTime = new Date().getTime(); - let startTime = ticker.blackboard.get("startTime", ticker.tree.id, this.id); - - if (currTime - startTime > this._maxTime) { - return Status.FAILURE; - } - - return child._execute(ticker); - } -} - -/** - * 循环节点 - * 必须且只能包含一个子节点 - * 如果maxLoop < 0, 直接返回成功 - * 否则等待次数超过之后, 返回Child Node的结果(RUNING的次数不计算在内) - */ -export class Repeater extends Decorator { - /** 最大循环次数 @internal */ - private _maxLoop: number; - - /** - * 创建 - * @param child 子节点 - * @param maxLoop 最大循环次数 - */ - constructor(child: BaseNode, maxLoop: number = -1) { - super(child); - this._maxLoop = maxLoop; - } - - /** - * 打开 - * @param {Ticker} ticker - */ - public open(ticker: Ticker): void { - ticker.blackboard.set("i", 0, ticker.tree.id, this.id); - } - - /** - * 执行 - * @param {Ticker} ticker - * @returns {Status} - */ - public tick(ticker: Ticker): Status { - if (this.children.length !== 1) { - throw new Error("(Repeater)节点必须包含一个子节点"); - } - - let child = this.children[0]; - let i = ticker.blackboard.get("i", ticker.tree.id, this.id); - let status = Status.SUCCESS; - - while (this._maxLoop < 0 || i < this._maxLoop) { - status = child._execute(ticker); - - if (status === Status.SUCCESS || status === Status.FAILURE) { - i++; - } else { - break; - } - } - - ticker.blackboard.set("i", i, ticker.tree.id, this.id); - return status; - } -} - -/** - * 循环节点 - * 只能包含一个子节点 - * 如果maxLoop < 0, 直接返回成功 - * 当Child Node返回 FAILURE, 本Node向自己的Parent Node返回 FAILURE - * 循环次数大于等于maxLoop时, 返回Child Node的结果 - */ -export class RepeatUntilFailure extends Decorator { - /** 最大循环次数 @internal */ - private _maxLoop: number; - - constructor(child: BaseNode, maxLoop: number = -1) { - super(child); - this._maxLoop = maxLoop; - } - - /** - * 打开 - * @param {Ticker} ticker - */ - public open(ticker: Ticker): void { - ticker.blackboard.set("i", 0, ticker.tree.id, this.id); - } - - /** - * 执行 - * @param {Ticker} ticker - * @returns {Status} - */ - public tick(ticker: Ticker): Status { - if (this.children.length !== 1) { - throw new Error("(RepeatUntilFailure)节点必须包含一个子节点"); - } - - let child = this.children[0]; - let i = ticker.blackboard.get("i", ticker.tree.id, this.id); - let status = Status.SUCCESS; - - while (this._maxLoop < 0 || i < this._maxLoop) { - status = child._execute(ticker); - - if (status === Status.SUCCESS) { - i++; - } else { - break; - } - } - - ticker.blackboard.set("i", i, ticker.tree.id, this.id); - return status; - } -} - -/** - * 循环节点(只能包含一个子节点) - * 如果maxLoop < 0, 直接返回失败 - * 当Child Node返回 SUCCESS, 本Node向自己的Parent Node返回 SUCCESS - * 循环次数大于等于maxLoop时, 返回Child Node的结果 - */ -export class RepeatUntilSuccess extends Decorator { - /** 最大循环次数 @internal */ - private _maxLoop: number; - - /** - * 创建 - * @param child 子节点 - * @param maxLoop 最大循环次数 - */ - constructor(child: BaseNode, maxLoop: number = -1) { - super(child); - this._maxLoop = maxLoop; - } - - /** - * 打开 - * @param {Ticker} ticker - */ - public open(ticker: Ticker): void { - ticker.blackboard.set("i", 0, ticker.tree.id, this.id); - } - - /** - * 执行 - * @param {Ticker} ticker - * @returns {Status} - */ - public tick(ticker: Ticker): Status { - if (this.children.length !== 1) { - throw new Error("(RepeatUntilSuccess)节点必须包含一个子节点"); - } - let child = this.children[0]; - let i = ticker.blackboard.get("i", ticker.tree.id, this.id); - let status = Status.FAILURE; - while (this._maxLoop < 0 || i < this._maxLoop) { - status = child._execute(ticker); - if (status === Status.FAILURE) { - i++; - } else { - break; - } - } - ticker.blackboard.set("i", i, ticker.tree.id, this.id); - return status; - } -} - -/** - * 逻辑节点, 一直执行(只能包含一个子节点) - * 直接返回 RUNING - */ -export class Runner extends Decorator { - /** - * 执行 - * @param {Ticker} ticker - * @returns {Status} - */ - public tick(ticker: Ticker): Status { - if (this.children.length !== 1) { - throw new Error("(Runner)节点必须包含一个子节点"); - } - let child = this.children[0]; - child._execute(ticker); - return Status.RUNNING; - } -} - -/** - * 成功节点(包含一个子节点) - * 直接返回 SUCCESS - */ -export class Succeeder extends Decorator { - /** - * 执行 - * @param {Ticker} ticker - * @returns {Status} - */ - public tick(ticker: Ticker): Status { - if (this.children.length !== 1) { - throw new Error("(Succeeder)节点必须包含一个子节点"); - } - let child = this.children[0]; - child._execute(ticker); - return Status.SUCCESS; - } -} \ No newline at end of file diff --git a/src/behaviortree/BehaviorTree.ts b/src/behaviortree/BehaviorTree.ts deleted file mode 100644 index ad33f81..0000000 --- a/src/behaviortree/BehaviorTree.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Blackboard } from "./Blackboard"; -import { BaseNode } from "./BTNode/BaseNode"; -import { createUUID } from "./header"; -import { Ticker } from "./Ticker"; - -/** - * 行为树 - * 所有节点全部添加到树中 - */ -export class BehaviorTree { - /** 行为树ID @internal */ - private _id: string; - /** 行为树跟节点 @internal */ - private _root: BaseNode; - /** - * constructor - * @param root 根节点 - */ - constructor(root: BaseNode) { - this._id = createUUID(); - this._root = root; - } - - /** - * 执行 - * @param subject 主体 - * @param blackboard 黑板 - * @param ticker 更新器 - */ - public tick(subject: any, blackboard: Blackboard, ticker?: Ticker): void { - ticker = ticker || new Ticker(subject, blackboard, this); - ticker.openNodes.length = 0; - this._root._execute(ticker); - // 上次打开的节点 - let lastOpenNodes = blackboard.get("openNodes", this._id) as BaseNode[]; - // 当前打开的节点 - let currOpenNodes = ticker.openNodes; - let start = 0; - for (let i = 0; i < Math.min(lastOpenNodes.length, currOpenNodes.length); i++) { - start = i + 1; - if (lastOpenNodes[i] !== currOpenNodes[i]) { - break; - } - } - // 关闭不需要的节点 - for (let i = lastOpenNodes.length - 1; i >= start; i--) { - lastOpenNodes[i]._close(ticker); - } - /* POPULATE BLACKBOARD */ - blackboard.set("openNodes", currOpenNodes, this._id); - blackboard.set("nodeCount", ticker.nodeCount, this._id); - } - - get id(): string { - return this._id; - } - - get root(): BaseNode { - return this._root; - } -} \ No newline at end of file diff --git a/src/behaviortree/Blackboard.ts b/src/behaviortree/Blackboard.ts deleted file mode 100644 index 6fcec06..0000000 --- a/src/behaviortree/Blackboard.ts +++ /dev/null @@ -1,105 +0,0 @@ -/** - * 行为树数据 - */ -interface ITreeData { - nodeMemory: { [nodeScope: string]: any }; - openNodes: any[]; -} - -/** 平台 */ -export class Blackboard { - /** 行为树打断保护 */ - public interruptDefend: boolean = false; - /** 打断行为树的标记 */ - public interrupt: boolean = false; - /** 基础记忆 @internal */ - private _baseMemory: any; - /** 树记忆 @internal */ - private _treeMemory: { [treeScope: string]: ITreeData }; - - constructor() { - this._baseMemory = {}; - this._treeMemory = {}; - } - - /** - * 清除 - */ - public clear(): void { - this._baseMemory = {}; - this._treeMemory = {}; - } - - /** - * 设置 - * @param key 键 - * @param value 值 - * @param treeScope 树范围 - * @param nodeScope 节点范围 - */ - public set(key: string, value: any, treeScope?: string, nodeScope?: string): void { - let memory = this._getMemory(treeScope, nodeScope); - memory[key] = value; - } - - /** - * 获取 - * @param key 键 - * @param treeScope 树范围 - * @param nodeScope 节点范围 - * @returns 值 - */ - public get(key: string, treeScope?: string, nodeScope?: string): any { - let memory = this._getMemory(treeScope, nodeScope); - return memory[key]; - } - - /** - * 获取树记忆 - * @param treeScope 树范围 - * @returns 树记忆 - * @internal - */ - private _getTreeMemory(treeScope: string): ITreeData { - if (!this._treeMemory[treeScope]) { - this._treeMemory[treeScope] = { - nodeMemory: {}, - openNodes: [], - }; - } - return this._treeMemory[treeScope]; - } - - /** - * 获取节点记忆 - * @param treeMemory 树记忆 - * @param nodeScope 节点范围 - * @returns 节点记忆 - * @internal - */ - private _getNodeMemory(treeMemory: ITreeData, nodeScope: string): { [key: string]: any } { - let memory = treeMemory.nodeMemory; - if (!memory[nodeScope]) { - memory[nodeScope] = {}; - } - return memory[nodeScope]; - } - - /** - * 获取记忆 - * @param treeScope 树范围 - * @param nodeScope 节点范围 - * @returns 记忆 - * @internal - */ - private _getMemory(treeScope?: string, nodeScope?: string): { [key: string]: any } { - let memory = this._baseMemory; - if (treeScope) { - memory = this._getTreeMemory(treeScope); - if (nodeScope) { - memory = this._getNodeMemory(memory, nodeScope); - } - } - return memory; - } -} \ No newline at end of file diff --git a/src/behaviortree/Ticker.ts b/src/behaviortree/Ticker.ts deleted file mode 100644 index 27e3728..0000000 --- a/src/behaviortree/Ticker.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { BehaviorTree } from "./BehaviorTree"; -import { Blackboard } from "./Blackboard"; -import { BaseNode } from "./BTNode/BaseNode"; - -export class Ticker { - tree: BehaviorTree; // 行为树跟节点 - openNodes: BaseNode[]; // 当前打开的节点 - nodeCount: number; // 当前打开的节点数量 - blackboard: Blackboard; // 数据容器 - debug: any; - subject: any; - constructor(subject: any, blackboard: Blackboard, tree: BehaviorTree) { - this.tree = tree; - this.openNodes = []; - this.nodeCount = 0; - this.debug = null; - this.subject = subject; - this.blackboard = blackboard; - } - - /** - * 进入节点 - * @param node 节点 - */ - public enterNode(node: BaseNode): void { - this.nodeCount++; - this.openNodes.push(node); - } - - /** - * 打开节点 - * @param node 节点 - */ - public openNode(node: BaseNode): void { } - - /** - * 更新节点 - * @param node 节点 - */ - public tickNode(node: BaseNode): void { } - - /** - * 关闭节点 - * @param node 节点 - */ - public closeNode(node: BaseNode): void { - this.openNodes.pop(); - } - - /** - * 退出节点 - * @param node 节点 - */ - public exitNode(node: BaseNode): void { } -} \ No newline at end of file diff --git a/src/behaviortree/header.ts b/src/behaviortree/header.ts deleted file mode 100644 index 458bb68..0000000 --- a/src/behaviortree/header.ts +++ /dev/null @@ -1,27 +0,0 @@ -export const enum Status { - FAILURE, - SUCCESS, - RUNNING, -} - -/** - * 创建UUID - * @returns UUID - * @internal - */ -export function createUUID(): string { - let s: string[] = Array(36); - let hexDigits = "0123456789abcdef"; - for (let i = 0; i < 36; i++) { - let start = Math.floor(Math.random() * 0x10); - s[i] = hexDigits.substring(start, start + 1); - } - // bits 12-15 of the time_hi_and_version field to 0010 - s[14] = "4"; - // bits 6-7 of the clock_seq_hi_and_reserved to 01 - let start = (parseInt(s[19], 16) & 0x3) | 0x8; - s[19] = hexDigits.substring(start, start + 1); - s[8] = s[13] = s[18] = s[23] = "-"; - let uuid = s.join(""); - return uuid; -} \ No newline at end of file diff --git a/src/cocos/CocosEntry.ts b/src/cocos/CocosEntry.ts index d9b8020..4e58c53 100644 --- a/src/cocos/CocosEntry.ts +++ b/src/cocos/CocosEntry.ts @@ -5,13 +5,11 @@ */ import { _decorator, Component, director, game, JsonAsset, macro, sys } from "cc"; -import { ECDataHelper } from "../ecmodule/ECDataHelper"; import { GlobalEvent } from "../global/GlobalEvent"; import { GlobalTimer } from "../global/GlobalTimer"; import { enableDebugMode, FrameConfig } from "../global/header"; import { InnerTimer } from "../global/InnerTimer"; import { Platform, PlatformType } from "../global/Platform"; -import { ECManager } from "../kunpocc"; import { ModuleBase } from "../module/ModuleBase"; import { debug, log } from "../tool/log"; import { Time } from "../tool/Time"; @@ -21,7 +19,6 @@ const _global = (globalThis || window || global) as any; const { property } = _decorator; export abstract class CocosEntry extends Component { @property({ displayName: "uiConfig", type: JsonAsset, tooltip: "编辑器导出的UI配置, 可不设置, 之后通过 PropsHelper.setConfig 手动设置" }) uiConfig: JsonAsset = null; - @property({ displayName: "ecConfig", type: JsonAsset, tooltip: "编辑器导出的实体配置, 可不设置, 之后通过 ECManager.registerEntityConfig 手动设置" }) ecConfig: JsonAsset = null; @property({ displayName: "游戏帧率" }) fps: number = 60; /** @@ -49,17 +46,11 @@ export abstract class CocosEntry extends Component { director.addPersistRootNode(this.node); this.node.setSiblingIndex(this.node.children.length - 1); PropsHelper.setConfig(this.uiConfig?.json); - let ecsMaps = _global["getKunpoRegisterECSMaps"]?.(); - if (this.ecConfig && (!ecsMaps || ecsMaps.size <= 0)) { - ECManager.registerEntityConfig(this.ecConfig.json); - } this.initPlatform(); this.initEvent(); this.initTime(); this.initAdapter(); this.initModule(); - // 注册所有组件 - ECDataHelper.registerComponents(); log("kunpo框架初始化完成"); this.onInit(); } diff --git a/src/ecmodule/Component.ts b/src/ecmodule/Component.ts deleted file mode 100644 index 95266c8..0000000 --- a/src/ecmodule/Component.ts +++ /dev/null @@ -1,112 +0,0 @@ -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; - - /** 是否需要销毁 @internal */ - public _needDestroy: boolean; - - /** 更新ID @internal */ - public _updateId: number = -1; - - /** 是否更新中 @internal */ - public get _updating(): boolean { - return this._updateId != -1; - } - - /** 生命周期函数 添加到实体 @internal */ - public _add(): void { - this.onAdd(); - } - - /** 生命周期函数 销毁 @internal */ - public _destroy(): void { - this.onDestroy(); - } - - /** 生命周期函数 添加到实体后 在这个函数中可以获取其他组件 @internal */ - public _enter(): void { - // 自动开启更新 - if (this.needUpdate) { - this.componentManager.startUpdateComponent(this); - } - this.onEnter(); - } - - /** 生命周期函数 从实体中移除 @internal */ - public _remove(): void { - this.stopUpdate(); - this.onRemove(); - this.componentManager._destroyComponent(this); - } - - /** 更新 @internal */ - 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(componentType: number): T { - return this.entity.getComponent(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; -} diff --git a/src/ecmodule/ComponentManager.ts b/src/ecmodule/ComponentManager.ts deleted file mode 100644 index d5ee82f..0000000 --- a/src/ecmodule/ComponentManager.ts +++ /dev/null @@ -1,290 +0,0 @@ -import { Component } from "./Component"; -import { ComponentPool } from "./ComponentPool"; - -/** - * 组件更新信息 - * @internal - */ -export class ComponentUpdate { - /** 组件更新类型 */ - public componentType: number; - - /** 组件更新列表 @internal */ - private readonly _components: Component[] = []; - - /** create constructor @internal */ - public constructor(componentType: number) { - this.componentType = componentType; - } - - /** - * 添加要更新的组件 - * @param component 组件 - * @internal - */ - public addComponent(component: Component): void { - this._components.push(component); - component._updateId = this._components.length - 1; - } - - /** - * 删除要更新的组件 - * @param {Component} component 组件 - * @internal - */ - 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(); - } - - /** 更新 @internal */ - 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} - * @internal - */ - protected componentPool: ComponentPool; - - /** 更新组件池 @internal */ - protected readonly updatingComponents: ComponentUpdate[] = []; - /** 组件更新顺序 @internal */ - protected readonly componentUpdateOrderList: number[] = []; - - /** 新添加的或者新停止更新的组件池 @internal */ - private readonly _toUpdateComponents: Component[] = []; - /** 新停止更新的组件池 @internal */ - private readonly _toStopComponents: Component[] = []; - - /** 当前更新的组件类型 @internal */ - private _currentUpdateComponentType: number = -1; - - /** - *Creates an instance of ComponentManager. - * @param {ComponentPool} componentPool 组件池 - * @param {number[]} componentUpdateOrderList 组件更新顺序 - * @internal - */ - constructor(componentPool: ComponentPool, componentUpdateOrderList: number[]) { - this.componentPool = componentPool; - this._toUpdateComponents.length = 0; - this._toStopComponents.length = 0; - for (const componentType of componentUpdateOrderList) { - this._addComponentUpdateOrder(componentType); - } - } - - /** - * 销毁组件管理器 - * @internal - */ - 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} 创建的组件 - * @internal - */ - public createComponent(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 - * @internal - */ - public _destroyComponent(component: Component): void { - if (!component._updating) { - component._destroy(); - this.componentPool.recycle(component); - } else { - component._needDestroy = true; - } - } - - /** - * 更新所有组件(内部使用) - * @param {number} dt 时间间隔 - * @internal - */ - public _update(dt: number): void { - this._updateAllComponents(dt); - this._currentUpdateComponentType = -1; - - this._clearStopComponents(); - this._addUpdateComponents(); - } - - /** - * 添加组件更新顺序,先添加的先更新 - * @param {number} componentType 组件类型 - * @returns {ComponentManager} 组件管理器 - * @internal - */ - 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; - } - - /** - * 添加组件到组件更新列表 - * @param {Component} component 组件 - * @internal - */ - 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); - } - - /** - * 组件更新列表中删除组件 - * @param {Component} component 组件 - * @internal - */ - private _removeComponentToUpdateList(component: Component): void { - this.updatingComponents[component.type].removeComponent(component); - } - - /** - * 更新所有组件 - * @param {number} dt 时间间隔 - * @internal - */ - 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); - } - } - - /** - * 清除停止更新的组件 - * @internal - */ - 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; - } - } - - /** - * 添加更新组件 - * @internal - */ - 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; - } - } -} diff --git a/src/ecmodule/ComponentPool.ts b/src/ecmodule/ComponentPool.ts deleted file mode 100644 index da4f007..0000000 --- a/src/ecmodule/ComponentPool.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { Component } from "./Component"; -import { ObjectBase } from "./ObjectBase"; -import { ObjectFactory } from "./ObjectFactory"; - -export class ComponentPool { - /** 组件对象类型到组件类型转换 @internal */ - private readonly _objectTypeToComponentType: number[] = new Array(128); - /** 组件池 @internal */ - private _pools: Map = new Map(); - /** 组件名称到组件对象类型转换 @internal */ - private _nameToObjectType: Map = new Map(); - /** - * 注册组件 - * @param {number} componentObjectType 组件对象类型 - * @param {number} componentType 组件类型 - * @param {string} name 组件名称 - * @param {new () => Component} ctor 构造函数 - * @internal - */ - 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; - } - - /** - * 通过组件名称获取组件对象类型 - * @param {string} componentName 组件名称 - * @returns {number} 组件对象类型 - */ - public getObjectTypeByName(componentName: string): number { - return this._nameToObjectType.get(componentName); - } - - /** - * 创建组件 - * @param {number} componentName 组件名 - * @returns {T} 创建的组件 - * @template T - * @internal - */ - public get(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} - * @internal - */ - 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 - * @internal - */ - public recycle(component: Component): void { - const objectFactory = this._pools.get(component.objectType); - objectFactory.recycle(component); - } - - /** - * 清理缓存 - * @internal - */ - public clear(): void { - for (const factory of this._pools.values()) { - factory._clear(); - } - } -} \ No newline at end of file diff --git a/src/ecmodule/ECDataHelper.ts b/src/ecmodule/ECDataHelper.ts deleted file mode 100644 index c0db2a4..0000000 --- a/src/ecmodule/ECDataHelper.ts +++ /dev/null @@ -1,109 +0,0 @@ -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 { - /** 组件池 @internal */ - 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): 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; - } -} diff --git a/src/ecmodule/ECDecorator.ts b/src/ecmodule/ECDecorator.ts deleted file mode 100644 index e497818..0000000 --- a/src/ecmodule/ECDecorator.ts +++ /dev/null @@ -1,141 +0,0 @@ -/** - * @Author: Gongxh - * @Date: 2025-01-14 - * @Description: 实体组件装饰器 - */ - -import { Color, Size, Vec2, Vec3 } from "cc"; -import { ObjectHelper } from "../tool/helper/ObjectHelper"; - - -export namespace _ecdecorator { - /** @internal */ - 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 | Record; - } - - 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; - } - /** 用来存储组件注册信息 */ - const eclassMap: Map = new Map(); - - /** 获取组件注册信息 */ - export function getComponentMaps(): Map { - 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; -}; \ No newline at end of file diff --git a/src/ecmodule/ECManager.ts b/src/ecmodule/ECManager.ts deleted file mode 100644 index 6f15e36..0000000 --- a/src/ecmodule/ECManager.ts +++ /dev/null @@ -1,172 +0,0 @@ -/** - * @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 -} - -interface IWorldConfig { - /** 实体管理器 */ - world: EntityManager; - /** 世界节点 */ - worldNode: Node; -} - -export class ECManager { - /** 实体管理器 @internal */ - private static _worlds: Map = new Map(); - /** 实体配置信息 @internal */ - private static _entityList: { [name: string]: Record } = {}; - - /** 注册所有组件 如果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 { - if (!config) { - return; - } - // 遍历并注册每个实体的配置 - 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 { - 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; - } - - /** - * 添加组件到实体 - * @param {EntityManager} world 实体管理器 - * @param {Entity} entity 实体 - * @param {Record} componentsData 组件数据 - * @internal - */ - private static _addComponentToEntity(world: EntityManager, entity: Entity, componentsData: Record): 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); - } -} diff --git a/src/ecmodule/ECType.ts b/src/ecmodule/ECType.ts deleted file mode 100644 index a5a7520..0000000 --- a/src/ecmodule/ECType.ts +++ /dev/null @@ -1,90 +0,0 @@ -/** - * @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"; - -/** 实体索引位数 @internal */ -export const EntityIndexBits = 16; -/** 实体索引掩码 @internal */ -export const EntityIndexMask = (1 << EntityIndexBits) - 1; -/** 最大实体数量 @internal */ -export const MaxEntityCount = 1 << EntityIndexBits; -export type EntityId = number; - -/** - * 2进制转10进制 (不支持小数和负数) - * @param {number} bitNumber 二进制数 - * @return {number} 十进制数 - * @internal - */ -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} 二进制数 - * @internal - */ -export function decimal2Bit(num: number): number { - let stack = new Stack(); - 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 - * @return {number} 实体index - * @internal - */ -export function getEntityIndex(id: EntityId): number { - return id & EntityIndexMask; -} - -/** - * 通过实体id获取实体版本 - * @param id 实体id - * @return {number} 实体版本 - * @internal - */ -export function getEntityVersion(id: EntityId): number { - return id >>> EntityIndexBits; -} - -/** - * 实体描述 - * @param id 实体id - * @return {string} 实体描述 - * @internal - */ -export function entityIdString(id: EntityId): string { - return `${getEntityIndex(id)}:${getEntityVersion(id)}`; -} -// console.log("-------->", EntityIndexBits); 16 -// console.log("-------->", EntityIndexMask); 65535 -// console.log("-------->", MaxEntityCount); 65536 \ No newline at end of file diff --git a/src/ecmodule/Entity.ts b/src/ecmodule/Entity.ts deleted file mode 100644 index 7acbba0..0000000 --- a/src/ecmodule/Entity.ts +++ /dev/null @@ -1,276 +0,0 @@ -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} - * @memberof Entity - */ - public tags: Set; - - /** - * 实体状态 - * @type {Map} - * @memberof Entity - */ - public states: Map; - /** - * 是否被激活 (添加到实体管理器时激活) - * @type {boolean} - */ - public active: boolean = false; - - /** - * 所属实体管理器 (实体创建后直接赋值) - * @private - * @type {EntityManager} - */ - public entityManager: EntityManager; - - /** - * 所有组件 - * @type {Map} - * @type {number} 组件类型 - * @type {Component} 组件 - */ - public readonly components: Map = new Map(); - - /** - * 实体被添加到EntityManager - * @internal - */ - public _add(): void { - this.active = true; - for (const component of this.components.values()) { - component._enter(); - } - } - - /** - * 实体销毁,不要手动调用 - * @internal - */ - 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(); - } - 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(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(); - } - 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(); - } -} diff --git a/src/ecmodule/EntityManager.ts b/src/ecmodule/EntityManager.ts deleted file mode 100644 index 5096784..0000000 --- a/src/ecmodule/EntityManager.ts +++ /dev/null @@ -1,442 +0,0 @@ -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} - * @internal - */ - public readonly insEntity: Entity = new Entity(); - - /** - * 单例实体激活状态 - * @type {boolean} - * @internal - */ - public insActive: boolean = false; - - /** - * 组件管理 - * @type {ComponentManager} - */ - public componentManager: ComponentManager; - - /** - * 普通实体事件容器 - * @type {EventManager} - * @internal - */ - private _eventManager: EventManager; - - /** - * 单例实体消息监听容器 - * @type {EventManager} - * @internal - */ - private _insEventManager: EventManager; - - /** 实体池 @internal */ - private readonly _entityPool: Entity[] = []; - /** tag标记池 @internal */ - private readonly _tagToEntity: Map> = new Map>(); - /** 实体回收池 @internal */ - private _recyclePool: Entity[] = []; - /** 实体回收池最大容量 @internal */ - private _maxCapacityInPool: number; - /** 实体回收版本 @internal */ - private _entityVersion: number[] = []; - /** 回收实体ID @internal */ - private _recycleEntityIds: EntityId[] = []; - /** 世界是否删除 @internal */ - private _isDestroyed: boolean; - /** 是否正在更新 @internal */ - private _updating: boolean; - /** - * 实体池最大容量,回收的多余的实体不会缓存 - * @param {string} name 名称 - * @param {ComponentPool} componentPool 组件池 - * @param {ComponentPool} componentUpdateOrderList 组件更新顺序 - * @param {number} [maxCapacityInPool=128] 实体回收池最大容量 - * @param {number} [preloadEntityCount=32] 预加载Entity数量 - */ - 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 标签 - * @internal - */ - public _addEntityTag(entityId: EntityId, tag: number): void { - this._validateEntityById(entityId); - let entitiesByTag = this._tagToEntity.get(tag); - if (!entitiesByTag) { - entitiesByTag = new Set(); - this._tagToEntity.set(tag, entitiesByTag); - } - entitiesByTag.add(entityId); - } - - /** - * 删除实体Tag(内部使用) - * @param {Entity} entity 实体 - * @param {number} tag 标签 - * @internal - */ - public _removeEntityTag(entity: Entity, tag: number): void { - this._removeEntityTagById(entity.id, tag); - } - - /** - * 通过实体ID删除实体Tag(内部使用) - * @param {EntityId} entityId 实体Id - * @param {number} tag 标签 - * @internal - */ - 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(componentName: string): T { - return this.componentManager.createComponent(componentName); - } - - /** - * 添加单例组件 - * @param component - */ - public addSingleton(component: Component): void { - this.insEntity.addComponent(component); - } - - /** - * 获取单例组件 - */ - public getSingleton(componentType: number): T { - return this.insEntity.getComponent(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 是否单次事件 - * @internal - */ - 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 发送参数 - * @internal - */ - 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); - } - - /** - * 移除消息监听 (内部使用) - * @param eventName 消息名 - * @param entity 实体 - * @param callback 事件回调 - * @internal - */ - 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); - } - - /** - * 更新 - * @param {number} dt 时间间隔 - */ - public update(dt: number): void { - this._updating = true; - this.componentManager._update(dt); - this._updating = false; - } - - /** - * 回收Entity - * @param {Entity} entity 要回收的Entity - * @internal - */ - 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 - * @internal - */ - private _destroyEntity(entity: Entity): void { - entity._destroy(); - if (this._recyclePool.length < this._maxCapacityInPool) { - this._recyclePool.push(entity); - } - } - - /** - * 实体根据tag添加到tag列表中 - * @param entity - * @internal - */ - 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); - } - } - - /** - * 验证实体ID是否存在 - * @param {EntityId} entityId 实体ID - * @internal - */ - private _validateEntityById(entityId: EntityId): void { - if (!this.exists(entityId)) { - throw new Error(`实体(${entityId})不存在`); - } - } -} diff --git a/src/ecmodule/ObjectBase.ts b/src/ecmodule/ObjectBase.ts deleted file mode 100644 index d285f8c..0000000 --- a/src/ecmodule/ObjectBase.ts +++ /dev/null @@ -1,17 +0,0 @@ -export class ObjectBase { - /** 是否被回收 */ - public recycled: boolean; - - /** 对象类型 */ - public objectType: number; - - /** 回收 @internal */ - public _recycle(): void { - this.recycled = true; - } - - /** 重新利用 @internal */ - public _reuse(): void { - this.recycled = false; - } -} \ No newline at end of file diff --git a/src/ecmodule/ObjectFactory.ts b/src/ecmodule/ObjectFactory.ts deleted file mode 100644 index 7e863c5..0000000 --- a/src/ecmodule/ObjectFactory.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { ObjectBase } from "./ObjectBase"; - -/** @internal */ -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 { - 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; - } -} \ No newline at end of file diff --git a/src/fgui/WindowBase.ts b/src/fgui/WindowBase.ts index 8e70693..fe16e17 100644 --- a/src/fgui/WindowBase.ts +++ b/src/fgui/WindowBase.ts @@ -23,6 +23,8 @@ export abstract class WindowBase extends GComponent implements IWindow { private _header: IWindowHeader = null; /** 窗口是否被遮挡了 @internal */ private _isCover: boolean = false; + /** 吞噬触摸的节点 @internal */ + private _swallowComponent: GComponent = null; /** * 初始化方法 (框架内部使用) * @param swallowTouch 是否吞噬触摸事件 @@ -33,14 +35,16 @@ export abstract class WindowBase extends GComponent implements IWindow { if (swallowTouch) { // 吞噬触摸事件,需要一个全屏的节点, 窗口本身可能留有安全区的边 let bgNode = new GComponent(); + bgNode.name = "swallow"; bgNode.setSize(Screen.ScreenWidth, Screen.ScreenHeight, true); bgNode.setPivot(0.5, 0.5, true); - bgNode.setPosition(Screen.ScreenWidth * 0.5, Screen.ScreenHeight * 0.5); + bgNode.setPosition(this.width * 0.5, this.height * 0.5); this.addChild(bgNode); // 调整显示层级 bgNode.parent.setChildIndex(bgNode, 0); bgNode.onClick(this.onEmptyAreaClick, this); bgNode.opaque = swallowTouch; + this._swallowComponent = bgNode; } // 窗口自身也要设置是否吞噬触摸 this.opaque = swallowTouch; @@ -65,6 +69,8 @@ export abstract class WindowBase extends GComponent implements IWindow { default: break; } + this._swallowComponent?.setSize(Screen.ScreenWidth, Screen.ScreenHeight, true); + this._swallowComponent?.setPosition(this.width * 0.5, + this.height * 0.5); this.onAdapted(); } diff --git a/src/kunpocc.ts b/src/kunpocc.ts index db79336..43c4c66 100644 --- a/src/kunpocc.ts +++ b/src/kunpocc.ts @@ -28,25 +28,6 @@ export { Socket } from "./net/socket/Socket"; /** 读取网络文件 */ export { ReadNetFile } from "./net/nettools/ReadNetFile"; -/** 四叉树 */ -export { Box } from "./quadtree/Box"; -export { Circle } from "./quadtree/Circle"; -export { IShape } from "./quadtree/IShape"; -export { Polygon } from "./quadtree/Polygon"; -export { QTConfig, QuadTree } from "./quadtree/QuadTree"; - -/** 行为树 */ -export { Agent as BTAgent } from "./behaviortree/Agent"; -export { BehaviorTree } from "./behaviortree/BehaviorTree"; -export { Blackboard as BTBlackboard } from "./behaviortree/Blackboard"; -export * as BTAction from "./behaviortree/BTNode/Action"; -export * as BTNode from "./behaviortree/BTNode/BaseNode"; -export * as BTComposite from "./behaviortree/BTNode/Composite"; -export * as BTCondition from "./behaviortree/BTNode/Condition"; -export * as BTDecorator from "./behaviortree/BTNode/Decorator"; -export { Status as BTStatus } from "./behaviortree/header"; -export { Ticker as BTTicker } from "./behaviortree/Ticker"; - /** UI */ export { Window } from "./fgui/Window"; export { WindowHeader } from "./fgui/WindowHeader"; @@ -57,23 +38,10 @@ export { WindowGroup } from "./ui/WindowGroup"; export { WindowHeaderInfo } from "./ui/WindowHeaderInfo"; export { WindowManager } from "./ui/WindowManager"; -/** EC */ -export { Component } from './ecmodule/Component'; -export { ComponentManager } from './ecmodule/ComponentManager'; -export { ComponentPool } from './ecmodule/ComponentPool'; -export { _ecdecorator } from './ecmodule/ECDecorator'; -export { ECManager } from './ecmodule/ECManager'; -export { Entity } from './ecmodule/Entity'; -export { EntityManager } from './ecmodule/EntityManager'; - /** 引擎相关 */ export { CocosEntry } from "./cocos/CocosEntry"; export { CocosUIModule } from "./cocos/CocosUIModule"; -/** 资源相关 */ -export { AssetLoader, IAssetConfig } from "./asset/AssetLoader"; -export { AssetPool } from "./asset/AssetPool"; - /** 条件显示节点 */ export { _conditionDecorator } from "./condition/ConditionDecorator"; export { ConditionManager } from "./condition/ConditionManager"; diff --git a/src/net/http/HttpManager.ts b/src/net/http/HttpManager.ts index ec2d582..20ef654 100644 --- a/src/net/http/HttpManager.ts +++ b/src/net/http/HttpManager.ts @@ -4,7 +4,6 @@ * @Description: 网络请求管理器 */ -import { GlobalEvent } from "../../global/GlobalEvent"; import { HttpRequest } from "./HttpRequest"; import { IHttpEvent } from "./IHttpEvent"; import { IHttpResponse } from "./IHttpResponse"; @@ -82,23 +81,15 @@ export class HttpManager { * @param {number} timeout (单位s) 请求超时时间 默认0 (0表示不超时) * @internal */ - private static _send(method: HttpRequestMethod, url: string, data: any, responseType: HttpResponseType, netEvent?: IHttpEvent, headers?: any[], timeout?: number): HttpRequest { + private static _send(method: HttpRequestMethod, url: string, data: any, responseType: HttpResponseType, netEvent: IHttpEvent, headers?: any[], timeout?: number): HttpRequest { let http = new HttpRequest() http.setNetCallback((result: "succeed" | "fail", response: IHttpResponse) => { switch (result) { case "succeed": - if (netEvent) { - netEvent.onComplete(response); - } else { - GlobalEvent.send(HttpManager.HttpEvent, result, response); - } + netEvent?.onComplete(response); break; case "fail": - if (netEvent) { - netEvent.onError(response); - } else { - GlobalEvent.send(HttpManager.HttpEvent, result, response); - } + netEvent?.onError(response); break; } }); diff --git a/src/quadtree/Box.ts b/src/quadtree/Box.ts deleted file mode 100644 index 01b7fad..0000000 --- a/src/quadtree/Box.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** - * @Author: Gongxh - * @Date: 2024-12-21 - * @Description: 矩形 - */ -import { v2, Vec2 } from "cc"; -import { ShapeType } from "./IShape"; -import { Polygon } from "./Polygon"; - -// 3|2 -// -- -// 0|1 -// 矩形的四个点 - -export class Box extends Polygon { - - public get shapeType(): ShapeType { - return ShapeType.BOX; - } - - constructor(x: number, y: number, width: number, height: number, tag: number = -1) { - let points: Vec2[] = new Array(4); - points[0] = v2(x, y); - points[1] = v2(x + width, y); - points[2] = v2(x + width, y + height); - points[3] = v2(x, y + height); - super(points, tag); - } - - public resetPoints(x: number, y: number, width: number, height: number): void { - let points: Vec2[] = new Array(4); - points[0] = v2(x, y); - points[1] = v2(x + width, y); - points[2] = v2(x + width, y + height); - points[3] = v2(x, y + height); - this.points = points; - } -} \ No newline at end of file diff --git a/src/quadtree/Circle.ts b/src/quadtree/Circle.ts deleted file mode 100644 index 9a65e1d..0000000 --- a/src/quadtree/Circle.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * @Author: Gongxh - * @Date: 2024-12-21 - * @Description: 原型 - */ - -import { Rect } from "cc"; -import { ShapeType } from "./IShape"; -import { Shape } from "./Shape"; - -export class Circle extends Shape { - private _radius: number; // 半径 - - public get shapeType(): ShapeType { - return ShapeType.CIRCLE; - } - - constructor(radius: number, tag: number = -1) { - super(tag); - this._radius = radius; - this._boundingBox.x = -this._radius; - this._boundingBox.y = -this._radius; - this._boundingBox.width = this._radius * 2; - this._boundingBox.height = this._radius * 2; - } - - public getBoundingBox(): Rect { - return this._boundingBox; - } - - public get radius(): number { - return this._radius; - } - - public set radius(value: number) { - this._radius = value; - this._boundingBox.x = -this._radius; - this._boundingBox.y = -this._radius; - this._boundingBox.width = this._radius * 2; - this._boundingBox.height = this._radius * 2; - } -} \ No newline at end of file diff --git a/src/quadtree/IShape.ts b/src/quadtree/IShape.ts deleted file mode 100644 index aea3a13..0000000 --- a/src/quadtree/IShape.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * @Author: Gongxh - * @Date: 2025-05-27 - * @Description: - */ - -import { Rect, Vec2 } from "cc"; - -export enum ShapeType { - CIRCLE = 1, - BOX = 2, - POLYGON = 3, -} - -export interface IShape { - /** 形状类型 */ - get shapeType(): ShapeType; - /** 形状掩码 @internal */ - get mask(): number; - /** 是否有效 @internal */ - - get isValid(): boolean; - get position(): Vec2; - get scale(): number; - get rotation(): number; - /** 获取包围盒 */ - getBoundingBox(): Rect; - setPosition(x: number, y: number): void; - setScale(value: number): void; - setRotation(angle: number): void; - destroy(): void; -} \ No newline at end of file diff --git a/src/quadtree/Polygon.ts b/src/quadtree/Polygon.ts deleted file mode 100644 index d3266d4..0000000 --- a/src/quadtree/Polygon.ts +++ /dev/null @@ -1,92 +0,0 @@ -/** - * @Author: Gongxh - * @Date: 2024-12-21 - * @Description: 多边形 - */ - -import { Rect, v2, Vec2 } from "cc"; -import { ShapeType } from "./IShape"; -import { Shape } from "./Shape"; - - -const vec2 = new Vec2(); -/** 点绕原点旋转 radians 弧度后的新点 */ -function rotate(radians: number, x: number, y: number): Vec2 { - let sin = Math.sin(radians); - let cos = Math.cos(radians); - vec2.x = x * cos - y * sin; - vec2.y = y * cos + x * sin; - return vec2; -} - -// /** 点绕点旋转 radians 弧度后的新点 */ -// export function rotateByPoint(radians: number, x: number, y: number, cx: number, cy: number): Vec2 { -// let sin = Math.sin(radians); -// let cos = Math.cos(radians); -// vec2.x = (x - cx) * cos - (y - cy) * sin + cx; -// vec2.y = (y - cy) * cos + (x - cx) * sin + cy; -// return vec2; -// } - -export class Polygon extends Shape { - protected _points: Vec2[] = []; // 多边形 - protected _realPoints: Vec2[]; - - public get shapeType(): ShapeType { - return ShapeType.POLYGON; - } - - constructor(points: Vec2[], tag: number = -1) { - super(tag); - this._points = points; - this._realPoints = new Array(points.length); - for (let i = 0, len = points.length; i < len; i++) { - this._realPoints[i] = v2(points[i].x, points[i].y); - } - this.getBoundingBox(); - } - - public getBoundingBox(): Rect { - if (this._isDirty) { - let minX = Number.MAX_VALUE; - let maxX = Number.MIN_VALUE; - let minY = Number.MAX_VALUE; - let maxY = Number.MIN_VALUE; - for (const point of this._points) { - let a = rotate(Math.PI / 180 * this._rotation, point.x, point.y); - minX = Math.min(minX, a.x); - minY = Math.min(minY, a.y); - maxX = Math.max(maxX, a.x); - maxY = Math.max(maxY, a.y); - } - this._boundingBox.x = minX; - this._boundingBox.y = minY; - this._boundingBox.width = maxX - minX; - this._boundingBox.height = maxY - minY; - this._isDirty = false; - } - return this._boundingBox; - } - - public get points(): Vec2[] { - let points = this._points; - let len = points.length; - for (let i = 0; i < len; i++) { - let m = points[i]; - this._realPoints[i] = m.rotate(Math.PI / 180 * this.rotation); - let a = this._realPoints[i]; - a.x = a.x * this.scale + this.position.x; - a.y = a.y * this.scale + this.position.y; - } - return this._realPoints; - } - - public set points(pts: Vec2[]) { - this._points = pts; - this._realPoints = new Array(pts.length); - for (let i = 0, len = pts.length; i < len; i++) { - this._realPoints[i] = v2(pts[i].x, pts[i].y); - } - this._isDirty = true; - } -} \ No newline at end of file diff --git a/src/quadtree/QuadTree.ts b/src/quadtree/QuadTree.ts deleted file mode 100644 index 777320c..0000000 --- a/src/quadtree/QuadTree.ts +++ /dev/null @@ -1,372 +0,0 @@ -/** - * @Author: Gongxh - * @Date: 2024-12-21 - * @Description: 树节点 - */ - -import { Graphics, Intersection2D, rect, Rect } from "cc"; -import { Circle } from "./Circle"; -import { IShape, ShapeType } from "./IShape"; -import { Polygon } from "./Polygon"; - -// 1|0 -// --- -// 2|3 -const enum Quadrant { - ONE = 0, - TWO, - THREE, - FOUR, - MORE, // 多个象限 -} - -const circleCircle = Intersection2D.circleCircle; -const polygonCircle = Intersection2D.polygonCircle; -const polygonPolygon = Intersection2D.polygonPolygon; -/** 两个形状是否碰撞 */ -function isCollide(shape1: IShape, shape2: IShape): boolean { - if (shape1.shapeType === ShapeType.CIRCLE) { - if (shape2.shapeType === ShapeType.CIRCLE) { - return circleCircle(shape1.position, (shape1 as Circle).radius * shape1.scale, shape2.position, (shape2 as Circle).radius * shape2.scale); - } else if (shape2.shapeType === ShapeType.BOX || shape2.shapeType === ShapeType.POLYGON) { - return polygonCircle((shape2 as Polygon).points, shape1.position, (shape1 as Circle).radius * shape1.scale); - } - } else if (shape1.shapeType === ShapeType.BOX || shape1.shapeType === ShapeType.POLYGON) { - if (shape2.shapeType === ShapeType.CIRCLE) { - return polygonCircle((shape1 as Polygon).points, shape2.position, (shape2 as Circle).radius * shape2.scale); - } else if (shape2.shapeType === ShapeType.BOX || shape2.shapeType === ShapeType.POLYGON) { - return polygonPolygon((shape2 as Polygon).points, (shape1 as Polygon).points); - } - } - return false; -} - -export const QTConfig = { - /** 每个节点(象限)所能包含物体的最大数量 */ - MAX_SHAPES: 12, - /** 四叉树的最大深度 */ - MAX_LEVELS: 5, -} - -export class QuadTree { - /** @internal */ - private _graphics: Graphics; - /** @internal */ - private _shapes_map: Map; // 根据类型存储形状对象 - /** @internal */ - private _trees: QuadTree[] = []; // 存储四个子节点 - /** @internal */ - private _level: number; // 树的深度 - /** @internal */ - private _bounds: Rect; // 树的外框 - /** @internal */ - private _ignore_shapes: IShape[] = []; // 不在树中的形状 - /** - * 创建一个四叉树 - * @param rect 该节点对应的象限在屏幕上的范围 - * @param level 该节点的深度,根节点的默认深度为0 - * @param graphics cc中用于绘制树的绘制组件 - */ - constructor(rect: Rect, level: number = 0, graphics: Graphics = undefined) { - this._shapes_map = new Map(); - this._trees = []; - this._level = level || 0; - this._bounds = rect; - this._graphics = graphics; - } - - /** - * 插入形状 - * @param shape 形状数据 - * 如果当前节点存在子节点,则检查物体到底属于哪个子节点,如果能匹配到子节点,则将该物体插入到该子节点中 - * 如果当前节点不存在子节点,将该物体存储在当前节点。随后,检查当前节点的存储数量,如果超过了最大存储数量,则对当前节点进行划分,划分完成后,将当前节点存储的物体重新分配到四个子节点中。 - */ - public insert(shape: IShape): void { - // 如果该节点下存在子节点 - if (this._trees.length > 0) { - let quadrant = this._getQuadrant(shape); - if (quadrant !== Quadrant.MORE) { - this._trees[quadrant].insert(shape); - return; - } - } - if (this._level == 0 && !this._isInner(shape, this._bounds)) { - // 插入跟节点并且形状不在根节点的框内,则把形状放入忽略列表中 - this._ignore_shapes.push(shape); - } else { - // 存储在当前节点下 - this._insert(shape); - // 如果当前节点存储的数量超过了 MAX_OBJECTS,并且深度没超过 MAX_LEVELS,则继续拆分 - if (!this._trees.length && this._size() > QTConfig.MAX_SHAPES && this._level < QTConfig.MAX_LEVELS) { - this._split(); - for (const shapes of this._shapes_map.values()) { - let length = shapes.length - 1; - for (let i = length; i >= 0; i--) { - let quadrant = this._getQuadrant(shapes[i]); - if (quadrant !== Quadrant.MORE) { - this._trees[quadrant].insert(shapes.splice(i, 1)[0]); - } - } - } - } - } - } - - /** @internal */ - private _insert(shape: IShape): void { - if (!this._shapes_map.has(shape.mask)) { - this._shapes_map.set(shape.mask, []); - } - this._shapes_map.get(shape.mask).push(shape); - } - - /** - * 检索功能: - * 给出一个物体对象,该函数负责将该物体可能发生碰撞的所有物体选取出来。该函数先查找物体所属的象限,该象限下的物体都是有可能发生碰撞的,然后再递归地查找子象限... - */ - public collide(shape: IShape, tag: number = -1): IShape[] { - let result: IShape[] = []; - if (this._trees.length > 0) { - let quadrant = this._getQuadrant(shape); - if (quadrant === Quadrant.MORE) { - let len = this._trees.length - 1; - for (let i = len; i >= 0; i--) { - result.push(...this._trees[i].collide(shape, tag)); - } - } else { - result.push(...this._trees[quadrant].collide(shape, tag)); - } - } - - for (const key of this._shapes_map.keys()) { - if (!(tag & key)) { - continue; - } - let shapes = this._shapes_map.get(key); - for (const other_shape of shapes) { - if (other_shape.isValid && shape !== other_shape && isCollide(shape, other_shape)) { - result.push(other_shape); - } - } - } - return result; - } - - /** - * 动态更新 - */ - public update(root?: QuadTree): void { - root = root || this; - let isRoot = (root === this); - isRoot && this.graphicsClear(); - this._updateIgnoreShapes(root); - this._updateShapes(root); - // 递归刷新子象限 - for (const tree of this._trees) { - tree.update(root); - } - this._removeChildTree(); - this.graphicsTreeBound(root); - - if (isRoot && this._graphics) { - this._graphics.stroke(); - } - } - - public clear(): void { - this._level = 0; - this._ignore_shapes.length = 0; - this._shapes_map.clear(); - for (const tree of this._trees) { - tree.clear(); - } - this._trees.length = 0; - } - - /** 当前形状是否包含在象限内 @internal */ - private _isInner(shape: IShape, bounds: Rect): boolean { - let rect = shape.getBoundingBox(); - return ( - rect.xMin * shape.scale + shape.position.x > bounds.xMin && - rect.xMax * shape.scale + shape.position.x < bounds.xMax && - rect.yMin * shape.scale + shape.position.y > bounds.yMin && - rect.yMax * shape.scale + shape.position.y < bounds.yMax - ); - } - - /** - * 获取形状对应的象限序号,以中心为界限切割: - * @param {Shape} shape 形状 - * 右上:象限一 - * 左上:象限二 - * 左下:象限三 - * 右下:象限四 - * @internal - */ - private _getQuadrant(shape: IShape): Quadrant { - let bounds = this._bounds; - let rect = shape.getBoundingBox(); - let center = bounds.center; - - let onTop = rect.yMin * shape.scale + shape.position.y > center.y; - let onBottom = rect.yMax * shape.scale + shape.position.y < center.y; - let onLeft = rect.xMax * shape.scale + shape.position.x < center.x; - let onRight = rect.xMin * shape.scale + shape.position.x > center.x; - if (onTop) { - if (onRight) { - return Quadrant.ONE; - } else if (onLeft) { - return Quadrant.TWO; - } - } else if (onBottom) { - if (onLeft) { - return Quadrant.THREE; - } else if (onRight) { - return Quadrant.FOUR; - } - } - return Quadrant.MORE; // 跨越多个象限 - } - - /** - * 划分函数 - * 如果某一个象限(节点)内存储的物体数量超过了MAX_OBJECTS最大数量 - * 则需要对这个节点进行划分 - * 它的工作就是将一个象限看作一个屏幕,将其划分为四个子象限 - * @internal - */ - private _split(): void { - let bounds = this._bounds; - let x = bounds.x; - let y = bounds.y; - let halfwidth = bounds.width * 0.5; - let halfheight = bounds.height * 0.5; - let nextLevel = this._level + 1; - this._trees.push( - new QuadTree(rect(bounds.center.x, bounds.center.y, halfwidth, halfheight), nextLevel, this._graphics), - new QuadTree(rect(x, bounds.center.y, halfwidth, halfheight), nextLevel, this._graphics), - new QuadTree(rect(x, y, halfwidth, halfheight), nextLevel, this._graphics), - new QuadTree(rect(bounds.center.x, y, halfwidth, halfheight), nextLevel, this._graphics) - ); - } - - /** 删除子树 @internal */ - private _removeChildTree(): void { - if (this._trees.length > 0) { - if (this._totalSize() <= 0) { - this._trees.length = 0; - } - } - } - - /** 更新忽略掉的形状 @internal */ - private _updateIgnoreShapes(root: QuadTree): void { - let shapes = this._ignore_shapes; - let lastIndex = shapes.length - 1; - let index = 0; - while (index < lastIndex) { - let shape = shapes[index]; - if (!shape.isValid) { - if (index !== lastIndex) { - shapes[index] = shapes[lastIndex]; - } - shapes.pop(); - lastIndex--; - continue; - } - if (this._isInner(shape, this._bounds)) { - if (index !== lastIndex) { - [shapes[index], shapes[lastIndex]] = [shapes[lastIndex], shapes[index]]; - } - root.insert(shapes.pop()); - } else { - index++; - } - } - } - - /** 更新有效的形状 @internal */ - private _updateShapes(root: QuadTree): void { - for (const shapes of this._shapes_map.values()) { - let lastIndex = shapes.length - 1; - let index = 0; - while (index <= lastIndex) { - let shape = shapes[index]; - if (!shape.isValid) { - if (index !== lastIndex) { - shapes[index] = shapes[lastIndex]; - } - shapes.pop(); - lastIndex--; - continue; - } - if (!this._isInner(shape, this._bounds)) { - // 如果矩形不属于该象限,则将该矩形重新插入根节点 - if (index !== lastIndex) { - [shapes[index], shapes[lastIndex]] = [shapes[lastIndex], shapes[index]]; - } - root.insert(shapes.pop()); - lastIndex--; - } else if (this._trees.length > 0) { - // 如果矩形属于该象限且该象限具有子象限,则将该矩形安插到子象限中 - let quadrant = this._getQuadrant(shape); - if (quadrant !== Quadrant.MORE) { - if (index !== lastIndex) { - [shapes[index], shapes[lastIndex]] = [shapes[lastIndex], shapes[index]]; - } - this._trees[quadrant].insert(shapes.pop()); - lastIndex--; - } else { - index++; - } - } else { - index++; - } - } - } - } - - /** 当前树以及子树中所有的形状数量 @internal */ - private _totalSize(): number { - let size = this._size(); - for (const tree of this._trees) { - size += tree._totalSize(); - } - return size; - } - - /** 当前树中所有的形状数量 @internal */ - private _size(): number { - let size = 0; - for (const shapes of this._shapes_map.values()) { - size += shapes.length; - } - return size + this._ignore_shapes.length; - } - - /** 画出当前树的边界 @internal */ - private graphicsTreeBound(root: QuadTree): void { - if (!this._graphics) { - return; - } - if (this._trees.length > 0) { - this._graphics.moveTo(this._bounds.x, this._bounds.center.y); - this._graphics.lineTo(this._bounds.x + this._bounds.width, this._bounds.center.y); - - this._graphics.moveTo(this._bounds.center.x, this._bounds.y); - this._graphics.lineTo(this._bounds.center.x, this._bounds.y + this._bounds.height); - } - if (this == root) { - this._graphics.moveTo(this._bounds.xMin, this._bounds.yMin); - this._graphics.lineTo(this._bounds.xMax, this._bounds.yMin); - this._graphics.lineTo(this._bounds.xMax, this._bounds.yMax); - this._graphics.lineTo(this._bounds.xMin, this._bounds.yMax); - this._graphics.lineTo(this._bounds.xMin, this._bounds.yMin); - } - } - - /** 清除绘制 @internal */ - private graphicsClear(): void { - this._graphics && this._graphics.clear(); - } -} \ No newline at end of file diff --git a/src/quadtree/Shape.ts b/src/quadtree/Shape.ts deleted file mode 100644 index 94d9864..0000000 --- a/src/quadtree/Shape.ts +++ /dev/null @@ -1,75 +0,0 @@ -/** - * @Author: Gongxh - * @Date: 2024-12-21 - * @Description: 四叉树的 形状基类 - */ - -import { Rect, Vec2 } from "cc"; -import { IShape, ShapeType } from "./IShape"; - -export abstract class Shape implements IShape { - /** - * 形状的掩码 用来过滤不需要检测的形状 通过&来匹配形状是否需要被检测 -1表示和所有物体碰撞 - */ - private _mask: number = -1; - protected _scale: number; - - /** 脏标记 用来重置包围盒 @internal */ - protected _isDirty: boolean; - - /** 包围盒 @internal */ - protected _boundingBox: Rect; - - /** 位置 @internal */ - protected _position: Vec2; - - /** 旋转角度 @internal */ - protected _rotation: number; - - /** 是否有效 下次更新时删除 @internal */ - private _valid: boolean = true; - - public abstract get shapeType(): ShapeType; - - public get mask(): number { return this._mask; } - public get position(): Vec2 { return this._position; } - public get scale(): number { return this._scale; } - public get rotation(): number { return this._rotation; } - public get isValid(): boolean { return this._valid; } - - constructor(mask: number) { - this._mask = mask; - this._scale = 1.0; - this._rotation = 0; - this._isDirty = true; - this._boundingBox = new Rect(); - this._position = new Vec2(); - } - - public setPosition(x: number, y: number) { - this._position.x = x; - this._position.y = y; - } - - public setRotation(angle: number) { - if (this._rotation !== angle) { - this._rotation = angle; - this._isDirty = true; - } - } - - public setScale(value: number) { - if (this._scale !== value) { - this._scale = value; - this._isDirty = true; - } - } - - /** 包围盒 子类重写 */ - public abstract getBoundingBox(): Rect; - - - public destroy(): void { - this._valid = false; - } -} \ No newline at end of file