API Docs for: 1.4.0
Show:

File: src\core\Game.ts

/**
* 
* @module Kiwi
* 
*/

module Kiwi {

	/**
	* The base class that is used when you create a new Game. Handles the initialisation of all of the various individual game managers and holds the RAF (RequestAnimationFrame object) which is used for the game loop.
	*
	* @class Game
	* @namespace Kiwi
	* @constructor
	* @param [domParent=''] {String} The ID of a DOM element that the game should use as its 'container'. If you are targeting Cocoon then you don't need to worry about this and can leave it blank.
	* @param [name='KiwiGame'] {String} The name of the game that is being created. 
	* @param [state=null] {Any} The state to load initially. This can either be the name of a state, but preferably this would be the state object itself.
	* @param [options] {Object} Any special options for the game. E.g. Is DEBUG_ON or DEBUG_OFF, RENDERER_CANVAS or RENDERER_WEBGL, TARGET_BROWSER or TARGET_COCOON
	*   @param [options.debug=Kiwi.DEBUG_ON] {Number} If debugging is enabled or not.
	*   @param [options.bootCallback=null] {Function} A callback to be executed when the game reaches the boot stage.
	*   @param [options.deviceTarget=Kiwi.TARGET_BROWSER] {Number} The type of device Kiwi is being used on.
	*   @param [options.renderer=Kiwi.RENDERER_AUTO] {Number} The renderer Kiwi should use. 
	*   @param [options.width=Kiwi.Stage.DEFAULT_WIDTH] {Number} The width of this instance of Kiwi.
	*   @param [options.height=Kiwi.Stage.DEFAULT_HEIGHT] {Number} The height of this instance of Kiwi.
	*   @param [options.scaleType=Kiwi.Stage.SCALE_NONE] {Number} The type of scaling that should be applied to Kiwi.
	*   @param [options.plugins=[]] {Array} A list of the names of plugins that are to be used with this game.
	*   @param [options.log] {Object} Default state of the Log properties
	*       @param [options.log.recording=true] {Boolean} If the logs should be recorded.
	*       @param [options.log.display=true] {Boolean} If the logs should be displayed or not.
	*       @param [options.log.enabled=true] {Boolean} If the Logger is enabled at all.
	*       @param [options.log.maxRecordings=Infinity] {Number} The maximum number of recordings to have at a single time.
	* @return {Kiwi.Game}
	* 
	*/
	export class Game {

		constructor(domParent: string = '', name: string = 'KiwiGame', state: any = null, options: any= {}) {

			Kiwi.Log.setDefaultsFromParams(options.log); 
			Kiwi.Log.log('Kiwi.Game: ' + name + ' is booting using Kiwi.js ' + Kiwi.VERSION, '#version');

			if (Kiwi.DEVICE === null) { 
				Kiwi.DEVICE = new Kiwi.System.Device();
			}

			if (options.debug !== 'undefined' && typeof options.debug === 'number') {
				switch (options.debug) {
					case Kiwi.DEBUG_ON:
						this._debugOption = options.debug;
						Kiwi.Log.log('  Kiwi.Game: Debugging turned ON.', '#debugging' );
						break;
					case Kiwi.DEBUG_OFF:
						this._debugOption = options.debug;
						Kiwi.Log.log('  Kiwi.Game: Debugging turned OFF.', '#debugging' );
						break;
					default:
						this._debugOption = Kiwi.DEBUG_ON;
						Kiwi.Log.error('  Kiwi.Game: Debug option passed, but is not a valid option. Turned ON by default.', '#debugging' );
						break;
				}
			} else {
				this._debugOption = Kiwi.DEBUG_ON;
				Kiwi.Log.log('  Kiwi.Game: Debug option not specified. Turned ON by default.', '#debugging');
			}

			if (options.bootCallback !== 'undefined') {
				this.bootCallbackOption = options.bootCallback;
			}

			//Which device are they targetting
			if (options.deviceTarget !== 'undefined' && typeof options.deviceTarget === 'number') {
				switch (options.deviceTarget) {
					case Kiwi.TARGET_BROWSER:
						this._deviceTargetOption = options.deviceTarget;
						Kiwi.Log.log('  Kiwi.Game: Targeting BROWSERS.', '#target');
						break;
					case Kiwi.TARGET_COCOON:
						this._deviceTargetOption = options.deviceTarget;
						Kiwi.Log.log('  Kiwi.Game: Targeting COCOONJS.', '#target');
						break;
					default:
						this._deviceTargetOption = Kiwi.TARGET_BROWSER;
						Kiwi.Log.error('  Kiwi.Game: Target device specified, but is not a valid option. Defaulting to BROWSER.', '#target');
						break;
				}
			} else {
				this._deviceTargetOption = Kiwi.TARGET_BROWSER;
				Kiwi.Log.log('  Kiwi.Game: Targeted device not specified. Defaulting to BROWSER.', '#target'); 
			}

			var renderDefault = Kiwi.RENDERER_WEBGL;
			var renderFallback = Kiwi.RENDERER_CANVAS;
			// Optimise renderer request
			if (options.renderer !== 'undefined' && typeof options.renderer === 'number') {
				switch (options.renderer) {
					case Kiwi.RENDERER_CANVAS:
						this._renderOption = options.renderer;
						Kiwi.Log.log('  Kiwi.Game: Rendering using CANVAS.', '#renderer', '#canvas');
						break;
					case Kiwi.RENDERER_WEBGL:
						if (Kiwi.DEVICE.webGL) {
							this._renderOption = options.renderer;
							Kiwi.Log.log('  Kiwi.Game: Rendering using WEBGL.', '#renderer', '#webgl');
						} else {
							this._renderOption = renderFallback;
							Kiwi.Log.log('  Kiwi.Game: WEBGL renderer requested but device does not support WEBGL. Rendering using CANVAS.', '#renderer', '#canvas');
						}
						break;
					case Kiwi.RENDERER_AUTO:
						if (Kiwi.DEVICE.webGL) {
							this._renderOption = renderDefault;
							Kiwi.Log.log('  Kiwi.Game: Renderer auto-detected WEBGL.', '#renderer', '#webgl');
						} else {
							this._renderOption = renderFallback;
							Kiwi.Log.log('  Kiwi.Game: Renderer auto-detected CANVAS.', '#renderer', '#canvas');
						}
						break;
					default:
						if (Kiwi.DEVICE.webGL) {
							this._renderOption = renderDefault;
							Kiwi.Log.log('  Kiwi.Game: Renderer specified, but is not a valid option. Defaulting to WEBGL.', '#renderer', '#webgl');
						} else {
							this._renderOption = renderFallback;
							Kiwi.Log.log('  Kiwi.Game: Renderer specified, but is not a valid option. WEBGL renderer sought by default but device does not support WEBGL. Defaulting to CANVAS.', '#renderer', '#canvas');
						}
						break;
				}
			} else {
				if (Kiwi.DEVICE.webGL) {
					this._renderOption = renderDefault;
					Kiwi.Log.log('  Kiwi.Game: Renderer not specified. Defaulting to WEBGL.', '#renderer', '#webgl');
				} else {
					this._renderOption = renderFallback;
					Kiwi.Log.log('  Kiwi.Game: Renderer not specified. WEBGL renderer sought by default but device does not support WEBGL. Defaulting to CANVAS.', '#renderer', '#canvas');
				}
			}


			this.id = Kiwi.GameManager.register(this);
			this._startup = new Kiwi.System.Bootstrap();

			this.audio = new Kiwi.Sound.AudioManager(this);

			this.fileStore = new Kiwi.Files.FileStore(this);
			this.input = new Kiwi.Input.InputManager(this);

			// Width / Height
			var width = Stage.DEFAULT_WIDTH;
			var height = Stage.DEFAULT_HEIGHT;

			if (options.width !== 'undefined' && typeof options.width === 'number') {
				width = options.width;
			}

			if (options.height !== 'undefined' && typeof options.height === 'number') {
				height = options.height;
			}

			Kiwi.Log.log('  Kiwi.Game: Stage Dimensions: ' + width + 'x' + height, '#dimensions');

			
			if ( !Kiwi.Utils.Common.isUndefined( options.scaleType ) ) {

				switch (options.scaleType) {
					case Kiwi.Stage.SCALE_FIT:
						Kiwi.Log.log('  Kiwi.Game: Stage scaling set to FIT.', '#scaling');
						break;
					case Kiwi.Stage.SCALE_STRETCH:
						Kiwi.Log.log('  Kiwi.Game: Stage scaling set to STRETCH.', '#scaling');
						break;
					case Kiwi.Stage.SCALE_NONE:
						Kiwi.Log.log('  Kiwi.Game: Stage scaling set to NONE.', '#scaling');
						break;
					default:
						Kiwi.Log.log('  Kiwi.Game: Stage specified, but is not a valid option. Set to NONE.', '#scaling');
						options.scaleType = 0;
						break;
				}

			} else {
				Kiwi.Log.log('  Kiwi.Game: Stage scaling not specified, defaulting to NONE.', '#scaling');
				options.scaleType = 0;
			}


			this.stage = new Kiwi.Stage(this, name, width, height, options.scaleType);

			this.renderer = null;

			this.cameras = new Kiwi.CameraManager(this);

			if (this._deviceTargetOption !== Kiwi.TARGET_COCOON) this.huds = new Kiwi.HUD.HUDManager(this);
			this.loader = new Kiwi.Files.Loader(this);

			this.states = new Kiwi.StateManager(this);
			this.rnd = new Kiwi.Utils.RandomDataGenerator([Date.now.toString()]);
			this.time = new Kiwi.Time.ClockManager(this);
			this.tweens = new Kiwi.Animations.Tweens.TweenManager(this);

			//  If we have a state then pass it to the StateManager
			if (state !== null) {
				this.states.addState(state, true);
			} else {
				Kiwi.Log.log('  Kiwi.Game: Default State not passed.', '#state');
			}

			this.pluginManager = new Kiwi.PluginManager(this, options.plugins);

			if (this.deviceTargetOption === Kiwi.TARGET_BROWSER) {

				this._startup.boot( domParent, () => this._start() );
			} else {
				if (domParent !== '') {
					Kiwi.Log.log('  Kiwi.Game: Not Targetting a BROWSER. DOM Parent parameter ignored.', '#dom');
				}
				this._start();
			}

		}

		/**
		* The render mode of the game. This will be either set to CANVAS or WEBGL.
		* @property _renderOption
		* @type number
		* @private
		*/
		private _renderOption: number;

		/**
		* Returns the render mode of the game. This is READ ONLY and is decided once the game gets initialised.
		* @property renderOption
		* @type number
		* @public
		*/
		public get renderOption(): number {
			return this._renderOption;
		}

		/**
		* A callback function that can be passed in as an option in the conifugration object. Invoked after the boot process has completed.
		* @property bootCallbackOption
		* @type Function
		* @private
		*/
		public bootCallbackOption: Function;

		/**
		* The type of device that you are targeting. This is either set to COCOON or BROWSER
		* @property _deviceTargetOption
		* @type number
		* @private
		*/
		private _deviceTargetOption: number;

		/**
		* Returns the device target option for the game. This is READ ONLY and is decided once the game gets initialised.
		* @property deviceTargetOption
		* @type number
		* @public
		*/
		public get deviceTargetOption(): number {
			return this._deviceTargetOption;
		}

		/**
		* If when rendering, the game should render a new CANVAS which is above everything. This new canvas is for debugging purposes only.
		* This gets set to either DEBUG_ON or DEBUG_OFF
		* @property _debugOption
		* @type number
		* @private
		*/
		private _debugOption: number;

		/**
		* Returns the debug option. This is READ ONLY and is decided once the game gets initialised.
		* @property debugOption
		* @type number
		* @public
		*/
		public get debugOption(): number {
			return this._debugOption;
		}

		/**
		* Returns true if debug option is set to Kiwi.DEBUG_ON
		* @property debug
		* @type boolean
		* @public
		*/

		public get debug(): boolean {
			return this._debugOption === Kiwi.DEBUG_ON;
		}

		/**
		* Holds the renderer that is being used. This is determined based on the _renderMode
		* @property renderer
		* @type IRenderManager
		* @public
		*/
		public renderer: IRenderManager;

		/**
		* Holds the hud manager.
		* @property huds
		* @type HUDManager
		* @public
		*/
		public huds: Kiwi.HUD.HUDManager;

		/**
		* The type of object that the game is.
		* @method objType
		* @return {String} "Game"
		* @public
		*/
		public objType() {
			return "Game";
		}

		/**
		* The object that peforms DOM and device startup operations for browsers (ie not cocoon)
		* @property _startup
		* @type Kiwi.System.Bootstrap
		* @private
		*/
		private _startup: Kiwi.System.Bootstrap = null;

		/*
		* The unique id for the game. 
		* @property id
		* @type Number
		* @public
		*/
		public id: number;

		/**
		* The audio manager that handles all of the audio in game. Inside you can globally mute the audio, create new sounds, e.t.c.
		* @property audio
		* @type Kiwi.Sound.AudioManager
		* @public
		*/
		public audio: Kiwi.Sound.AudioManager = null;

		/**
		* The global file store for this game. This handles the storage and access of information loaded, as well as tags that maybe set for them individual files.
		* @property fileStore
		* @type Kiwi.Files.FileStore
		* @public
		*/
		public fileStore: Kiwi.Files.FileStore = null;

		/**
		* Handles any user input with the game. These could via the users keyboard, mouse or touch events.
		* @property input
		* @type Kiwi.Input.InputManager
		* @public
		*/
		public input: Kiwi.Input.InputManager = null;

		/**
		* Manages the cameras the are on the stage. Single default Camera only in this version.
		* @property cameras
		* @type Kiwi.CameraManager
		* @public
		*/
		public cameras: Kiwi.CameraManager = null;

		/**
		* Manages plugins registration and initialisation for the game instance.
		* @property pluginManager
		* @type Kiwi.PluginManager
		* @public
		*/
		public pluginManager: Kiwi.PluginManager = null;

		/**
		* Loads files from outside sources and checks to see that they have loaded correctly or not.
		* @property loader
		* @type Kiwi.Files.Loader
		* @public
		*/
		public loader: Kiwi.Files.Loader = null;

		/**
		* The Request Animation Frame that is being used for the update and render loops.
		* @property raf
		* @type Kiwi.Utils.RequestAnimationFrame
		* @public
		*/
		public raf: Kiwi.Utils.RequestAnimationFrame = null;

		/**
		* The ONLY stage that is being used for this game.
		* @property stage
		* @type Stage
		* @public
		*/
		public stage: Kiwi.Stage = null;

		/**
		* Manages all of the states that exist for this game. Via the manager you can create new states, switch states and do various other tasks.
		* @property states
		* @type Kiwi.StateManager
		* @public
		*/
		public states: Kiwi.StateManager = null;

		/**
		* Holds a reference to the clocks that are being used and has a MASTER clock that is being used for the game.
		* @property time
		* @type Kiwi.Time.ClockManager
		* @public
		*/
		public time: Kiwi.Time.ClockManager = null;

		/**
		* The tween manager holds a reference to all of the tweens that are created and currently being used. 
		* @property tweens
		* @type Kiwi.Animations.Tweens.TweenManager
		* @public
		*/
		public tweens: Kiwi.Animations.Tweens.TweenManager = null;

		/**
		* A Random Data Generator. This is useful for create unique ids and random information.
		* @property rnd
		* @type Kiwi.Utils.RandomDataGenerato
		* @public
		*/
		public rnd: Kiwi.Utils.RandomDataGenerator = null;

		/**
		* The framerate at which the game will update at.
		* @property _framerate
		* @type Number
		* @default 60
		* @private
		*/
		private _frameRate: number = 60;

		/**
		* The interval between frames.
		* @property _interval
		* @type Number
		* @default 1000/60
		* @private
		*/
		private _interval: number = 1000 / 60;

		/**
		* The current interval between frames.
		* @property _delta
		* @type number
		* @private
		*/
		private _delta: number = 0;

		/**
		* The last time the game was updated
		* @property _lastTime
		* @type number
		* @private
		*/
		private _lastTime: number;

		/**
		* The number of frames since the game was launched.
		* @property _frame
		* @type number
		* @private
		* @since 1.1.0
		*/
		private _frame: number = 0;

		/**
		* The number of frames since the game was launched.
		* 
		* Use this to drive cyclic animations. You may manually reset it in a Kiwi.State.create() function to restart the count from 0.
		*
		* The largest exact integer value of a JavaScript number is 2^53, or 9007199254740992. At 60 frames per second, this will take 4,760,273 years to become inaccurate.
		* @property frame
		* @type number
		* @public
		* @since 1.1.0
		*/
		public get frame(): number {
			return( this._frame );
		}
		public set frame( value: number ) {
			this._frame = Kiwi.Utils.GameMath.truncate( value );
		}

		/**
		* The number of ideal frames since the game was launched.
		*
		* Use this to drive cyclic animations. This will be smoother than using the frame parameter. It is derived from the total time elapsed since the game launched.
		* @property idealFrame
		* @type number
		* @public
		* @since 1.1.0
		*/
		public get idealFrame(): number {
			return (this.time.elapsed() / (1000 / this._frameRate) );
		}


		/**
		* The current frameRate that the update/render loops are running at. Note that this may not be an  accurate representation.
		* @property frameRate
		* @return string
		* @public
		*/
		public get frameRate(): number {

			return this._frameRate;
		}
		public set frameRate(value: number) {
			if (value > 0) {
				this._frameRate = value;
				this._interval = 1000 / this._frameRate;
				this.time.setMasterInterval( this._interval );
			}
		}

		/**
		* The start method gets executed when the game is ready to be booted, and handles the start-up of the managers.
		* Once the managers have started up the start loop will then begin to create the game loop.
		* @method start
		* @private
		*/
		private _start() {


			this.stage.boot(this._startup);

			if (!this.stage.renderer) Kiwi.Log.error( "  Kiwi.Game: Could not create rendering context", '#renderer' );
			if(this._renderOption === Kiwi.RENDERER_WEBGL  &&  this.stage.ctx)
				this._renderOption = Kiwi.RENDERER_CANVAS;  // Adapt to fallback if WebGL failed
			this.renderer = this.stage.renderer;
			this.renderer.boot();

			this.cameras.boot();
			if (this._deviceTargetOption !== Kiwi.TARGET_COCOON) this.huds.boot();

			this.time.boot();
			this.input.boot();
			this.audio.boot();

			this.fileStore.boot();
			this.loader.boot();
			this.states.boot();

			this.pluginManager.boot();

			this._lastTime = Date.now();

			this.raf = new Kiwi.Utils.RequestAnimationFrame( () => this._loop() );
			this.raf.start();

			if (this.bootCallbackOption) {
				Kiwi.Log.log("  Kiwi.Game: invoked boot callback", '#boot');
				this.bootCallbackOption( this );
			}
		}

		/**
		* The game loop. 
		* @method _loop
		* @private
		*/
		private _loop() {

			// Only update non-graphical game systems if a full frame
			// has passed
			this._delta = this.raf.currentTime - this._lastTime;
			if (this._delta > this._interval) {

				this.time.update();
				this.audio.update();
				this.input.update();
				this.tweens.update();
				this.cameras.update();
				if (this._deviceTargetOption !== Kiwi.TARGET_COCOON) {
					this.huds.update();
				}
				this.states.update();
				this.pluginManager.update();
				this._frame++;

				this._lastTime =
					this.raf.currentTime - (this._delta % this._interval);
			}

			// Graphics MUST be drawn every frame to avert frame buffer issues
			// under some clients
			if (this.states.current !== null) {

				// Only update if there is a current state
				this.cameras.render();
				this.states.postRender();
			}
		}

	}

}