新增 CoroutineManager 协同程序

This commit is contained in:
yhh
2020-08-28 19:12:21 +08:00
parent 7a308f76b6
commit 358e899e8b
17 changed files with 760 additions and 35 deletions

View File

@@ -66,7 +66,7 @@ module es {
this._logs[i] = new FrameLog();
this.sampleFrames = this.targetSampleFrames = 1;
this.width = Core.graphicsDevice.viewport.width * 0.8;
this.width = Math.floor(Core.graphicsDevice.viewport.width * 0.8);
es.Core.emitter.addObserver(CoreEvents.GraphicsDeviceReset, this.onGraphicsDeviceReset, this);
this.onGraphicsDeviceReset();
@@ -269,7 +269,7 @@ module es {
if (Math.max(this._frameAdjust) > TimeRuler.autoAdjustDelay) {
this.sampleFrames = Math.min(TimeRuler.maxSampleFrames, this.sampleFrames);
this.sampleFrames = Math.max(this.targetSampleFrames, (maxTime / frameSpan) + 1);
this.sampleFrames = Math.max(this.targetSampleFrames, Math.floor(maxTime / frameSpan) + 1);
this._frameAdjust = 0;
}

View File

@@ -0,0 +1,38 @@
module es {
/**
* startCoroutine返回的接口提供了在执行中停止协同程序的能力
*/
export interface ICoroutine {
/**
* 停止协同程序
*/
stop();
/**
* 设置协程应该使用时间增量还是使用非缩放时间增量来计时
* @param useUnscaledDeltaTime
*/
setUseUnscaledDeltaTime(useUnscaledDeltaTime): ICoroutine;
}
export class Coroutine {
public static waitForSeconds(seconds: number){
return WaitForSeconds.waiter.wait(seconds);
}
}
/**
* 协作程序需要暂停一段时间时的助手类。
* 返回协同程序。
* waitForSeconds返回number。
*/
export class WaitForSeconds {
public static waiter: WaitForSeconds = new WaitForSeconds();
public waitTime: number;
public wait(seconds: number): WaitForSeconds {
WaitForSeconds.waiter.waitTime = seconds;
return WaitForSeconds.waiter;
}
}
}

View File

@@ -0,0 +1,159 @@
module es {
/**
* CoroutineManager使用的内部类用于隐藏协同程序所需的数据
*/
export class CoroutineImpl implements ICoroutine, IPoolable {
public enumerator: IEnumerator;
/**
* 每当产生延迟时它就被添加到跟踪延迟的waitTimer中
*/
public waitTimer: number;
public isDone: boolean;
public waitForCoroutine: CoroutineImpl;
public useUnscaledDeltaTime: boolean = false;
public stop(){
this.isDone = true;
}
public setUseUnscaledDeltaTime(useUnscaledDeltaTime): es.ICoroutine {
this.useUnscaledDeltaTime = useUnscaledDeltaTime;
return this;
}
public prepareForuse(){
this.isDone = false;
}
public reset() {
this.isDone = true;
this.waitTimer = 0;
this.waitForCoroutine = null;
this.enumerator = null;
this.useUnscaledDeltaTime = false;
}
}
export interface IEnumerator {
current: any;
moveNext(): boolean;
reset();
}
/**
* 基本CoroutineManager。协同程序可以做以下事情:
* - return null(在下一帧继续执行)
* - return Coroutine.waitForSeconds(3)。等待3秒(延时3秒后再次执行)
* - return startCoroutine(another())(在再次执行之前等待另一个协程)
*/
export class CoroutineManager extends GlobalManager {
/**
* 标记来跟踪何时处于更新循环中。
* 如果一个新的协程在更新循环期间启动我们必须将其插入到shouldRunNextFrame列表中以避免在迭代时修改列表。
*/
public _isInUpdate: boolean;
public _unblockedCoroutines: CoroutineImpl[] = [];
public _shouldRunNextFrame: CoroutineImpl[] = [];
/**
* 将i枚举器添加到CoroutineManager。协程在每一帧调用更新之前被执行。
* @param enumerator
*/
public startCoroutine(enumerator: IEnumerator) {
// 查找或创建CoroutineImpl
let coroutine = Pool.obtain<CoroutineImpl>(CoroutineImpl);
coroutine.prepareForuse();
// 设置协程并添加它
coroutine.enumerator = enumerator;
let shouldContinueCoroutine = this.tickCoroutine(coroutine);
// 防止空协程
if (!shouldContinueCoroutine)
return null;
if (this._isInUpdate)
this._shouldRunNextFrame.push(coroutine);
else
this._unblockedCoroutines.push(coroutine);
return coroutine;
}
public update() {
this._isInUpdate = true;
for (let i = 0; i < this._unblockedCoroutines.length; i ++){
let coroutine = this._unblockedCoroutines[i];
// 检查已停止的协程
if (coroutine.isDone){
Pool.free<CoroutineImpl>(coroutine);
continue;
}
// 我们是否在等待其他协程完成
if (coroutine.waitForCoroutine != null){
if (coroutine.waitForCoroutine.isDone){
coroutine.waitForCoroutine = null;
}else{
this._shouldRunNextFrame.push(coroutine);
continue;
}
}
// 如果我们有计时器,就用它
if (coroutine.waitTimer > 0){
// 还有时间。递减并再次运行下一帧确保递减与适当的deltaTime。
coroutine.waitTimer -= coroutine.useUnscaledDeltaTime ? Time.unscaledDeltaTime : Time.deltaTime;
this._shouldRunNextFrame.push(coroutine);
continue;
}
if (this.tickCoroutine(coroutine))
this._shouldRunNextFrame.push(coroutine);
}
this._unblockedCoroutines.length = 0;
this._unblockedCoroutines.concat(this._shouldRunNextFrame);
this._shouldRunNextFrame.length = 0;
this._isInUpdate = false;
}
/**
* 如果协同程序在下一帧继续运行则返回true。此方法将把完成的协程放回池中!
* @param coroutine
*/
public tickCoroutine(coroutine: CoroutineImpl){
// 这个协同程序已经完成了
if (!coroutine.enumerator.moveNext() || coroutine.isDone){
Pool.free<CoroutineImpl>(coroutine);
return false;
}
if (coroutine.enumerator.current == null){
// 再运行下一帧
return true;
}
if (coroutine.enumerator.current instanceof WaitForSeconds){
coroutine.waitTimer = (coroutine.enumerator.current as WaitForSeconds).waitTime;
return true;
}
if (coroutine.enumerator.current instanceof Number){
console.warn("协同程序检查返回一个Number类型请不要在生产环境使用");
coroutine.waitTimer = Number(coroutine.enumerator.current);
return true;
}
if (coroutine.enumerator.current instanceof CoroutineImpl){
coroutine.waitForCoroutine = coroutine.enumerator.current as CoroutineImpl;
return true;
}else {
return true;
}
}
}
}

67
source/src/Utils/Pool.ts Normal file
View File

@@ -0,0 +1,67 @@
module es {
/**
* 用于池任何对象
*/
export class Pool<T> {
private static _objectQueue = new Array(10);
/**
* 预热缓存使用最大的cacheCount对象填充缓存
* @param type
* @param cacheCount
*/
public static warmCache(type: any, cacheCount: number){
cacheCount -= this._objectQueue.length;
if (cacheCount > 0) {
for (let i = 0; i < cacheCount; i++) {
this._objectQueue.unshift(new type());
}
}
}
/**
* 将缓存修剪为cacheCount项目
* @param cacheCount
*/
public static trimCache(cacheCount: number){
while (cacheCount > this._objectQueue.length)
this._objectQueue.shift();
}
/**
* 清除缓存
*/
public static clearCache() {
this._objectQueue.length = 0;
}
/**
* 如果可以的话,从堆栈中弹出一个项
*/
public static obtain<T>(type: any): T {
if (this._objectQueue.length > 0)
return this._objectQueue.shift();
return new type() as T;
}
/**
* 将项推回堆栈
* @param obj
*/
public static free<T>(obj: T) {
this._objectQueue.unshift(obj);
if (egret.is(obj, "IPoolable")){
obj["reset"]();
}
}
}
export interface IPoolable {
/**
* 重置对象以供重用。对象引用应该为空,字段可以设置为默认值
*/
reset();
}
}