对ECS系统进行注释、移除JobSystem

This commit is contained in:
yhh
2023-03-13 17:46:16 +08:00
parent 78079252c9
commit 1adc5f1729
17 changed files with 936 additions and 837 deletions

View File

@@ -1,51 +1,35 @@
///<reference path="./EntitySystem.ts"/>
module es {
/**
* 追踪每个实体的冷却时间,当实体的计时器耗尽时进行处理
*
* 一个示例系统将是ExpirationSystem该系统将在特定生存期后删除实体。
* 你不必运行会为每个实体递减timeLeft值的系统
* 而只需使用此系统在寿命最短的实体时在将来执行
* 然后重置系统在未来的某一个最短命实体的时间运行
*
* 另一个例子是一个动画系统
* 你知道什么时候你必须对某个实体进行动画制作比如300毫秒内。
* 所以你可以设置系统以300毫秒为单位运行来执行动画
*
* 这将在某些情况下节省CPU周期
* 这个类是一个实体系统的基类,其可以被子类继承并在子类中实现具体的实体处理逻辑。
* 该类提供了实体的添加、删除、更新等基本操作,并支持设置系统的更新时序、检查系统是否需要处理实体、获取系统的场景等方法
*/
export abstract class DelayedIteratingSystem extends EntitySystem {
/**
* 一个实体应被处理的时间
*/
private delay: number = 0;
/**
* 如果系统正在运行,并倒计时延迟
*/
private running: boolean = false;
/**
* 倒计时
*/
private acc: number = 0;
private delay = 0;
private running = false;
private acc = 0;
constructor(matcher: Matcher) {
super(matcher);
}
protected process(entities: Entity[]) {
let processed = entities.length;
if (processed == 0) {
const processed = entities.length;
if (processed === 0) {
this.stop();
return;
}
this.delay = Number.MAX_VALUE;
for (let i = 0; processed > i; i++) {
let e = entities[i];
this.processDelta(e, this.acc);
let remaining = this.getRemainingDelay(e);
for (let i = 0; i < processed; i++) {
const entity = entities[i];
this.processDelta(entity, this.acc);
const remaining = this.getRemainingDelay(entity);
if (remaining <= 0) {
this.processExpired(e);
this.processExpired(entity);
} else {
this.offerDelay(remaining);
}
@@ -59,6 +43,7 @@ module es {
this.acc += Time.deltaTime;
return this.acc >= this.delay;
}
return false;
}
@@ -67,7 +52,7 @@ module es {
* 如果系统已经停止(不运行),那么提供的延迟将被用来重新启动系统,无论其值如何
* 如果系统已经在倒计时,并且提供的延迟大于剩余时间,系统将忽略它。
* 如果提供的延迟时间短于剩余时间,系统将重新启动,以提供的延迟时间运行。
* @param offeredDelay
* @param offeredDelay 提供的延迟时间,单位为秒
*/
public offerDelay(offeredDelay: number) {
if (!this.running) {
@@ -78,22 +63,6 @@ module es {
}
}
/**
* 处理本系统感兴趣的实体
* 从实体定义的延迟中抽象出accumulativeDelta
* @param entity
* @param accumulatedDelta 本系统最后一次执行后的delta时间
*/
protected abstract processDelta(entity: Entity, accumulatedDelta: number);
protected abstract processExpired(entity: Entity);
/**
* 返回该实体处理前的延迟时间
* @param entity
*/
protected abstract getRemainingDelay(entity: Entity): number;
/**
* 获取系统被命令处理实体后的初始延迟
*/
@@ -102,9 +71,9 @@ module es {
}
/**
* 获取系统计划运行前的时间
* 如果系统没有运行,则返回零
*/
* 获取系统计划运行前的时间
* 如果系统没有运行,则返回零
*/
public getRemainingTimeUntilProcessing(): number {
if (this.running) {
return this.delay - this.acc;
@@ -127,5 +96,25 @@ module es {
this.running = false;
this.acc = 0;
}
/**
* 处理给定实体的延迟时间的一部分,抽象出累积的 Delta 值。
* @param entity 要处理的实体
* @param accumulatedDelta 本系统最后一次执行后的累积 delta 时间
*/
protected abstract processDelta(entity: Entity, accumulatedDelta: number);
/**
* 处理已到期的实体。
* @param entity 要处理的实体
*/
protected abstract processExpired(entity: Entity);
/**
* 获取给定实体剩余的延迟时间。
* @param entity 要检查的实体
* @returns 剩余的延迟时间(以秒为单位)
*/
protected abstract getRemainingDelay(entity: Entity): number;
}
}

View File

@@ -1,53 +1,76 @@
///<reference path="./EntitySystem.ts" />
module es {
/**
* 基本实体处理系统。将其用作处理具有特定组件的许多实体的基础
*
* 按实体引用遍历实体订阅成员实体的系统
* 当你需要处理与Matcher相匹配的实体并且你更喜欢使用Entity的时候可以使用这个功能。
* 定义一个处理实体的抽象类,继承自 EntitySystem 类。
* 子类需要实现 processEntity 方法,用于实现具体的实体处理逻辑。
*/
export abstract class EntityProcessingSystem extends EntitySystem {
/**
* 是否启用系统,默认为启用。
*/
public enabled: boolean = true;
/**
* 构造函数,初始化实体匹配器。
* @param matcher 实体匹配器
*/
constructor(matcher: Matcher) {
super(matcher);
}
/**
* 处理单个实体,由子类实现。
* @param entity 待处理的实体
*/
public abstract processEntity(entity: Entity): void;
/**
* 处理特定的实体
* @param entity
* 在晚于 update 的时间更新实体,由子类实现。
* @param entity 待处理的实体
*/
public abstract processEntity(entity: Entity);
public lateProcessEntity(entity: Entity) {
public lateProcessEntity(entity: Entity): void {
// do nothing
}
/**
* 遍历这个系统的所有实体逐个处理它们
* @param entities
* 遍历系统的所有实体逐个进行实体处理。
* @param entities 实体数组
*/
protected process(entities: Entity[]) {
if (entities.length == 0)
// 如果实体数组为空,则直接返回
if (entities.length === 0) {
return;
for (let i = 0, s = entities.length; i < s; ++ i) {
let entity = entities[i];
}
// 遍历实体数组,逐个进行实体处理
for (let i = 0, len = entities.length; i < len; i++) {
const entity = entities[i];
this.processEntity(entity);
}
}
/**
* 在晚于 update 的时间更新实体。
* @param entities 实体数组
*/
protected lateProcess(entities: Entity[]) {
if (entities.length == 0)
// 如果实体数组为空,则直接返回
if (entities.length === 0) {
return;
}
for (let i = 0, s = entities.length; i < s; ++ i) {
let entity = entities[i];
// 遍历实体数组,逐个进行实体处理
for (let i = 0, len = entities.length; i < len; i++) {
const entity = entities[i];
this.lateProcessEntity(entity);
}
}
/**
* 判断系统是否需要进行实体处理。
* 如果启用了系统,则需要进行实体处理,返回 true
* 否则不需要进行实体处理,返回 false。
*/
protected checkProcessing(): boolean {
return this.enabled;
}

View File

@@ -1,40 +1,63 @@
module es {
/**
* 实体系统以一定的时间间隔进行处理
* 定义一个按时间间隔处理的抽象类,继承自 EntitySystem 类。
* 子类需要实现 process 方法,用于实现具体的处理逻辑。
*/
export abstract class IntervalSystem extends EntitySystem {
/**
* 累积增量以跟踪间隔
*/
protected acc: number = 0;
private acc: number = 0;
/**
* 更新之间需要等待多长时间
*/
private readonly interval: number = 0;
private intervalDelta: number = 0;
private readonly interval: number;
/**
* 时间间隔的余数,用于计算下一次需要等待的时间
*/
private intervalRemainder: number = 0;
/**
* 构造函数,初始化时间间隔。
* @param matcher 实体匹配器
* @param interval 时间间隔
*/
constructor(matcher: Matcher, interval: number) {
super(matcher);
this.interval = interval;
}
protected checkProcessing() {
/**
* 判断是否需要进行处理。
* 如果需要进行处理,则更新累积增量和时间间隔余数,返回 true
* 否则返回 false。
*/
protected checkProcessing(): boolean {
// 更新累积增量
this.acc += Time.deltaTime;
if (this.acc >= this.interval) {
this.acc -= this.interval;
this.intervalDelta = (this.acc - this.intervalDelta);
// 如果累积增量超过时间间隔,则进行处理
if (this.acc >= this.interval) {
// 更新时间间隔余数
this.intervalRemainder = this.acc - this.interval;
// 重置累积增量
this.acc = 0;
// 返回 true表示需要进行处理
return true;
}
// 返回 false表示不需要进行处理
return false;
}
/**
* 获取本系统上次处理后的实际delta
* 获取本系统上次处理后的实际 delta 值。
* 实际 delta 值等于时间间隔加上时间间隔余数。
*/
protected getIntervalDelta() {
return this.interval + this.intervalDelta;
protected getIntervalDelta(): number {
return this.interval + this.intervalRemainder;
}
}
}

View File

@@ -1,102 +0,0 @@
module es {
/**
* JobSystem使用实体的子集调用Executeentities并在指定数量的线程上分配工作负载。
*/
export abstract class JobSystem extends EntitySystem {
public readonly _threads: number;
public readonly _jobs: Job[];
public readonly _executeStr: string;
constructor(matcher: Matcher, threads: number) {
super(matcher);
this._threads = threads;
this._jobs = new Array(threads);
for (let i = 0; i < this._jobs.length; i++) {
this._jobs[i] = new Job();
}
this._executeStr = JSON.stringify(this.execute, function (key, val) {
if (typeof val === 'function') {
return val + '';
}
return val;
});
}
protected process(entities: Entity[]) {
let remainder = entities.length & this._threads;
let slice = entities.length / this._threads + (remainder == 0 ? 0 : 1);
for (let t = 0; t < this._threads; t++) {
let from = t * slice;
let to = from + slice;
if (to > entities.length) {
to = entities.length;
}
let job = this._jobs[t];
job.set(entities, from, to, this._executeStr, this);
if (from != to) {
const worker = WorkerUtils.makeWorker(this.queueOnThread);
const workerDo = WorkerUtils.workerMessage(worker);
workerDo(job).then((message) => {
let job = message as Job;
this.resetJob(job);
worker.terminate();
}).catch((err) => {
job.err = err;
worker.terminate();
});
}
}
}
private queueOnThread() {
onmessage = ({ data: { jobId, message } }) => {
let job = message[0] as Job;
let executeFunc: Function = JSON.parse(job.execute, function (k, v) {
if (v.indexOf && v.indexOf('function') > -1) {
return eval("(function(){return " + v + " })()")
}
return v;
});
for (let i = job.from; i < job.to; i++) {
executeFunc.call(job.context, job.entities[i]);
}
postMessage({ jobId, result: message }, null);
};
}
/**
* 当操作完成时,改变的值需要用户进行手动传递
* 由于worker数据无法共享所以这块需要特殊处理
* @example this.test = job[0].context.test;
* @param job
*/
protected abstract resetJob(job: Job);
/**
* 对指定实体进行多线程操作
* @param entity
*/
protected abstract execute(entity: Entity);
}
class Job {
public entities: Entity[];
public from: number;
public to: number;
public worker: Worker;
public execute: string;
public err: string;
public context;
public set(entities: Entity[], from: number, to: number, execute: string, context: any) {
this.entities = entities;
this.from = from;
this.to = to;
this.execute = execute;
this.context = context;
}
}
}

View File

@@ -1,11 +1,21 @@
module es {
/**
* 定义一个被动的实体系统,继承自 EntitySystem 类。
* 被动的实体系统不会对实体进行任何修改,只会被动地接收实体的变化事件。
*/
export abstract class PassiveSystem extends EntitySystem {
public onChanged(entity: Entity) {
}
/**
* 当实体发生变化时,不进行任何操作。
* @param entity 发生变化的实体
*/
public onChanged(entity: Entity) { }
/**
* 不进行任何处理,只进行开始和结束计时。
* @param entities 实体数组,未被使用
*/
protected process(entities: Entity[]) {
// 我们用我们自己的不考虑实体的基本实体系统来代替
// 调用 begin 和 end 方法,开始和结束计时
this.begin();
this.end();
}

View File

@@ -1,17 +1,30 @@
/** 用于协调其他系统的通用系统基类 */
module es {
/**
* 定义一个处理实体的抽象类,继承自 EntitySystem 类。
* 子类需要实现 processSystem 方法,用于实现具体的处理逻辑。
*/
export abstract class ProcessingSystem extends EntitySystem {
public onChanged(entity: Entity) {
}
/** 处理我们的系统 每帧调用 */
public abstract processSystem();
/**
* 当实体发生变化时,不进行任何操作。
* @param entity 发生变化的实体
*/
public onChanged(entity: Entity) { }
/**
* 处理实体,每帧调用 processSystem 方法进行处理。
* @param entities 实体数组,未被使用
*/
protected process(entities: Entity[]) {
// 调用 begin 和 end 方法,开始和结束计时
this.begin();
// 调用子类实现的 processSystem 方法进行实体处理
this.processSystem();
this.end();
}
/**
* 处理实体的具体方法,由子类实现。
*/
public abstract processSystem(): void;
}
}

View File

@@ -1,39 +1,51 @@
module es {
interface IdentityHashMap {
[key: string]: ComponentType;
}
/**
* 组件类型工厂,用于生成和管理组件类型。
* 维护了一个类型映射表,将组件类型与其唯一索引相对应,以便在运行时高效地检查实体是否包含特定的组件类型。
*/
export class ComponentTypeFactory {
private componentTypes_: IdentityHashMap;
/** 组件类型与其唯一索引的映射表 */
private componentTypes: Record<string, ComponentType> = {};
/** 组件类型列表,按索引访问组件类型 */
public readonly types: Bag<ComponentType> = new Bag<ComponentType>();
/** 当前组件类型的计数器 */
private componentTypeCount = 0;
private componentTypeCount_ = 0;
public types: Bag<ComponentType>;
constructor() {
this.componentTypes_ = {};
this.types = new Bag<ComponentType>();
/**
* 获取给定组件类型的唯一索引。
* 如果该组件类型尚未存在于类型映射表中,则创建一个新的组件类型,并将其添加到映射表和类型列表中。
* @param c 要查找或创建的组件类型
* @returns 组件类型的唯一索引
*/
public getIndexFor(c: new (...args: any[]) => any): number {
return this.getTypeFor(c).getIndex();
}
public getTypeFor(c): ComponentType {
if ("number" === typeof c) {
/**
* 获取给定组件类型的ComponentType对象。
* 如果该组件类型尚未存在于类型映射表中则创建一个新的ComponentType对象并将其添加到映射表和类型列表中。
* @param c 要查找或创建的组件类型
* @returns 组件类型的ComponentType对象
*/
public getTypeFor(c: new (...args: any[]) => any): ComponentType {
// 如果给定的组件类型是一个已有的索引则直接返回对应的ComponentType对象
if (typeof c === "number") {
return this.types.get(c);
}
let type: ComponentType = this.componentTypes_[getClassName(c)];
// 获取给定组件类型对应的类名
const className = getClassName(c);
if (type == null) {
const index: number = this.componentTypeCount_++;
type = new ComponentType(c, index);
this.componentTypes_[getClassName(c)] = type;
// 如果类型映射表中不存在该组件类型则创建一个新的ComponentType对象
if (!this.componentTypes[className]) {
const index = this.componentTypeCount++;
const type = new ComponentType(c, index);
this.componentTypes[className] = type;
this.types.set(index, type);
}
return type;
}
public getIndexFor(c): number {
return this.getTypeFor(c).getIndex();
// 返回对应的ComponentType对象
return this.componentTypes[className];
}
}
}

View File

@@ -1,12 +1,28 @@
module es {
/**
* 组件类型管理器,维护了一个组件类型和它们对应的位掩码之间的映射关系。
* 用于实现实体匹配器中组件类型的比较操作,以确定实体是否符合给定的匹配器条件。
*/
export class ComponentTypeManager {
/** 存储组件类型和它们对应的位掩码的Map */
private static _componentTypesMask: Map<any, number> = new Map<any, number>();
/**
* 将给定的组件类型添加到组件类型列表中,并分配一个唯一的位掩码。
* @param type 要添加的组件类型
*/
public static add(type) {
if (!this._componentTypesMask.has(type))
if (!this._componentTypesMask.has(type)) {
this._componentTypesMask.set(type, this._componentTypesMask.size);
}
}
/**
* 获取给定组件类型的位掩码。
* 如果该组件类型还没有分配位掩码,则将其添加到列表中,并分配一个唯一的位掩码。
* @param type 要获取位掩码的组件类型
* @returns 组件类型的位掩码
*/
public static getIndexFor(type) {
let v = -1;
if (!this._componentTypesMask.has(type)) {

View File

@@ -1,4 +1,7 @@
module es {
/**
* 定义一个实体匹配器类。
*/
export class Matcher {
protected allSet: (new (...args: any[]) => Component)[] = [];
protected exclusionSet: (new (...args: any[]) => Component)[] = [];
@@ -25,59 +28,61 @@ module es {
}
public isInterested(components: Bits) {
if (this.allSet.length != 0) {
for (let i = 0, s = this.allSet.length; i < s; ++ i) {
let type = this.allSet[i];
if (!components.get(ComponentTypeManager.getIndexFor(type)))
if (this.allSet.length !== 0) {
for (let i = 0; i < this.allSet.length; i++) {
const type = this.allSet[i];
if (!components.get(ComponentTypeManager.getIndexFor(type))) {
return false;
}
}
}
if (this.exclusionSet.length != 0) {
for (let i = 0, s = this.exclusionSet.length; i < s; ++ i) {
let type = this.exclusionSet[i];
if (components.get(ComponentTypeManager.getIndexFor(type)))
if (this.exclusionSet.length !== 0) {
for (let i = 0; i < this.exclusionSet.length; i++) {
const type = this.exclusionSet[i];
if (components.get(ComponentTypeManager.getIndexFor(type))) {
return false;
}
}
}
if (this.oneSet.length != 0) {
for (let i = 0, s = this.oneSet.length; i < s; ++ i) {
let type = this.oneSet[i];
if (components.get(ComponentTypeManager.getIndexFor(type)))
if (this.oneSet.length !== 0) {
for (let i = 0; i < this.oneSet.length; i++) {
const type = this.oneSet[i];
if (components.get(ComponentTypeManager.getIndexFor(type))) {
return true;
}
}
return false;
}
return true;
}
public all(...types: any[]): Matcher {
let t;
for (let i = 0, s = types.length; i < s; ++ i) {
t = types[i];
this.allSet.push(t);
}
/**
* 添加所有包含的组件类型。
* @param types 所有包含的组件类型列表
*/
public all(...types: (new (...args: any[]) => Component)[]): Matcher {
this.allSet.push(...types);
return this;
}
public exclude(...types: any[]) {
let t;
for (let i = 0, s = types.length; i < s; ++ i) {
t = types[i];
this.exclusionSet.push(t);
}
/**
* 添加排除包含的组件类型。
* @param types 排除包含的组件类型列表
*/
public exclude(...types: (new (...args: any[]) => Component)[]): Matcher {
this.exclusionSet.push(...types);
return this;
}
public one(...types: any[]) {
for (let i = 0, s = types.length; i < s; ++ i) {
const t = types[i];
this.oneSet.push(t);
}
/**
* 添加至少包含其中之一的组件类型。
* @param types 至少包含其中之一的组件类型列表
*/
public one(...types: (new (...args: any[]) => Component)[]): Matcher {
this.oneSet.push(...types);
return this;
}
}

View File

@@ -1,38 +0,0 @@
module es {
/**
* 开辟一个新线程
* 注意:它无法获得主线程中的上下文
*/
export class WorkerUtils {
/** 正在执行的队列 */
private static readonly pendingJobs = {};
private static jobIdGen = 0;
/**
* 创建一个worker
* @param doFunc worker所能做的事情
*/
public static makeWorker(doFunc: Function) {
const worker = new Worker(URL.createObjectURL(new Blob([`(${doFunc.toString()})()`])));
return worker;
}
public static workerMessage(worker: Worker) {
worker.onmessage = ({ data: { result, jobId } }) => {
if (typeof this.pendingJobs[jobId] == 'function')
this.pendingJobs[jobId](result);
delete this.pendingJobs[jobId];
};
return (...message: any[]) => {
return new Promise(resolve => {
const jobId = this.jobIdGen++;
this.pendingJobs[jobId] = resolve;
worker.postMessage({ jobId, message });
});
}
}
}
}