|
@@ -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);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|