Source: components/AppTimerComponent.js

import { Utils } from '../common/Utils';
import { FPSTimer } from '../common/FPSTimer';
import { BaseComponent } from './BaseComponent';

const __ = {
	private: Symbol('private'),
}

/**
 * @class AppTimerComponent
 * The application timer component.
 * @memberof THING
 * @extends THING.BaseComponent
 */
class AppTimerComponent extends BaseComponent {

	/**
	 * The timer controller for application.
	 */
	constructor() {
		super();

		this[__.private] = {};
		let _private = this[__.private];

		// Delta time
		_private.frameRate = 0;
		_private.lastFrame = 0;
		_private.startTime = 0;

		if (_NEED_LOGGER) {
			_private.loggerTime = 0;
		}

		// Initialize FPS timer
		_private.fpsTimer = new FPSTimer();

		// Callback functions
		_private.onTick = null;
	}

	// #region Private

	/**
	 * Get fixed delta time in milliseconds.
	 * @param {Number} elapsedTime The elapsed time in milliseconds.
	 * @returns {Number}
	 * @private
	 */
	_getDeltaTime(elapsedTime) {
		let _private = this[__.private];

		let deltaTime = 0;
		if (_private.startTime === 0) {
			_private.startTime = elapsedTime;
			deltaTime = elapsedTime;
		}
		else {
			const currentFrame = Math.round((elapsedTime - _private.startTime) / _private.frameRate);
			deltaTime = (currentFrame - _private.lastFrame) * _private.frameRate;
			_private.lastFrame = currentFrame;
		}

		return deltaTime;
	}

	_updateLogger(deltaTime) {
		let app = this.object;

		let _private = this[__.private];
		_private.loggerTime += deltaTime;
		if (_private.loggerTime > 30) {
			let objectManager = app.objectManager;
			let msg = {
				"common": {
					'tickableObjects': objectManager.tickableObjects.length,
					'lateUpdateObjects': objectManager.lateUpdateObjects.length,
					'objects': objectManager.objects.length,
				},
				"render": {
					"fps": this.fpsCounter,
				},
			}

			Utils.logger.api("frame", { msg: JSON.stringify(msg) });

			let collector = Utils.logger.getApiCollector();
			if (collector) {
				let count = collector.getApiCount();
				Utils.logger.api("apiCount", { msg: JSON.stringify(count) });
				collector.clearApiCount();
			}

			_private.loggerTime = 0;
		}
	}

	// #endregion

	// #region BaseComponent Interface

	onAdd(object) {
		super.onAdd(object);
	}

	onRemove() {
		let _private = this[__.private];

		_private.fpsTimer = null;
		_private.onTick = null;

		super.onRemove();
	}

	// #endregion

	init(options = {}) {
		let _private = this[__.private];

		// Delta time
		let frameRate = Utils.parseValue(options['frameRate'], 60);
		_private.frameRate = 1000 / frameRate;
		_private.lastFrame = 0;
		_private.startTime = 0;

		if (_NEED_LOGGER) {
			_private.loggerTime = 0;
		}

		_private.onTick = options['onTick'];
	}

	update(elapsedTime) {
		let _private = this[__.private];

		// Get the delta time by elapsed time in milliseconds
		let deltaTime = this._getDeltaTime(elapsedTime);

		// Convert delta time from milliseconds to seconds to update fps timer
		deltaTime = _private.fpsTimer.update(deltaTime / 1000);
		if (!deltaTime) {
			return;
		}

		_private.onTick(deltaTime);

		if (_NEED_LOGGER) {
			this._updateLogger(deltaTime);
		}
	}

	/**
	 * Get the elapsed time(seconds) when started.
	 * @type {Number}
	 * @example
	 * 	let elapsedTime = THING.App.current.elapsedTime;
	 * 	console.log(elapsedTime);
	 */
	get elapsedTime() {
		return this[__.private].fpsTimer.elapsedTime;
	}

	/**
	 * Get the delta time(seconds) of previous frame.
	 * @type {Number}
	 * @example
	 * 	let deltaTime = THING.App.current.deltaTime;
	 * 	console.log(deltaTime);
	 */
	get deltaTime() {
		return this[__.private].fpsTimer.deltaTime;
	}

	/**
	 * Get the current total frame count after started.
	 * @type {Number}
	 * @example
	 * 	let currentFrameCount = THING.App.current.currentFrameCount;
	 * 	console.log(currentFrameCount);
	 */
	get currentFrameCount() {
		return this[__.private].fpsTimer.currentFrameCount;
	}

	/**
	 * Get/Set fixed delta time in seconds.
	 * @type {Number}
	 * @private
	 */
	get fixedDeltaTime() {
		return this[__.private].fpsTimer.fixedDeltaTime;
	}
	set fixedDeltaTime(value) {
		this[__.private].fpsTimer.fixedDeltaTime = value;
	}

	/**
	 * Get/Set the max FPS number, null indicates unlimited.
	 * @type {Number}
	 * @example
	 * 	// Limit max render FPS to 1
	 * 	THING.App.current.maxFPS = 1;
	 * 	// Unlimit max render FPS
	 * 	THING.App.current.maxFPS = null;
	 */
	get maxFPS() {
		return this[__.private].fpsTimer.maxFPS;
	}
	set maxFPS(value) {
		this[__.private].fpsTimer.maxFPS = value;
	}

	/**
	 * Enable/Disable slow motion.
	 * @type {Boolean}
	 * @private
	 */
	get slowMotion() {
		return this.fixedDeltaTime === 1 / 1000;
	}
	set slowMotion(value) {
		if (value) {
			this.fixedDeltaTime = 1 / 1000;
		}
		else {
			this.fixedDeltaTime = null;
		}
	}

	/**
	 * Get the FPS Counter.
	 * @type {Number}
	 * @example
	 * 	let fpsCounter = THING.App.current.fpsCounter;
	 * 	console.log(fpsCounter);
	 */
	get fpsCounter() {
		let _private = this[__.private];

		return _private.fpsTimer.fpsCounter;
	}

}

AppTimerComponent.exportProperties = [
	'elapsedTime',
	'deltaTime',
	'currentFrameCount',
	'maxFPS',
	'fpsCounter'
];

export { AppTimerComponent }