import { Model } from "@Model/Model"; import { Emitter } from "@Model/Emitter"; import { Clip } from "@Model/Clip"; enum ActuatorModel { Play = 1, Record = 2, View = 3, Offline = 4 } interface IActuatorEvent { startChange: boolean; loop: number; } /** * 模型执行器 */ class Actuator extends Emitter { /** * 速度系数 */ public speed: number = 1; /** * 模拟帧率 */ public fps: number = 36; /** * 仿真是否进行 */ private startFlag: boolean = false; /** * 模式 */ public mod: ActuatorModel = ActuatorModel.View; /** * 录制剪辑 */ public recordClip?: Clip; /** * 开始录制 */ public startRecord(clip: Clip) { // 记录录制片段 this.recordClip = clip; // 如果仿真未开启,开启仿真 if (!this.start()) this.start(true); // 设置状态 this.mod = ActuatorModel.Record; } /** * 结束录制 */ public endRecord() { // 如果仿真未停止,停止仿真 if (this.start()) this.start(false); // 设置状态 this.mod = ActuatorModel.View; } /** * 主时钟状态控制 */ public start(start?: boolean): boolean { if (start === undefined) { return this.startFlag; } else { this.startFlag = start; this.lastTime = 0; this.alignTimer = 0; this.emit("startChange", start); return start; } } /** * 绑定模型 */ public model: Model; /** * 上一帧的时间 */ private lastTime: number = 0; /** * 对其计时器 */ private alignTimer: number = 0; public tickerType: 1 | 2 = 2; private ticker(t: number) { if (this.startFlag && t !== 0) { if (this.lastTime === 0) { this.lastTime = t; } else { let durTime = (t - this.lastTime) / 1000; this.lastTime = t; // 丢帧判定 if (durTime > 0.1) { console.log("Actuator: Ticker dur time error. dropping...") } else { this.alignTimer += durTime; if (this.alignTimer > (1 / this.fps)) { // 更新模型 this.model.update(this.alignTimer * this.speed); // 绘制模型 this.model.draw(); // 录制模型 if ( this.mod === ActuatorModel.Record || this.mod === ActuatorModel.Offline ) { this.recordClip?.record(this.alignTimer * this.speed); } this.emit("loop", this.alignTimer); this.alignTimer = 0; } } } } else { this.emit("loop", Infinity); } } /** * 帧率对其时钟 * 1、使用 requestAnimationFrame 保证最高的性能 * 2、最大模拟帧率只能小于 60 * 3、可能出现帧率对其问题 */ private tickerAlign = (t: number) => { this.ticker(t); requestAnimationFrame(this.tickerAlign); } /** * 精确时钟 */ private tickerExp = () => { this.ticker(window.performance.now()); setTimeout(this.tickerExp, (1 / this.fps) * 1000); } /** * 执行器 */ private runTicker = (t: number) => { if (this.tickerType === 1) { this.ticker(t); requestAnimationFrame(this.runTicker); } else { this.ticker(window.performance.now()); setTimeout(this.runTicker, (1 / this.fps) * 1000); } } public constructor(model: Model) { super(); this.model = model; this.runTicker(0); } } export { Actuator, ActuatorModel }