mirror of
https://github.com/kirikayakazuto/CocosCreator_ECS
synced 2025-10-30 19:05:52 +00:00
ECS Demo
This commit is contained in:
13
assets/Script/ECS/components.meta
Executable file
13
assets/Script/ECS/components.meta
Executable file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "1.1.3",
|
||||
"uuid": "3878b852-fe53-405d-a5bf-3978aedd9ce7",
|
||||
"importer": "folder",
|
||||
"isBundle": false,
|
||||
"bundleName": "",
|
||||
"priority": 1,
|
||||
"compressionType": {},
|
||||
"optimizeHotUpdate": {},
|
||||
"inlineSpriteFrames": {},
|
||||
"isRemoteBundle": {},
|
||||
"subMetas": {}
|
||||
}
|
||||
15
assets/Script/ECS/components/ComAttackable.ts
Normal file
15
assets/Script/ECS/components/ComAttackable.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { ComType, EntityIndex } from "../lib/Const";
|
||||
import { ECSComponent } from "../lib/ECSComponent";
|
||||
|
||||
@ECSComponent(ComType.ComAttackable)
|
||||
export class ComAttackable {
|
||||
public duration: number; // 攻击持续时间
|
||||
public countDown: number; // 攻击剩余时间
|
||||
public hurtFrame: number; // 攻击帧
|
||||
public mustAttackFrame: number;
|
||||
public hurted: boolean;
|
||||
public dirty: boolean; //
|
||||
public attack: number; // 攻击力
|
||||
|
||||
public hurtArea: cc.Vec2; // 攻击区域
|
||||
}
|
||||
10
assets/Script/ECS/components/ComAttackable.ts.meta
Normal file
10
assets/Script/ECS/components/ComAttackable.ts.meta
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"uuid": "7ee555b4-eed0-4207-81f5-41b51bb3bd73",
|
||||
"importer": "typescript",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
7
assets/Script/ECS/components/ComBeAttacked.ts
Normal file
7
assets/Script/ECS/components/ComBeAttacked.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { ComType } from "../lib/Const";
|
||||
import { ECSComponent } from "../lib/ECSComponent";
|
||||
|
||||
@ECSComponent(ComType.ComBeAttacked)
|
||||
export class ComBeAttacked {
|
||||
|
||||
}
|
||||
10
assets/Script/ECS/components/ComBeAttacked.ts.meta
Normal file
10
assets/Script/ECS/components/ComBeAttacked.ts.meta
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"uuid": "c3d913ea-e619-4f5e-bb18-3fe947505dda",
|
||||
"importer": "typescript",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
9
assets/Script/ECS/components/ComBehaviorTree.ts
Normal file
9
assets/Script/ECS/components/ComBehaviorTree.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { BT } from "../../Common/BehaviorTree";
|
||||
import { ComType } from "../lib/Const";
|
||||
import { ECSComponent } from "../lib/ECSComponent";
|
||||
|
||||
@ECSComponent(ComType.ComBehaviorTree)
|
||||
export class ComBehaviorTree {
|
||||
public root: BT.NodeBase = null;
|
||||
public bb: BT.BlackBoard = new BT.BlackBoard();
|
||||
}
|
||||
10
assets/Script/ECS/components/ComBehaviorTree.ts.meta
Normal file
10
assets/Script/ECS/components/ComBehaviorTree.ts.meta
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"uuid": "a529fff3-0126-4e53-a4c7-e5ddb6d76976",
|
||||
"importer": "typescript",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
10
assets/Script/ECS/components/ComCocosNode.ts
Normal file
10
assets/Script/ECS/components/ComCocosNode.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { EventBase } from "../../Struct/NodeEvent";
|
||||
import { ComType } from "../lib/Const";
|
||||
import { ECSComponent } from "../lib/ECSComponent";
|
||||
|
||||
@ECSComponent(ComType.ComCocosNode)
|
||||
export class ComCocosNode {
|
||||
public node: cc.Node = null;
|
||||
public loaded = false;
|
||||
public events: EventBase[] = [];
|
||||
}
|
||||
10
assets/Script/ECS/components/ComCocosNode.ts.meta
Normal file
10
assets/Script/ECS/components/ComCocosNode.ts.meta
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"uuid": "e75ab1ce-3260-45cb-975a-9c4c1ab72f94",
|
||||
"importer": "typescript",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
10
assets/Script/ECS/components/ComMonitor.ts
Normal file
10
assets/Script/ECS/components/ComMonitor.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { ComType, EntityIndex } from "../lib/Const";
|
||||
import { ECSComponent } from "../lib/ECSComponent";
|
||||
|
||||
@ECSComponent(ComType.ComMonitor)
|
||||
export class ComMonitor {
|
||||
public lookLen = 0;
|
||||
public lookSize = 0;
|
||||
public aroundLen = 0;
|
||||
public others: EntityIndex[] = [];
|
||||
}
|
||||
10
assets/Script/ECS/components/ComMonitor.ts.meta
Normal file
10
assets/Script/ECS/components/ComMonitor.ts.meta
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"uuid": "274fabed-397f-4950-b691-2c6b6b894c9f",
|
||||
"importer": "typescript",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
12
assets/Script/ECS/components/ComMovable.ts
Executable file
12
assets/Script/ECS/components/ComMovable.ts
Executable file
@@ -0,0 +1,12 @@
|
||||
import { ComType } from "../lib/Const";
|
||||
import { ECSComponent } from "../lib/ECSComponent";
|
||||
|
||||
@ECSComponent(ComType.ComMovable)
|
||||
export class ComMovable {
|
||||
public running = false;
|
||||
public speed = 0;
|
||||
public points: cc.Vec2[] = [];
|
||||
public pointIdx = 0;
|
||||
|
||||
public speedDirty = false;
|
||||
}
|
||||
10
assets/Script/ECS/components/ComMovable.ts.meta
Executable file
10
assets/Script/ECS/components/ComMovable.ts.meta
Executable file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"uuid": "773fd91d-89d6-499e-9488-3d85f11938a1",
|
||||
"importer": "typescript",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
9
assets/Script/ECS/components/ComNodeConfig.ts
Normal file
9
assets/Script/ECS/components/ComNodeConfig.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { ComType } from "../lib/Const";
|
||||
import { ECSComponent } from "../lib/ECSComponent";
|
||||
|
||||
@ECSComponent(ComType.ComNodeConfig)
|
||||
export class ComNodeConfig {
|
||||
id = 0; // 唯一标识
|
||||
prefabUrl = ''
|
||||
layer = 0; // 层级
|
||||
}
|
||||
10
assets/Script/ECS/components/ComNodeConfig.ts.meta
Normal file
10
assets/Script/ECS/components/ComNodeConfig.ts.meta
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"uuid": "ef2ff8f3-8ffa-49f6-8e55-b8ba0284a095",
|
||||
"importer": "typescript",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
16
assets/Script/ECS/components/ComRoleConfig.ts
Normal file
16
assets/Script/ECS/components/ComRoleConfig.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { ComType } from "../lib/Const";
|
||||
import { ECSComponent } from "../lib/ECSComponent";
|
||||
|
||||
|
||||
@ECSComponent(ComType.ComRoleConfig)
|
||||
export class ComRoleConfig {
|
||||
public team: number;
|
||||
public maxHP: number;
|
||||
public lastHP: number;
|
||||
public nowHP: number;
|
||||
public HPDirty: boolean;
|
||||
|
||||
public attack: number;
|
||||
|
||||
public moveSpeed: number;
|
||||
}
|
||||
10
assets/Script/ECS/components/ComRoleConfig.ts.meta
Normal file
10
assets/Script/ECS/components/ComRoleConfig.ts.meta
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"uuid": "a0036aea-32a4-4b9b-a587-5262ea4cedf7",
|
||||
"importer": "typescript",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
11
assets/Script/ECS/components/ComTransform.ts
Normal file
11
assets/Script/ECS/components/ComTransform.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { ComType } from "../lib/Const";
|
||||
import { ECSComponent } from "../lib/ECSComponent";
|
||||
|
||||
@ECSComponent(ComType.ComTransform)
|
||||
export class ComTransform {
|
||||
public dir = cc.v2(1, 0); //方向向量
|
||||
public x = 0;
|
||||
public y = 0;
|
||||
public width = 0;
|
||||
public height = 0;
|
||||
}
|
||||
10
assets/Script/ECS/components/ComTransform.ts.meta
Normal file
10
assets/Script/ECS/components/ComTransform.ts.meta
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"uuid": "0bb2991a-84c5-4b22-ade6-59d2e7e97252",
|
||||
"importer": "typescript",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
13
assets/Script/ECS/lib.meta
Executable file
13
assets/Script/ECS/lib.meta
Executable file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "1.1.3",
|
||||
"uuid": "fa6ac7d5-2bad-42ee-90ba-44147985428d",
|
||||
"importer": "folder",
|
||||
"isBundle": false,
|
||||
"bundleName": "",
|
||||
"priority": 1,
|
||||
"compressionType": {},
|
||||
"optimizeHotUpdate": {},
|
||||
"inlineSpriteFrames": {},
|
||||
"isRemoteBundle": {},
|
||||
"subMetas": {}
|
||||
}
|
||||
16
assets/Script/ECS/lib/Const.ts
Normal file
16
assets/Script/ECS/lib/Const.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
export type EntityIndex = number;
|
||||
|
||||
export type ComPoolIndex = number;
|
||||
|
||||
export enum ComType {
|
||||
ComCocosNode = 0,
|
||||
ComMovable = 1,
|
||||
ComNodeConfig = 2,
|
||||
ComBehaviorTree = 3,
|
||||
ComTransform = 4,
|
||||
ComMonitor = 5,
|
||||
ComRoleConfig = 6,
|
||||
ComAttackable = 7,
|
||||
ComBeAttacked = 8
|
||||
}
|
||||
10
assets/Script/ECS/lib/Const.ts.meta
Executable file
10
assets/Script/ECS/lib/Const.ts.meta
Executable file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"uuid": "ce38ec2f-50f5-4f7a-ad24-48a576032322",
|
||||
"importer": "typescript",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
36
assets/Script/ECS/lib/ECSComponent.ts
Executable file
36
assets/Script/ECS/lib/ECSComponent.ts
Executable file
@@ -0,0 +1,36 @@
|
||||
import { ComType, EntityIndex } from "./Const";
|
||||
|
||||
/** 构造函数 */
|
||||
export interface ECSComConstructor extends Function {
|
||||
new(): any;
|
||||
}
|
||||
|
||||
export interface ECSTypedComConstructor<T> extends ECSComConstructor {
|
||||
new():T;
|
||||
}
|
||||
|
||||
/** 通过type存取 构造函数 */
|
||||
const ComConsMap: {[key: number]: ECSComConstructor} = cc.js.createMap();
|
||||
function RegistComConstructor(comType: ComType, func: ECSComConstructor) {
|
||||
ComConsMap[comType] = func;
|
||||
}
|
||||
export function GetComConstructor(comType: ComType) {
|
||||
return ComConsMap[comType];
|
||||
}
|
||||
|
||||
/** 通过构造函数存取 type */
|
||||
function SetComConstructorType(comCons: ECSComConstructor, type: ComType) {
|
||||
comCons['__type__'] = type;
|
||||
}
|
||||
export function GetComConstructorType<T>(comCons: {prototype: T}): ComType {
|
||||
return comCons['__type__'];
|
||||
}
|
||||
|
||||
/** ECSComponent */
|
||||
export function ECSComponent(type: ComType) {
|
||||
return function(func: ECSComConstructor) {
|
||||
SetComConstructorType(func, type);
|
||||
RegistComConstructor(type, func);
|
||||
};
|
||||
}
|
||||
|
||||
10
assets/Script/ECS/lib/ECSComponent.ts.meta
Executable file
10
assets/Script/ECS/lib/ECSComponent.ts.meta
Executable file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"uuid": "58b062eb-ce1e-49dd-8520-15713b92a51f",
|
||||
"importer": "typescript",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
35
assets/Script/ECS/lib/ECSComponentPool.ts
Executable file
35
assets/Script/ECS/lib/ECSComponentPool.ts
Executable file
@@ -0,0 +1,35 @@
|
||||
import { ComPoolIndex } from "./Const";
|
||||
import { ECSTypedComConstructor } from "./ECSComponent";
|
||||
|
||||
/**
|
||||
* 组件池
|
||||
*/
|
||||
export class ECSComponentPool<T> {
|
||||
private _componentConstructor: ECSTypedComConstructor<T>;
|
||||
public constructor(comCons: ECSTypedComConstructor<T>) {
|
||||
this._componentConstructor = comCons;
|
||||
}
|
||||
|
||||
private _components: T[] = []; // components
|
||||
private _reservedIdxs: ComPoolIndex[] = []; // 缓存的component idx
|
||||
|
||||
|
||||
public get(idx: ComPoolIndex): T {
|
||||
return this._components[idx];
|
||||
}
|
||||
|
||||
public alloc(): ComPoolIndex {
|
||||
if(this._reservedIdxs.length > 0) {
|
||||
let ret = this._reservedIdxs.pop();
|
||||
this._componentConstructor.apply(this._components[ret]); // 重置对象
|
||||
return ret;
|
||||
}
|
||||
let newInstance = new this._componentConstructor();
|
||||
this._components.push(newInstance);
|
||||
return this._components.length - 1;
|
||||
}
|
||||
|
||||
public free(idx: ComPoolIndex) {
|
||||
this._reservedIdxs.push(idx);
|
||||
}
|
||||
}
|
||||
10
assets/Script/ECS/lib/ECSComponentPool.ts.meta
Executable file
10
assets/Script/ECS/lib/ECSComponentPool.ts.meta
Executable file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"uuid": "a40c60fb-7834-4de4-b1e9-6a346dd68e60",
|
||||
"importer": "typescript",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
67
assets/Script/ECS/lib/ECSEntity.ts
Executable file
67
assets/Script/ECS/lib/ECSEntity.ts
Executable file
@@ -0,0 +1,67 @@
|
||||
import {ECSWorld} from "./ECSWorld"
|
||||
import {ECSComConstructor, GetComConstructorType } from "./ECSComponent";
|
||||
import { ComPoolIndex, ComType } from "./Const";
|
||||
import { ECSComponentPool } from "./ECSComponentPool";
|
||||
|
||||
/** 实体 */
|
||||
export class Entity {
|
||||
public id: number; // 唯一标识
|
||||
public index: number; //
|
||||
public dead: boolean; //
|
||||
|
||||
// 实体上的组件, 存放的是ComponentPool的index
|
||||
private _components: Array<ComPoolIndex> = new Array<ComPoolIndex>(Object.keys(ComType).length/2).fill(-1);
|
||||
|
||||
private _world: ECSWorld = null;
|
||||
public get world(): ECSWorld {
|
||||
return this._world;
|
||||
}
|
||||
public set world(world: ECSWorld) {
|
||||
this._world = world;
|
||||
}
|
||||
|
||||
/** 获取实体上的组件 */
|
||||
public getComponent<T>(typeOrFunc: ComType | {prototype: T}): ComPoolIndex {
|
||||
let type = typeof typeOrFunc == 'number' ? typeOrFunc : GetComConstructorType(typeOrFunc);
|
||||
let comPoolIdx = this._components[type];
|
||||
if(comPoolIdx == -1) return -1;
|
||||
return comPoolIdx;
|
||||
}
|
||||
|
||||
/** 添加组件 */
|
||||
public addComponent<T>(func: {prototype: T}): ComPoolIndex {
|
||||
let type = GetComConstructorType(func);
|
||||
if(this._components[type] !== -1) {
|
||||
return this._components[type];
|
||||
}
|
||||
let comPoolIdx = this._components[type] = this._world.getComponentPool(func).alloc();
|
||||
this._world.setEntityDirty(this);
|
||||
return comPoolIdx;
|
||||
}
|
||||
|
||||
/** 移除组件 */
|
||||
public removeComponent<T extends ECSComConstructor>(func: ECSComConstructor, dirty = true) {
|
||||
let comPoolIdx = this._components[GetComConstructorType(func)];
|
||||
if(comPoolIdx == -1) {
|
||||
console.error(`[ECSEntity]: removeComponent error, type: ${GetComConstructorType(func)}`);
|
||||
return false;
|
||||
}
|
||||
this._components[GetComConstructorType(func)] = -1;
|
||||
this._world.getComponentPool<ECSComponentPool<T>>(func).free(comPoolIdx);
|
||||
dirty && this._world.setEntityDirty(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** 移除所有组件 */
|
||||
public removeAllComponents(dirty: boolean) {
|
||||
for(let type = 0; type < this._components.length; type++) {
|
||||
let comPoolIdx = this._components[type];
|
||||
if(comPoolIdx == -1) continue;
|
||||
this._world.getComponentPool<ECSComponentPool<any>>(type).free(comPoolIdx);
|
||||
}
|
||||
this._components.fill(-1);
|
||||
if(dirty) {
|
||||
this._world.setEntityDirty(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
10
assets/Script/ECS/lib/ECSEntity.ts.meta
Executable file
10
assets/Script/ECS/lib/ECSEntity.ts.meta
Executable file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"uuid": "f6eafcbc-0cee-4a3d-90a7-d1d89d934869",
|
||||
"importer": "typescript",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
67
assets/Script/ECS/lib/ECSFillter.ts
Executable file
67
assets/Script/ECS/lib/ECSFillter.ts
Executable file
@@ -0,0 +1,67 @@
|
||||
import { ComType, EntityIndex } from "./Const";
|
||||
import { Entity } from "./ECSEntity";
|
||||
import { ECSWorld } from "./ECSWorld";
|
||||
|
||||
export class ECSFillter {
|
||||
private _world: ECSWorld = null;
|
||||
|
||||
private _entitiesMap = new Map<EntityIndex, boolean>();
|
||||
|
||||
private _acceptComTypes: ComType[] = []; // 接收的组件类型
|
||||
private _rejectComTypes: ComType[] = []; // 拒绝的组件类型
|
||||
|
||||
public constructor(world: ECSWorld, accepts?: ComType[], rejects?: ComType[]) {
|
||||
this._world = world;
|
||||
this._acceptComTypes = accepts && accepts.length > 0 ? accepts : this._acceptComTypes;
|
||||
this._rejectComTypes = rejects && rejects.length > 0 ? rejects : this._rejectComTypes;
|
||||
}
|
||||
|
||||
public get entities() {
|
||||
return this._entitiesMap;
|
||||
}
|
||||
|
||||
public onEntityEnter(entity: EntityIndex) {
|
||||
if(this._entitiesMap.has(entity)) {
|
||||
console.warn(`[ECSFillter]: addEntity entity is had ${entity}`);
|
||||
return true;
|
||||
}
|
||||
this._entitiesMap.set(entity, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
public onEntityLeave(entity: EntityIndex) {
|
||||
if(!this._entitiesMap.has(entity)) {
|
||||
console.warn(`[ECSFillter]: removeEntity entity not had ${entity}`);
|
||||
return true;
|
||||
}
|
||||
this._entitiesMap.delete(entity);
|
||||
}
|
||||
|
||||
public walk(callback?: (entity: number) => boolean) {
|
||||
this._entitiesMap.forEach((value, entity) => {
|
||||
callback(entity);
|
||||
});
|
||||
}
|
||||
|
||||
public isAccept(entity: Entity) {
|
||||
for(let i = 0; i < this._acceptComTypes.length; i++) {
|
||||
if(entity.getComponent(this._acceptComTypes[i]) == -1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for(let i = 0; i < this._rejectComTypes.length; i++) {
|
||||
if(entity.getComponent(this._rejectComTypes[i]) !== -1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public isContains(entity: number) {
|
||||
return this._entitiesMap.has(entity);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
10
assets/Script/ECS/lib/ECSFillter.ts.meta
Executable file
10
assets/Script/ECS/lib/ECSFillter.ts.meta
Executable file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"uuid": "a2265ecd-3751-461b-9d2f-755ccc56c3da",
|
||||
"importer": "typescript",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
14
assets/Script/ECS/lib/ECSSystem.ts
Executable file
14
assets/Script/ECS/lib/ECSSystem.ts
Executable file
@@ -0,0 +1,14 @@
|
||||
import { ECSWorld } from "./ECSWorld";
|
||||
|
||||
export abstract class ECSSystem {
|
||||
/** 连接 */
|
||||
public abstract onAdd(world: ECSWorld): void;
|
||||
/** 断开连接 */
|
||||
public abstract onRemove(world: ECSWorld): void;
|
||||
/** 添加实体 */
|
||||
public abstract onEntityEnter(world: ECSWorld, entity: number): void;
|
||||
/** */
|
||||
public abstract onEntityLeave(world: ECSWorld, entity: number): void;
|
||||
/** 更新 */
|
||||
public abstract onUpdate(world: ECSWorld, dt: number): void;
|
||||
}
|
||||
10
assets/Script/ECS/lib/ECSSystem.ts.meta
Executable file
10
assets/Script/ECS/lib/ECSSystem.ts.meta
Executable file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"uuid": "925b6e67-1234-4933-a867-339843b9a663",
|
||||
"importer": "typescript",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
203
assets/Script/ECS/lib/ECSWorld.ts
Executable file
203
assets/Script/ECS/lib/ECSWorld.ts
Executable file
@@ -0,0 +1,203 @@
|
||||
import { Entity } from "./ECSEntity"
|
||||
import { ECSFillter } from "./ECSFillter"
|
||||
import { ECSComConstructor, GetComConstructor as GetComConstructor, GetComConstructorType } from "./ECSComponent";
|
||||
import { ECSSystem } from "./ECSSystem";
|
||||
import { ComPoolIndex, ComType, EntityIndex } from "./Const";
|
||||
import { ECSComponentPool } from "./ECSComponentPool";
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export class ECSWorld {
|
||||
private _systems: ECSSystem[] = []; // world内所有的system
|
||||
private _entities: Entity[] = []; // world内所有的entity
|
||||
private _reservedIds: number[] = []; // 缓存
|
||||
|
||||
private _componentPools: ECSComponentPool<any>[] = [];
|
||||
private _fillters = new Map<string, ECSFillter>();
|
||||
|
||||
private _entitiesToDelete: number[] = [];
|
||||
private _entityIdSeed: number = 0;
|
||||
|
||||
/** 获取ComponentPool */
|
||||
public getComponentPool<T>(typeOrFunc: ComType | {prototype: T}): ECSComponentPool<T> {
|
||||
let type = typeof typeOrFunc == "number" ? typeOrFunc : GetComConstructorType(typeOrFunc);
|
||||
if(!this._componentPools[type]) {
|
||||
this._componentPools[type] = new ECSComponentPool<T>(GetComConstructor(type));
|
||||
}
|
||||
return this._componentPools[type] as any;
|
||||
}
|
||||
|
||||
/** 添加system */
|
||||
public addSystem(system: ECSSystem) {
|
||||
this._systems.push(system);
|
||||
system.onAdd(this);
|
||||
for(let i = 0; i < this._entities.length; i++) {
|
||||
if(this._entities[i].id !== -1) {
|
||||
system.onEntityEnter(this, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 移除system */
|
||||
public removeSystem(system: ECSSystem) {
|
||||
system.onRemove(this);
|
||||
for(let i = 0; i < this._entities.length; i++) {
|
||||
if(this._entities[i].id !== -1) {
|
||||
system.onEntityLeave(this, i);
|
||||
}
|
||||
}
|
||||
for(let i = this._systems.length - 1; i >= 0; i--) {
|
||||
if(this._systems[i] == system) {
|
||||
this._systems.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 创建实体 */
|
||||
public createEntity(): number {
|
||||
let entity: Entity = null;
|
||||
let index = -1;
|
||||
if(this._reservedIds.length > 0) {
|
||||
index = this._reservedIds.pop();
|
||||
entity = this._entities[index];
|
||||
}else {
|
||||
entity = new Entity();
|
||||
index = this._entities.length;
|
||||
this._entities.push(entity);
|
||||
}
|
||||
entity.id = this._entityIdSeed++;
|
||||
entity.world = this;
|
||||
entity.index = index;
|
||||
entity.dead = false;
|
||||
for(let system of this._systems) {
|
||||
system.onEntityEnter(this, entity.index);
|
||||
}
|
||||
return entity.index;
|
||||
}
|
||||
|
||||
/** 移除实体 */
|
||||
public removeEntity(entity: EntityIndex): boolean {
|
||||
if(entity <= 0) return false;
|
||||
if(!this._entities[entity] || this._entities[entity].dead) {
|
||||
console.warn(`[ECSWorld] removeEntity entity is removed`);
|
||||
return false;
|
||||
}
|
||||
this._entities[entity].dead = true;
|
||||
this._entitiesToDelete.push(entity);
|
||||
|
||||
this._fillters.forEach((fillter, key) => {
|
||||
fillter.isContains(entity) && fillter.onEntityLeave(entity);
|
||||
});
|
||||
for(let system of this._systems) {
|
||||
system.onEntityLeave(this, entity);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public getComponent<T>(entity: EntityIndex, com: {prototype: T}) {
|
||||
if(!this._entities[entity]) return null;
|
||||
let comPoolIdx = this._entities[entity].getComponent(com);
|
||||
return this.getComponentPool<T>(com).get(comPoolIdx);
|
||||
}
|
||||
|
||||
public removeComponent(entity: EntityIndex, com: ECSComConstructor) {
|
||||
if(!this._entities[entity]) return ;
|
||||
this._entities[entity].removeComponent(com);
|
||||
}
|
||||
|
||||
public addComponent<T>(entity: EntityIndex, com: {prototype: T}) {
|
||||
if(!this._entities[entity]) return null;
|
||||
let comPoolIdx = this._entities[entity].addComponent(com);
|
||||
return this.getComponentPool<T>(com).get(comPoolIdx)
|
||||
}
|
||||
|
||||
public getSingletonComponent<T>(com: {prototype: T}): T {
|
||||
let entity = this._entities[0];
|
||||
let comPoolIdx = entity.getComponent(<ECSComConstructor>com);
|
||||
let pool = this.getComponentPool<T>(com);
|
||||
if(comPoolIdx >= 0) return pool.get(comPoolIdx);
|
||||
return pool.get(entity.addComponent(com));
|
||||
}
|
||||
|
||||
public setEntityDirty(entity: Entity): void {
|
||||
this._fillters.forEach((fillter, key) => {
|
||||
let accept = !entity.dead && fillter.isAccept(entity);
|
||||
if(accept != fillter.isContains(entity.index)) {
|
||||
accept ? fillter.onEntityEnter(entity.index) : fillter.onEntityLeave(entity.index);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public getEntityId(entity: EntityIndex) : number {
|
||||
return this._entities[entity].id;
|
||||
}
|
||||
|
||||
public getFilter(fillterKey: string): ECSFillter {
|
||||
if(this._fillters.has(fillterKey)) {
|
||||
return this._fillters.get(fillterKey);
|
||||
}
|
||||
let [acceptStr, rejectStr] = fillterKey.split("-");
|
||||
let accept = acceptStr && acceptStr.length > 0 ? acceptStr.split(',').map(Number) : null;
|
||||
let reject = rejectStr && rejectStr.length > 0 ? rejectStr.split(',').map(Number) : null;
|
||||
let fillter = new ECSFillter(this, accept, reject);
|
||||
this._fillters.set(fillterKey, fillter);
|
||||
// 将当期的entity放入fillter
|
||||
for(let i=1; i<this._entities.length; i++) {
|
||||
const entity = this._entities[i];
|
||||
if(fillter.isAccept(entity)) {
|
||||
fillter.onEntityEnter(entity.index);
|
||||
}
|
||||
}
|
||||
return fillter;
|
||||
}
|
||||
|
||||
public update(dt:number) {
|
||||
for(let system of this._systems) {
|
||||
system.onUpdate(this, dt);
|
||||
}
|
||||
if(this._entitiesToDelete.length > 0) {
|
||||
this._realRemoveEntity();
|
||||
}
|
||||
}
|
||||
|
||||
private _realRemoveEntity() {
|
||||
for(let entityIdx of this._entitiesToDelete) {
|
||||
this._entities[entityIdx].removeAllComponents(false);
|
||||
this._entities[entityIdx].id = -1;
|
||||
this._reservedIds.push(entityIdx);
|
||||
}
|
||||
this._entitiesToDelete.length = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export function GenFillterKey(accepts: ECSComConstructor[], rejects?: ECSComConstructor[]) {
|
||||
let acceptTypes: ComType[] = [];
|
||||
let rejectTypes: ComType[] = [];
|
||||
|
||||
if(accepts && accepts.length > 0) {
|
||||
for(let i = 0; i < accepts.length; i++) {
|
||||
acceptTypes[i] = GetComConstructorType(accepts[i]);
|
||||
}
|
||||
}
|
||||
if(rejects && rejects.length > 0) {
|
||||
for(let i = 0; i < rejects.length; i++) {
|
||||
rejectTypes[i] = GetComConstructorType(rejects[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if(acceptTypes.length < 0) {
|
||||
console.error(`[ECSWorld]: GenFillterKey 必须要有accpters`);
|
||||
return "";
|
||||
}
|
||||
|
||||
acceptTypes.sort();
|
||||
rejectTypes.sort();
|
||||
|
||||
let key = Array.prototype.join.call(acceptTypes, ",");
|
||||
if(!rejectTypes || rejectTypes.length <= 0) return key;
|
||||
key += '-';
|
||||
key += Array.prototype.join.call(rejectTypes, ",");
|
||||
return key;
|
||||
}
|
||||
10
assets/Script/ECS/lib/ECSWorld.ts.meta
Executable file
10
assets/Script/ECS/lib/ECSWorld.ts.meta
Executable file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"uuid": "decf2eee-3ef7-456d-bd8c-8d4eaa63e617",
|
||||
"importer": "typescript",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
13
assets/Script/ECS/systems.meta
Normal file
13
assets/Script/ECS/systems.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "1.1.3",
|
||||
"uuid": "db93b255-0e1f-4c8a-adc8-d3d2fca077cc",
|
||||
"importer": "folder",
|
||||
"isBundle": false,
|
||||
"bundleName": "",
|
||||
"priority": 1,
|
||||
"compressionType": {},
|
||||
"optimizeHotUpdate": {},
|
||||
"inlineSpriteFrames": {},
|
||||
"isRemoteBundle": {},
|
||||
"subMetas": {}
|
||||
}
|
||||
71
assets/Script/ECS/systems/SysAttack.ts
Normal file
71
assets/Script/ECS/systems/SysAttack.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { ComAttackable } from "../components/ComAttackable";
|
||||
import { ComBeAttacked } from "../components/ComBeAttacked";
|
||||
import { ComRoleConfig } from "../components/ComRoleConfig";
|
||||
import { ComTransform } from "../components/ComTransform";
|
||||
import { ECSSystem } from "../lib/ECSSystem";
|
||||
import { ECSWorld, GenFillterKey } from "../lib/ECSWorld";
|
||||
|
||||
const FILTER_ATTACKABLE = GenFillterKey([ComAttackable]);
|
||||
const FILTER_BEATTACKED = GenFillterKey([ComBeAttacked]);
|
||||
export class SysAttack extends ECSSystem {
|
||||
/** 连接 */
|
||||
public onAdd(world: ECSWorld): void {
|
||||
|
||||
}
|
||||
/** 断开连接 */
|
||||
public onRemove(world: ECSWorld): void {
|
||||
|
||||
}
|
||||
/** 添加实体 */
|
||||
public onEntityEnter(world: ECSWorld, entity: number): void {
|
||||
|
||||
}
|
||||
/** */
|
||||
public onEntityLeave(world: ECSWorld, entity: number): void {
|
||||
|
||||
}
|
||||
/** 更新 */
|
||||
public onUpdate(world: ECSWorld, dt: number): void {
|
||||
let filter = world.getFilter(FILTER_ATTACKABLE);
|
||||
filter.walk((entity: number) => {
|
||||
let comTransSelf = world.getComponent(entity, ComTransform);
|
||||
let comAttackable = world.getComponent(entity, ComAttackable);
|
||||
let comRoleConfigSelf = world.getComponent(entity, ComRoleConfig);
|
||||
if(comAttackable.countDown <= 0) return ;
|
||||
comAttackable.countDown -= dt;
|
||||
|
||||
if(comAttackable.mustAttackFrame)
|
||||
if(comAttackable.dirty && comAttackable.countDown <= comAttackable.hurtFrame) {
|
||||
comAttackable.dirty = false;
|
||||
world.getFilter(FILTER_BEATTACKED).walk((entityOther: number) => {
|
||||
let comRoleConfigOther = world.getComponent(entityOther, ComRoleConfig);
|
||||
let comTransOther = world.getComponent(entityOther, ComTransform);
|
||||
if(!comRoleConfigOther || comRoleConfigOther.team == comRoleConfigSelf.team) return ;
|
||||
let xDiff = comTransOther.x - comTransSelf.x;
|
||||
if(xDiff * Math.sign(xDiff) >= comAttackable.hurtArea.x || Math.abs(comTransOther.y - comTransSelf.y) >= comAttackable.hurtArea.y) {
|
||||
return ;
|
||||
}
|
||||
|
||||
// 扣血
|
||||
if(!comRoleConfigOther || comRoleConfigOther.nowHP <= 0) return ;
|
||||
comRoleConfigOther.lastHP = comRoleConfigOther.nowHP;
|
||||
comRoleConfigOther.nowHP -= comAttackable.attack;
|
||||
comRoleConfigOther.HPDirty = true;
|
||||
|
||||
// 打断对方的攻击动作
|
||||
let comAttackableOther = world.getComponent(entityOther, ComAttackable);
|
||||
if(!comAttackableOther || comAttackableOther.countDown <= 0) return ;
|
||||
if(comAttackableOther.countDown >= comAttackableOther.mustAttackFrame) {
|
||||
comAttackableOther.dirty = false;
|
||||
}
|
||||
|
||||
comAttackable.countDown = 0.25;
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
10
assets/Script/ECS/systems/SysAttack.ts.meta
Normal file
10
assets/Script/ECS/systems/SysAttack.ts.meta
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"uuid": "780c245c-45f2-47ae-a01a-b61dcfb3ab3e",
|
||||
"importer": "typescript",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
65
assets/Script/ECS/systems/SysBehaviorTree.ts
Normal file
65
assets/Script/ECS/systems/SysBehaviorTree.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { BT } from "../../Common/BehaviorTree";
|
||||
import { ComBehaviorTree } from "../components/ComBehaviorTree";
|
||||
import { ECSSystem } from "../lib/ECSSystem";
|
||||
import { ECSWorld, GenFillterKey } from "../lib/ECSWorld";
|
||||
|
||||
|
||||
|
||||
const FILTER_BEHAVIORTREE = GenFillterKey([ComBehaviorTree]);
|
||||
|
||||
const Context = new BT.ExecuteContext();
|
||||
|
||||
export class SysBehaviorTree extends ECSSystem {
|
||||
|
||||
/** 连接 */
|
||||
public onAdd(world: ECSWorld): void{
|
||||
Context.init(this, world);
|
||||
}
|
||||
/** 断开连接 */
|
||||
public onRemove(world: ECSWorld): void {
|
||||
|
||||
}
|
||||
/** 添加实体 */
|
||||
public onEntityEnter(world: ECSWorld, entity: number): void {
|
||||
|
||||
}
|
||||
|
||||
/** */
|
||||
public onEntityLeave(world: ECSWorld, entity: number): void {
|
||||
|
||||
}
|
||||
|
||||
/** 更新 */
|
||||
public onUpdate(world: ECSWorld, dt: number): void {
|
||||
Context.executor = this;
|
||||
Context.dt = dt;
|
||||
Context.world = world;
|
||||
|
||||
world.getFilter(FILTER_BEHAVIORTREE).walk((entity: number) => {
|
||||
let comBehavior = world.getComponent(entity, ComBehaviorTree);
|
||||
Context.set(entity, dt, comBehavior.bb);
|
||||
if(comBehavior.root.state !== BT.NodeState.Executing) {
|
||||
this.onEnterBTNode(comBehavior.root, Context);
|
||||
}else {
|
||||
this.updateBTNode(comBehavior.root, Context);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
/** 进入节点 */
|
||||
public onEnterBTNode(node: BT.NodeBase, context: BT.ExecuteContext) {
|
||||
let handler = BT.NodeHandlers[node.type];
|
||||
handler.onEnter(node, context);
|
||||
}
|
||||
|
||||
/** 更新节点状态 */
|
||||
public updateBTNode(node: BT.NodeBase, context: BT.ExecuteContext) {
|
||||
let handler = BT.NodeHandlers[node.type];
|
||||
handler.onUpdate(node, context);
|
||||
}
|
||||
|
||||
public canExecuteBTNode(node: BT.NodeBase, context: BT.ExecuteContext) : boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
10
assets/Script/ECS/systems/SysBehaviorTree.ts.meta
Normal file
10
assets/Script/ECS/systems/SysBehaviorTree.ts.meta
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"uuid": "1b6bd6b5-cb8e-46e8-8052-8ed53e75a2e5",
|
||||
"importer": "typescript",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
96
assets/Script/ECS/systems/SysCocosView.ts
Executable file
96
assets/Script/ECS/systems/SysCocosView.ts
Executable file
@@ -0,0 +1,96 @@
|
||||
import CocosHelper from "../../Common/CocosHelper";
|
||||
import { ComNodeConfig } from "../components/ComNodeConfig";
|
||||
import { ComCocosNode } from "../components/ComCocosNode";
|
||||
import { ECSSystem } from "../lib/ECSSystem";
|
||||
import { ECSWorld, GenFillterKey } from "../lib/ECSWorld";
|
||||
import { ComTransform } from "../components/ComTransform";
|
||||
import { EventProcess } from "../../Core/EventProcess";
|
||||
import { ComRoleConfig } from "../components/ComRoleConfig";
|
||||
|
||||
export interface ITouchProcessor {
|
||||
onTouchStart(worldPos: cc.Vec2, world: ECSWorld): void;
|
||||
onTouchMove?(worldPos: cc.Vec2, world: ECSWorld): void;
|
||||
onTouchEnd?(worldPos: cc.Vec2, world: ECSWorld): void;
|
||||
onTouchCancel?(worldPos: cc.Vec2, world: ECSWorld): void;
|
||||
}
|
||||
|
||||
const FILTER_COCOS_NODE = GenFillterKey([ComNodeConfig], [ComCocosNode]);
|
||||
const FILTER_NODE_EVENT = GenFillterKey([ComCocosNode, ComTransform]);
|
||||
export class SysCocosView extends ECSSystem implements ITouchProcessor {
|
||||
|
||||
onTouchStart(worldPos: cc.Vec2, world: ECSWorld): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
onAdd(world: ECSWorld) {
|
||||
|
||||
}
|
||||
|
||||
onRemove(world: ECSWorld) {
|
||||
|
||||
}
|
||||
|
||||
onEntityEnter(world: ECSWorld, entity: number) {
|
||||
|
||||
}
|
||||
|
||||
onEntityLeave(world: ECSWorld, entity: number) {
|
||||
|
||||
}
|
||||
|
||||
onUpdate(world:ECSWorld, dt:number) {
|
||||
world.getFilter(FILTER_COCOS_NODE).walk((entity: number) => {
|
||||
let comNodeConfig = world.getComponent(entity, ComNodeConfig);
|
||||
let comView = world.addComponent(entity, ComCocosNode);
|
||||
|
||||
let comRoleConfig = world.getComponent(entity, ComRoleConfig);
|
||||
this._loadView(world, entity, comNodeConfig).then((node: cc.Node) => {
|
||||
console.log('load view success');
|
||||
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
world.getFilter(FILTER_NODE_EVENT).walk((entity: number) => {
|
||||
let comCocosNode = world.getComponent(entity, ComCocosNode);
|
||||
if(!comCocosNode.loaded) return ;
|
||||
let eventProcess = comCocosNode.node.getComponent(EventProcess);
|
||||
if(!eventProcess) return ;
|
||||
|
||||
let comTrans = world.getComponent(entity, ComTransform);
|
||||
eventProcess.sync(comTrans.x, comTrans.y, comTrans.dir);
|
||||
while(comCocosNode.events.length) {
|
||||
let event = comCocosNode.events.shift();
|
||||
eventProcess.processEvent(event);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private async _loadView(world: ECSWorld, entity: number, nodeConfig: ComNodeConfig) {
|
||||
let prefab = await CocosHelper.loadResSync<cc.Prefab>(nodeConfig.prefabUrl, cc.Prefab);
|
||||
if(!prefab) {
|
||||
cc.warn(`加载失败: ${nodeConfig.prefabUrl}`);
|
||||
return;
|
||||
}
|
||||
let comView = world.getComponent(entity, ComCocosNode);
|
||||
if(comView.node) { // 销毁当前node
|
||||
this.destoryView(comView.node);
|
||||
}
|
||||
let layers = cc.find('Canvas/Layers');
|
||||
if(!layers) return ;
|
||||
|
||||
let node = cc.instantiate(prefab);
|
||||
node.parent = layers.getChildByName(`${nodeConfig.layer}`);
|
||||
comView.node = node;
|
||||
comView.loaded = true;
|
||||
return node;
|
||||
}
|
||||
|
||||
private destoryView(node: cc.Node) {
|
||||
node.removeFromParent();
|
||||
node.destroy();
|
||||
}
|
||||
}
|
||||
10
assets/Script/ECS/systems/SysCocosView.ts.meta
Normal file
10
assets/Script/ECS/systems/SysCocosView.ts.meta
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"uuid": "e4f95950-b571-48dd-8c95-7808eef0c336",
|
||||
"importer": "typescript",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
82
assets/Script/ECS/systems/SysMonitor.ts
Normal file
82
assets/Script/ECS/systems/SysMonitor.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import { ComMonitor } from "../components/ComMonitor";
|
||||
import { ComRoleConfig } from "../components/ComRoleConfig";
|
||||
import { ComTransform } from "../components/ComTransform";
|
||||
import { ECSSystem } from "../lib/ECSSystem";
|
||||
import { ECSWorld, GenFillterKey } from "../lib/ECSWorld";
|
||||
|
||||
const FILTER_MONITOR = GenFillterKey([ComRoleConfig, ComTransform, ComMonitor]);
|
||||
export class SysMonitor extends ECSSystem {
|
||||
/** 连接 */
|
||||
public onAdd(world: ECSWorld): void {
|
||||
|
||||
}
|
||||
/** 断开连接 */
|
||||
public onRemove(world: ECSWorld): void {
|
||||
|
||||
}
|
||||
/** 添加实体 */
|
||||
public onEntityEnter(world: ECSWorld, entity: number): void {
|
||||
|
||||
}
|
||||
/** */
|
||||
public onEntityLeave(world: ECSWorld, entity: number): void {
|
||||
let filter = world.getFilter(FILTER_MONITOR);
|
||||
// 判断当前monitor是否
|
||||
filter.entities.forEach((value: boolean, otherEntity: number) => {
|
||||
let comMonitor = world.getComponent(otherEntity, ComMonitor);
|
||||
if(!comMonitor) return ;
|
||||
for(let i=comMonitor.others.length-1; i>=0; i--) {
|
||||
if(comMonitor.others[i] == entity) {
|
||||
comMonitor.others.splice(i);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
/** 更新 */
|
||||
public onUpdate(world: ECSWorld, dt: number): void {
|
||||
let filter = world.getFilter(FILTER_MONITOR);
|
||||
filter.walk((entity: number) => {
|
||||
let comMonitor = world.getComponent(entity, ComMonitor);
|
||||
let comTrans = world.getComponent(entity, ComTransform);
|
||||
let comRoleConfig = world.getComponent(entity, ComRoleConfig);
|
||||
|
||||
// 判断当前monitor是否
|
||||
filter.entities.forEach((value: boolean, otherEntity: number) => {
|
||||
let comTransOther = world.getComponent(otherEntity, ComTransform);
|
||||
let comRoleConfigOther = world.getComponent(otherEntity, ComRoleConfig);
|
||||
if(entity == otherEntity || !comRoleConfigOther || comRoleConfigOther.team == comRoleConfig.team) return ;
|
||||
|
||||
let a = cc.v2(comTrans.x, comTrans.y);
|
||||
|
||||
let centerPoint = a.add(comTrans.dir.mul(comMonitor.lookLen));
|
||||
let b = centerPoint.add(cc.v2(comTrans.dir.y, -comTrans.dir.x).mul(comMonitor.lookSize));
|
||||
let c = centerPoint.add(cc.v2(-comTrans.dir.y, comTrans.dir.x).mul(comMonitor.lookSize));
|
||||
|
||||
let _check = (com: ComTransform) => {
|
||||
return (a.sub(cc.v2(com.x, com.y)).len() < comMonitor.aroundLen || isInTriangle(cc.v2(com.x, com.y), a, b, c))
|
||||
}
|
||||
for(let i=comMonitor.others.length-1; i>=0; i--) {
|
||||
const com = world.getComponent(comMonitor.others[i], ComTransform);
|
||||
if(!com || !_check(com)) {
|
||||
comMonitor.others.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if(comMonitor.others.indexOf(otherEntity) == -1 && _check(comTransOther)) {
|
||||
comMonitor.others.push(otherEntity);
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 判断一个点是否在三角形内
|
||||
function isInTriangle(point: cc.Vec2, triA: cc.Vec2, triB: cc.Vec2, triC: cc.Vec2) {
|
||||
let AB = triB.sub(triA), AC = triC.sub(triA), BC = triC.sub(triB), AD = point.sub(triA), BD = point.sub(triB);
|
||||
//@ts-ignore
|
||||
return (AB.cross(AC) >= 0 ^ AB.cross(AD) < 0) && (AB.cross(AC) >= 0 ^ AC.cross(AD) >= 0) && (BC.cross(AB) > 0 ^ BC.cross(BD) >= 0);
|
||||
}
|
||||
10
assets/Script/ECS/systems/SysMonitor.ts.meta
Normal file
10
assets/Script/ECS/systems/SysMonitor.ts.meta
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"uuid": "0b207deb-ff30-4fbe-aca7-2f77f299569d",
|
||||
"importer": "typescript",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
70
assets/Script/ECS/systems/SysMovable.ts
Normal file
70
assets/Script/ECS/systems/SysMovable.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { ComCocosNode as ComCocosNode } from "../components/ComCocosNode";
|
||||
import { ComMovable } from "../components/ComMovable";
|
||||
import { ComTransform } from "../components/ComTransform";
|
||||
import { ECSSystem } from "../lib/ECSSystem";
|
||||
import { ECSWorld, GenFillterKey as GenFilterKey } from "../lib/ECSWorld";
|
||||
|
||||
const FILTER_MOVE = GenFilterKey([ComMovable, ComTransform, ComCocosNode]);
|
||||
export class SysMovable extends ECSSystem {
|
||||
/** 连接 */
|
||||
public onAdd(world: ECSWorld): void{
|
||||
|
||||
}
|
||||
/** 断开连接 */
|
||||
public onRemove(world: ECSWorld): void {
|
||||
|
||||
}
|
||||
/** 添加实体 */
|
||||
public onEntityEnter(world: ECSWorld, entity: number): void {
|
||||
|
||||
}
|
||||
|
||||
/** */
|
||||
public onEntityLeave(world: ECSWorld, entity:number): void {
|
||||
|
||||
}
|
||||
|
||||
/** 更新 */
|
||||
public onUpdate(world: ECSWorld, dt:number): void {
|
||||
world.getFilter(FILTER_MOVE).walk((entity: number) => {
|
||||
let comMovable = world.getComponent(entity, ComMovable);
|
||||
let comTrans = world.getComponent(entity, ComTransform);
|
||||
|
||||
if(comMovable.speed <= 0 || comMovable.pointIdx >= comMovable.points.length) {
|
||||
return ;
|
||||
}
|
||||
|
||||
if(!comMovable.running) {
|
||||
comMovable.running = true;
|
||||
}
|
||||
|
||||
let moveLen = comMovable.speed * dt;
|
||||
while(moveLen > 0 && comMovable.pointIdx < comMovable.points.length) {
|
||||
let nextPoint = comMovable.points[comMovable.pointIdx];
|
||||
let offsetX = nextPoint.x - comTrans.x;
|
||||
let offsetY = nextPoint.y - comTrans.y;
|
||||
let offsetLen = Math.sqrt(offsetX * offsetX + offsetY * offsetY);
|
||||
if(offsetLen <= moveLen) {
|
||||
moveLen -= offsetLen;
|
||||
comTrans.x = nextPoint.x;
|
||||
comTrans.y = nextPoint.y;
|
||||
comMovable.pointIdx ++;
|
||||
continue;
|
||||
}
|
||||
comTrans.dir.x = offsetX / offsetLen;
|
||||
comTrans.dir.y = offsetY / offsetLen;
|
||||
comTrans.x += moveLen * comTrans.dir.x;
|
||||
comTrans.y += moveLen * comTrans.dir.y;
|
||||
|
||||
moveLen = -1;
|
||||
}
|
||||
|
||||
if(comMovable.pointIdx >= comMovable.points.length) {
|
||||
comMovable.speed = 0;
|
||||
comMovable.speedDirty = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
10
assets/Script/ECS/systems/SysMovable.ts.meta
Normal file
10
assets/Script/ECS/systems/SysMovable.ts.meta
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"uuid": "6f024564-9b8c-47ad-95eb-eca4afd96720",
|
||||
"importer": "typescript",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
69
assets/Script/ECS/systems/SysRoleState.ts
Normal file
69
assets/Script/ECS/systems/SysRoleState.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { EventDeath, EventHPChange, EventHurt, EventRun, EventStand } from "../../Struct/NodeEvent";
|
||||
import { ComBehaviorTree } from "../components/ComBehaviorTree";
|
||||
import { ComCocosNode } from "../components/ComCocosNode";
|
||||
import { ComMonitor } from "../components/ComMonitor";
|
||||
import { ComMovable } from "../components/ComMovable";
|
||||
import { ComNodeConfig } from "../components/ComNodeConfig";
|
||||
import { ComRoleConfig } from "../components/ComRoleConfig";
|
||||
import { ECSSystem } from "../lib/ECSSystem";
|
||||
import { ECSWorld, GenFillterKey } from "../lib/ECSWorld";
|
||||
|
||||
const FILTER_ROLE_NODE = GenFillterKey([ComCocosNode, ComRoleConfig]);
|
||||
export class SysRoleState extends ECSSystem {
|
||||
/** 连接 */
|
||||
public onAdd(world: ECSWorld): void {
|
||||
|
||||
}
|
||||
/** 断开连接 */
|
||||
public onRemove(world: ECSWorld): void {
|
||||
|
||||
}
|
||||
/** 添加实体 */
|
||||
public onEntityEnter(world: ECSWorld, entity: number): void {
|
||||
|
||||
}
|
||||
/** */
|
||||
public onEntityLeave(world: ECSWorld, entity: number): void {
|
||||
|
||||
}
|
||||
/** 更新 */
|
||||
public onUpdate(world: ECSWorld, dt: number): void {
|
||||
world.getFilter(FILTER_ROLE_NODE).walk((entity: number) => {
|
||||
let comCocosNode = world.getComponent(entity, ComCocosNode);
|
||||
if(!comCocosNode.loaded) return ;
|
||||
let comRoleConfig = world.getComponent(entity, ComRoleConfig);
|
||||
let comMovable = world.getComponent(entity, ComMovable);
|
||||
|
||||
if(comMovable && comMovable.speedDirty) {
|
||||
comMovable.speedDirty = false;
|
||||
if(comMovable.speed > 0) {
|
||||
comCocosNode.events.push(new EventRun());
|
||||
}else {
|
||||
comCocosNode.events.push(new EventStand());
|
||||
}
|
||||
}
|
||||
|
||||
if(comRoleConfig && comRoleConfig.HPDirty) {
|
||||
comCocosNode.events.push(new EventHPChange(comRoleConfig.maxHP, comRoleConfig.lastHP, comRoleConfig.nowHP));
|
||||
if(comRoleConfig.lastHP > comRoleConfig.nowHP) {
|
||||
comCocosNode.events.push(new EventHurt());
|
||||
}
|
||||
if(comRoleConfig.nowHP <= 0) {
|
||||
comCocosNode.events.push(new EventDeath(() => {
|
||||
world.removeComponent(entity, ComNodeConfig);
|
||||
world.removeComponent(entity, ComCocosNode);
|
||||
world.removeEntity(entity);
|
||||
comCocosNode.node.destroy();
|
||||
}));
|
||||
world.removeComponent(entity, ComBehaviorTree);
|
||||
world.removeComponent(entity, ComMonitor);
|
||||
world.removeComponent(entity, ComMovable);
|
||||
world.removeComponent(entity, ComRoleConfig);
|
||||
}
|
||||
comRoleConfig.HPDirty = false;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
10
assets/Script/ECS/systems/SysRoleState.ts.meta
Normal file
10
assets/Script/ECS/systems/SysRoleState.ts.meta
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"uuid": "b679be6f-54ac-4790-99ff-41e3993bf130",
|
||||
"importer": "typescript",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
13
assets/Script/ECS/worlds.meta
Normal file
13
assets/Script/ECS/worlds.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "1.1.3",
|
||||
"uuid": "00fbfe49-b0ae-426b-ae03-f11fba4ce919",
|
||||
"importer": "folder",
|
||||
"isBundle": false,
|
||||
"bundleName": "",
|
||||
"priority": 1,
|
||||
"compressionType": {},
|
||||
"optimizeHotUpdate": {},
|
||||
"inlineSpriteFrames": {},
|
||||
"isRemoteBundle": {},
|
||||
"subMetas": {}
|
||||
}
|
||||
5
assets/Script/ECS/worlds/WorldCocosView.ts
Normal file
5
assets/Script/ECS/worlds/WorldCocosView.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { ECSWorld } from "../lib/ECSWorld";
|
||||
|
||||
export class WorldCocosView extends ECSWorld {
|
||||
|
||||
}
|
||||
10
assets/Script/ECS/worlds/WorldCocosView.ts.meta
Normal file
10
assets/Script/ECS/worlds/WorldCocosView.ts.meta
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"uuid": "2bbeb931-7f40-48e4-937d-13006b7f016b",
|
||||
"importer": "typescript",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
Reference in New Issue
Block a user