xx 1 year ago
parent
commit
4155aaa0dd
1 changed files with 196 additions and 0 deletions
  1. 196 0
      assets/script/run/lib/Coroutine.ts

+ 196 - 0
assets/script/run/lib/Coroutine.ts

@@ -0,0 +1,196 @@
+
+import { Component, Director, director, isValid } from 'cc';
+declare type GeneratorFunc = (...args: any[]) => Generator<any, any, any>
+declare type GeneratorRecord = { generator: Generator<any, any, any>, gfunc?: GeneratorFunc, comp?: Component, waiter?: IWaiter };
+
+interface IWaiter {
+    isOver(dt: number): boolean;
+}
+
+class WaitForSecond implements IWaiter {
+    private _endTime: number = 0;
+    private _currTime: number = 0;
+
+    constructor(endTime: number) {
+        this._endTime = endTime;
+    }
+
+    isOver(dt: number) {
+        this._currTime += dt;
+
+        return this._currTime >= this._endTime;
+    }
+}
+
+class WaitForFrame implements IWaiter {
+    private _frameCount: number = 0;
+    private _frame: number = 0;
+    constructor(frameCount: number) {
+        this._frameCount = frameCount;
+    }
+
+    isOver(dt: number) {
+        return this._frame++ >= this._frameCount;
+    }
+}
+
+export default class Coroutine {
+
+    private static _instance: Coroutine = null;
+    static get instance() {
+        if (Coroutine._instance == null) {
+            Coroutine._instance = new Coroutine();
+        }
+        return Coroutine._instance;
+    }
+
+    /**等待多少秒 */
+    static createWaitForSecond(sec: number) {
+        return new WaitForSecond(sec);
+    }
+    /**等待多少帧 */
+    static createWaitForFrame(frameCount: number) {
+        return new WaitForFrame(frameCount);
+    }
+
+    private _mapCoroutine: Map<any, GeneratorRecord[]> = new Map();
+    private _getTimmer: () => number = null;
+    private _lastTime: number = 0;
+
+    private constructor() {
+        if (typeof window?.performance?.now !== 'function') {
+            this._getTimmer = () => {
+                return new Date().getTime();
+            }
+        } else {
+            this._lastTime = window.performance.now();
+            this._getTimmer = () => window.performance.now();
+        }
+    }
+
+    /**@summary start coroutiine
+     * @param component cc component
+     * @param func generator func or string
+     * @param args your funcion params
+    */
+    start(component: Component, func: GeneratorFunc | string, ...args: any[]) {
+        if (!(component instanceof Component)) {
+            throw Error('component must a component');
+        }
+        const mapData: GeneratorRecord = {
+            generator: null,
+            comp: component
+        };
+        if (typeof func === 'string') {
+            const gf: GeneratorFunc = (<any>component)[func];
+            const generator = gf.call(component, ...args);
+            if (typeof generator?.next === 'function') {
+                mapData.generator = generator;
+                mapData.gfunc = (<any>component)[func];
+            } else {
+                throw Error(func + ' is not a GeneratorFunction !!!');
+            }
+        } else {
+            mapData.gfunc = func;
+            mapData.generator = func.call(component, ...args);
+        }
+        const _processCount = this._mapCoroutine.size;
+        if (mapData.generator) {
+            const list = this._mapCoroutine.get(component);
+            if (!list) {
+                this._mapCoroutine.set(component, [mapData]);
+            } else {
+                list.push(mapData);
+            }
+        }
+        if (_processCount == 0 && this._mapCoroutine.size > 0) {
+            this._lastTime = this._getTimmer();
+            director.on(Director.EVENT_AFTER_UPDATE, this._update, this);
+        }
+    }
+
+    /**@summary stop coroutiine
+     * @param component cc component
+     * @param func if no input, that will stop all coroutine of component;
+    */
+    stop(component: Component, func?: GeneratorFunc | string) {
+        if (!func) {
+            this._mapCoroutine.delete(component);
+        } else {
+            const list = this._mapCoroutine.get(component);
+            if (!list) return;
+            let deleteIds: number[] = [];
+            for (let i = 0; i < list.length; ++i) {
+                if (!list[i]) {
+                    list[i] = null;
+                    deleteIds.push(i);
+                }
+                else if (typeof func === 'string' && list[i].gfunc === (<any>component)[func]) {
+                    list[i] = null;
+                    deleteIds.push(i);
+                } else if (list[i].gfunc == func) {
+                    list[i] = null;
+                    deleteIds.push(i);
+                }
+            }
+            if (deleteIds.length >= list.length) {
+                this._mapCoroutine.delete(component);
+                return;
+            } else if (deleteIds.length >= 2) {
+                this._mapCoroutine.set(component, list.filter((e) => !!e));
+            } else if (deleteIds.length == 1) {
+                list.splice(deleteIds[0], 1);
+            }
+        }
+
+
+    }
+
+    private _update() {
+        const dt = (this._getTimmer() - this._lastTime) / 1000;
+        this._mapCoroutine.forEach((gr, k) => {
+            let deleteIds: number[] = [];
+            let count = gr.length;
+            for (let i = 0; i < count; ++i) {
+                if (!(gr && isValid(gr[i]?.comp?.node, true))) {
+                    deleteIds.push(i);
+                    gr[i] = null;
+                } else {
+                    if (gr[i].waiter) {
+                        if (!gr[i].waiter.isOver(dt)) {
+                            continue;
+                        }
+                    }
+                    const it = gr[i].generator.next();
+                    if (it.value instanceof WaitForSecond) {
+                        if (!it.value.isOver(dt)) {
+                            gr[i].waiter = it.value;
+                            continue;
+                        }
+                    } else if (it.value instanceof WaitForFrame) {
+                        gr[i].waiter = it.value;
+                        if (!it.value.isOver(dt)) {
+                            gr[i].waiter = it.value;
+                            continue;
+                        }
+                    }
+                    if (it.done) {
+                        deleteIds.push(i);
+                        gr[i] = null;
+                    }
+                }
+            }
+            if (deleteIds.length >= gr.length) {
+                this._mapCoroutine.delete(k);
+                return;
+            } else if (deleteIds.length >= 2) {
+                this._mapCoroutine.set(k, gr.filter((e) => !!e));
+            } else if (deleteIds.length == 1) {
+                gr.splice(deleteIds[0], 1);
+            }
+        });
+        this._lastTime = this._getTimmer();
+        this._mapCoroutine.size == 0 && director.off(Director.EVENT_AFTER_UPDATE, this._update, this);
+    }
+}
+