/**
*
* @module Kiwi
*
*/
module Kiwi {
/**
* A State in Kiwi.JS is the main class that developers use when wanting to create a Game.
* States in Kiwi are used keep different sections of a game seperated. So a single game maybe comprised of many different States.
* Such as one for the menu, in-game, leaderboard, e.t.c.
* There can only ever be a single State active at a given time.
*
* @class State
* @namespace Kiwi
* @extends Kiwi.Group
* @constructor
* @param name {String} Name of this State. Should be unique to differentiate itself from other States.
* @return {Kiwi.State}
*/
export class State extends Group {
constructor(name: string) {
super(null, name);
this.config = new Kiwi.StateConfig(this, name);
this.components = new Kiwi.ComponentManager(Kiwi.STATE, this);
this.transform.parent = null;
this._trackingList = [];
}
/**
* Returns the type of object this state is.
* @method objType
* @return {String} "State"
* @public
*/
public objType() {
return "State";
}
/**
* Returns the type of child this is.
* @method childType
* @return {Number} Kiwi.GROUP
* @public
*/
public childType() {
return Kiwi.GROUP;
}
/**
* The configuration object for this State.
* @property config
* @type Kiwi.StateConfig
* @public
*/
public config: Kiwi.StateConfig;
/**
* A reference to the Kiwi.Game that this State belongs to.
* @property game
* @type Kiwi.Game
* @public
*/
public game: Kiwi.Game = null;
/**
* The library that this state use's for the loading of textures.
* @property textureLibrary
* @type Kiwi.Textures.TextureLibrary
* @public
*/
public textureLibrary: Kiwi.Textures.TextureLibrary;
/**
* The library that this state use's for the loading of audio.
* @property audioLibrary
* @type Kiwi.Sound.AudioLibrary
* @public
*/
public audioLibrary: Kiwi.Sound.AudioLibrary;
/**
* The library that this state use's for the loading of data.
* @property dataLibrary
* @type Kiwi.Files.DataLibrary
* @public
*/
public dataLibrary: Kiwi.Files.DataLibrary;
/**
* Holds all of the textures that are avaiable to be accessed once this state has been loaded.
* E.g. If you loaded a image and named it 'flower', once everything has loaded you can then access the flower image by saying this.textures.flower
* @property textures
* @type Object
* @public
*/
public textures;
/**
* Holds all of the audio that are avaiable to be accessed once this state has been loaded.
* E.g. If you loaded a piece of audio and named it 'lazerz', once everything has loaded you can then access the lazers (pew pew) by saying this.audio.lazerz
* @property audio
* @type Object
* @public
*/
public audio;
/**
* Holds all of the data that are avaiable to be accessed once this state has been loaded.
* E.g. If you loaded a piece of data and named it 'cookieLocation', once everything has loaded you can then access the cookies by saying this.data.cookieLocation
* @property data
* @type Object
* @public
*/
public data;
/**
* This method is executed when this State is about to be switched too. This is the first method to be executed, and happens before the Init method.
* Is called each time a State is switched to.
* @method boot
* @public
*/
public boot() {
this.textureLibrary = new Kiwi.Textures.TextureLibrary(this.game);
this.textures = this.textureLibrary.textures;
this.audioLibrary = new Kiwi.Sound.AudioLibrary(this.game);
this.audio = this.audioLibrary.audio;
this.dataLibrary = new Kiwi.Files.DataLibrary(this.game);
this.data = this.dataLibrary.data;
}
/*
* Currently unused.
*/
public setType(value: number) {
if (this.config.isInitialised === false) {
this.config.type = value;
}
}
/*
*--------------
* Methods that are to be Over-Ridden by Devs.
*--------------
*/
/**
* Gets executed when the state has been initalised and gets switched to for the first time.
* This method only ever gets called once and it is before the preload method.
* Can have parameters passed to it by the previous state that switched to it.
* @method init
* @param [values] { Any }
* @public
*/
public init(...paramsArr: any[]) { }
/**
* This method is where you would load of all the assets that are requried for this state or in the entire game.
*
* @method preload
* @public
*/
public preload() { }
/**
* This method is progressively called whilst loading files and is executed each time a file has been loaded.
* This can be used to create a 'progress' bar during the loading stage of a game.
* @method loadProgress
* @param percent {Number} The percent of files that have been loaded so far. This is a number from 0 - 1.
* @param bytesLoaded {Number} The number of bytes that have been loaded so far.
* @param file {Kiwi.Files.File} The last file to have been loaded.
* @public
*/
public loadProgress(percent: number, bytesLoaded: number, file: Kiwi.Files.File) { }
/**
* Gets executed when the game is finished loading and it is about to 'create' the state.
* @method loadComplete
* @public
*/
public loadComplete() { }
/**
* The game loop that gets executed while the game is loading.
* @method loadUpdate
* @public
*/
public loadUpdate() {
for (var i = 0; i < this.members.length; i++) {
if (this.members[i].active === true)
{
this.members[i].update();
}
}
}
/**
* Is executed once all of the assets have loaded and the game is ready to be 'created'.
* @method create
* @param [values]* {Any}
* @public
*/
public create(...paramsArr: any[]) { }
/**
* Is called every frame before the update loop. When overriding make sure you include a super call.
* @method preUpdate
* @public
*/
public preUpdate() {
this.components.preUpdate();
}
/**
* The update loop that is executed every frame while the game is 'playing'. When overriding make sure you include a super call too.
* @method update
* @public
*/
public update() {
this.components.update();
for (var i = 0; i < this.members.length; i++) {
//Should the update loop be executed?
if (this.members[i].active === true) {
this.members[i].update();
}
//Does the child need to be destroyed?
if (this.members[i].exists === false) {
this.members[i].destroy( true );
}
}
}
/**
* The post update loop is executed every frame after the update method.
* When overriding make sure you include a super call at the end of the method.
* @method postUpdate
* @public
*/
public postUpdate() {
this.components.postUpdate();
}
/**
* Called after all of the layers have rendered themselves, useful for debugging.
* @method postRender
* @public
*/
public postRender() { }
/**
* Called just before this State is going to be Shut Down and another one is going to be switched too.
* @method shutDown
* @public
*/
public shutDown() { }
/*
*--------------
* Loading Methods
*--------------
*/
/**
* Adds a new image file that is be loaded when the state gets up to the loading all of the assets.
*
* @method addImage
* @param key {String} A key for this image so that you can access it when the loading has finished.
* @param url {String} The location of the image.
* @param [storeAsGlobal=true] {boolean} If the image should be deleted when switching to another state or if the other states should still be able to access this image.
* @param [width] {Number} The width of the image. If not passed the width will be automatically calculated.
* @param [height] {Number} The height of the image. If not passed the height will be automatically calculated.
* @param [offsetX] {Number} The offset of the image when rendering on the x axis.
* @param [offsetY] {Number} The offset of the image when rendering on the y axis.
* @public
*/
public addImage(key: string, url: string, storeAsGlobal: boolean = true, width?: number, height?: number, offsetX?: number, offsetY?: number) {
return this.game.loader.addImage(key, url, width, height, offsetX, offsetY, storeAsGlobal);
}
/**
* Adds a new spritesheet image file that is be loaded when the state gets up to the loading all of the assets.
*
* @method addSpriteSheet
* @param key {String} A key for this image so that you can access it when the loading has finished.
* @param url {String} The location of the image.
* @param frameWidth {Number} The width of a single frame in the spritesheet
* @param frameHeight {Number} The height of a single frame in the spritesheet
* @param [storeAsGlobal=true] {boolean} If the image should be deleted when switching to another state or if the other states should still be able to access this image.
* @param [numCells] {Number} The number of cells/frames that are in the spritesheet. If not specified will calculate this based of the width/height of the image.
* @param [rows] {Number} The number of cells that are in a row. If not specified will calculate this based of the width/height of the image.
* @param [cols] {Number} The number of cells that are in a column. If not specified will calculate this based of the width/height of the image.
* @param [sheetOffsetX=0] {Number} The offset of the whole spritesheet on the x axis.
* @param [sheetOffsetY=0] {Number} The offset of the whole spritesheet on the y axis.
* @param [cellOffsetX=0] {Number} The spacing between cells on the x axis.
* @param [cellOffsetY=0] {Number} The spacing between cells on the y axis.
* @public
*/
public addSpriteSheet(key: string, url: string, frameWidth: number, frameHeight: number, storeAsGlobal: boolean = true, numCells?: number, rows?: number, cols?: number, sheetOffsetX?: number, sheetOffsetY?: number, cellOffsetX?: number, cellOffsetY?: number) {
return this.game.loader.addSpriteSheet(key, url, frameWidth, frameHeight, numCells, rows, cols, sheetOffsetX, sheetOffsetY, cellOffsetX, cellOffsetY, storeAsGlobal);
}
/**
* Adds a new texture atlas that is to be loaded when the states gets up to the stage of loading the assets.
*
* @method addTextureAtlas
* @param key {String} A key for this image so that you can access it when the loading has finished.
* @param imageURL {String} The location of the image.
* @param [jsonID] {String} The id for the json file that is to be loaded. So that you can access it outside of the texture atlas.
* @param [jsonURL] {String} The location of the json file you have loaded.
* @param [storeAsGlobal=true] {boolean} If the image should be delete when switching to another state or if the other states should still be able to access this image.
* @public
*/
public addTextureAtlas(key: string, imageURL: string, jsonID?: string, jsonURL?: string, storeAsGlobal: boolean = true) {
return this.game.loader.addTextureAtlas(key, imageURL, jsonID, jsonURL, storeAsGlobal);
}
/**
* Adds a json file that is to be loaded when the state gets up to the stage of loading the assets.
*
* @method addJSON
* @param key {string} A key for this json so that you can access it when the loading has finished
* @param url {string} The location of the JSON file.
* @param [storeAsGlobal=true] {boolean} If the json should be deleted when switching to another state or if the other states should still be able to access this json.
* @public
*/
public addJSON(key: string, url: string, storeAsGlobal: boolean = true) {
return this.game.loader.addJSON(key, url, storeAsGlobal);
}
/**
* Adds a new audio file that is to be loaded when the state gets up to the stage of loading the assets.
*
* @method addAudio
* @param key {string} A key for this audio so that you can access it when the loading has finished
* @param url {string} The location of the audio file. You can pass a array of urls, in which case the first supported filetype will be used.
* @param [storeAsGlobal=true] {boolean} If the audio should be deleted when switching to another state or if the other states should still be able to access this audio.
*/
public addAudio(key: string, url: any, storeAsGlobal: boolean = true) {
return this.game.loader.addAudio(key, url, storeAsGlobal);
}
/*
*----------------
* Garbage Collection
*----------------
*/
/**
* Contains a reference to all of the Objects that have ever been created for this state. Generally Kiwi.Entities or Kiwi.Groups.
* Useful for keeping track of sprites that are not used any more and need to be destroyed.
* @property trackingList
* @type Array
* @private
*/
private _trackingList: Kiwi.IChild[];
/**
* Adds a new Objects to the tracking list.
* This is an INTERNAL Kiwi method and DEVS shouldn't need to worry about it.
* @method addToTrackingList
* @param child {Object} The Object which you are adding to the tracking list.
* @public
*/
public addToTrackingList(child: Kiwi.IChild) {
//check to see that its not already in the tracking list.
if (this._trackingList.indexOf(child) !== -1) return;
//add to the list
this._trackingList.push(child);
}
/**
* Removes a Object from the tracking list. This should only need to happen when a child is being destroyed.
* This is an INTERNAL Kiwi method and DEVS shouldn't really need to worry about it.
* @method removeFromTrackingList
* @param child {Object} The object which is being removed from the tracking list.
* @public
*/
public removeFromTrackingList(child:Kiwi.IChild) {
//check to see that it is in the tracking list.
var n = this._trackingList.indexOf(child);
if (n > -1) {
this._trackingList.splice(n, 1);
}
}
/**
* Destroys all of Objects in the tracking list that are not currently on stage.
* All that currently don't have this STATE as an ancestor.
* Returns the number of Objects removed.
* @method destroyUnused
* @return {Number} The amount of objects removed.
* @public
*/
public destroyUnused():number {
var d = 0;
for (var i = 0; i < this._trackingList.length; i++) {
if (this.containsAncestor(this._trackingList[i], this) === false) {
this._trackingList[i].destroy();
this._trackingList.splice(i, 1);
i--;
d++;
}
}
return d;
}
/**
* Used to mark all Entities that have been created for deletion, regardless of it they are on the stage or not.
* @method destroy
* @param [deleteAll=true] If all of the Objects ever created should have the destroy method executed also.
* @public
*/
public destroy(deleteAll: boolean=true) {
if (deleteAll == true) {
//destroy all of the tracking list
while( this._trackingList.length > 0 ) {
//If the item is a group then we don't want it to destroy it's children, as this method will do that eventually anyway.
this._trackingList[0].destroy( true , false );
}
this._trackingList = [];
//destroy all of the groups members. Usually they would be apart of the trackingList
while (this.members.length > 0) {
//If the item is a group then we don't want it to destroy it's children, as this method will do that eventually anyway.
this.members[0].destroy(true, false);
}
this.members = [];
}
}
/**
* Recursively goes through a child given and runs the destroy method on all that are passed.
* @method _destroyChildren
* @param child {Object}
* @private
*/
private _destroyChildren(child: any) {
if (child.childType() == Kiwi.GROUP) {
for (var i = 0; i < child.members.length; i++) {
this._destroyChildren(child.members[i]);
}
}
child.destroy( true );
}
}
}