经过一些对rxjs的研究,搞出了一个简单但是还不完整的解决方法。
可以让使用这个scheduler的timer使用ccc的deltaTime来计时,这样就可以动态暂停回复以及变速了。
// GameplayScheduler.ts
import { SchedulerAction, SchedulerLike, Subscription } from "rxjs";
const {ccclass, property} = cc._decorator;
class MAction<T> extends Subscription implements SchedulerAction<T>{
constructor(
public sche: GameplayScheduler,
public work: (this: SchedulerAction<T>, state: T) => void,
public delay: number,
public state: T
)
{
super();
}
schedule(state?: T, delay?: number): Subscription{
this.delay = delay + this.sche.now();
this.state = state;
this.sche.reschedule(this);
return this;
}
}
// use scaled deltatime to fire scheduled works
export class GameplayScheduler implements SchedulerLike{
private _now : number = 0;
private _actions : MAction<any>[] = [];
now(): number { return this._now; }
schedule<T>(work: (this: SchedulerAction<T>, state: T) => void, delay: number, state: T): Subscription{
const exec_time = delay + this.now();
const ix = this._find_insert_idx(exec_time);
if ( !( 0 <= ix && ix <= this._actions.length ) ) cc.error("wrong ix: ", ix);
const sub = new MAction(this, work, exec_time, state);
this._actions.splice( ix, 0, sub );
return sub;
}
reschedule<T>(act: MAction<T>){
const ix = this._find_insert_idx(act.delay);
this._actions.splice(ix, 0, act);
}
update(dt:number){
this._now += dt * 1000;
const { _actions } = this;
while( _actions.length > 0 && _actions[0].delay <= this._now ){
const act = _actions.shift();
act.work(act.state);
}
}
private _find_insert_idx(delay:number): number{
let [L, R] = [0, this._actions.length];
while (L < R){
let m = Math.floor((L+R) / 2);
const elem = this._actions[m];
if ( elem.delay <= delay ){ L = m+1; }
else { R = m; }
}
return L;
}
}
// SchedulerCtrl.ts
import { GameplayScheduler } from "./GameplayScheduler";
import { timer, Subscription } from 'rxjs';
const { ccclass, property } = cc._decorator;
@ccclass
export class SchedulerCtrl extends cc.Component{
@property(cc.Node)
btn_start : cc.Node = null;
@property(cc.Node)
btn_stop : cc.Node = null;
private _sche : GameplayScheduler = null;
private _ob : Subscription = null;
protected onLoad(): void {
this._sche = new GameplayScheduler();
}
start(){
this.btn_start.on('click', () => {
// this._ob = timer(1000, 1000).subscribe( x => cc.log(x)); // default scheduler
this._ob = timer(1000, 1000, this._sche).subscribe( x => cc.log(x)); //gameplay scheduler
});
this.btn_stop.on('click', () => {
if (this._ob == null) return;
this._ob.unsubscribe();
this._ob = null;
});
}
update(dt: number){
this._sche.update(dt);
}
}