//=====Asset object=====
/**
This class is used as a wrapper for all the assets loaded by and retrieved from the asset manager. The constructor is meant for internal use only use, but it's available in case type checking needs to be performed.
@class AssetHandle
@constructor
*/
function AssetHandle(path, asset){
this._path = path;
this._asset = asset;
this._referenceCount = 1;
}
/**
@method getPath
@return {String} The asset's path. It's used to fetch the asset and to reference it within the asset manager.
*/
AssetHandle.prototype.getPath = function(){
return this._path;
};
/**
@method getAsset
@return Returns the asset object. The object type depends on the type of asset.
*/
AssetHandle.prototype.getAsset = function(){
return this._asset;
};
//=====Manager object=====
/**
This class is responsible for loading, unloading and managing the life time of assets. It uses reference counting to ensure that only one instance of a particular asset is loaded at a time.
The asset manager will trigger events. "assetLoaded" event is triggered when an asset is downloaded, this event doesn't trigger if an asset's reference count is incremented. "assetUnloaded" event is triggered when an asset's reference count drops to 0 and thus gets unloaded.
"errorLoadingAsset" event is triggered when an asset could not be loaded for any reason. "finishedLoading" event is triggered when all asset requests passed in the load function have been processed, for more information see the load function.
@class AssetManager
@constructor
@extends EventHandler
*/
function AssetManager(){
//Inherit from the event handling object.
EventHandler.call(this);
var thisAssetManager = this,
/**
This is the object that the manager uses to download assets. The manager sets default image and audio types. If those happen to be insufficient, they need to be changed directly in the file.
@property assetDownloader
@type AssetDownloader
*/
assetDownloader = new AssetDownloader(),
assets = {},
assetsBeingLoaded = new LinkedList(),
assetsWaitingToBeLoaded = {},
//Names of events triggered by the manager.
finishedLoading = 'finishedLoading',
assetLoaded = 'assetLoaded',
errorLoadingAsset = 'errorLoadingAsset',
assetUnloaded = 'assetUnloaded';
assetDownloader.setImageTypes('png');
assetDownloader.setAudioTypes(['mp3', 'audio/mpeg'],['ogg', 'audio/ogg']);
//Functionality for assigning an id to load requests.
var nextAvailableLoadId = 0;
function requestLoadId(){
return nextAvailableLoadId++;
}
/**
@method load
@param assetPath* {String|Array} An array of strings or one or more strings representing paths of assets to be loaded.
@return {Number} A unique id for this load request. Once the loading is finished, a "finishedLoading" event will be triggered. To identify which load request it corresponds to, the event data will be this id.
@example
assetManager.load('music.mp3', 'sprite.png');
@example
var array = ['music.mp3', 'sprite.png'];
assetManager.load(array);
*/
this.load = function(/*assetPath, assetPath, ... or [assetPath, assetPath, ...]*/){
var assetPathArray = Array.isArray(arguments[0]) ? arguments[0] : arguments,
loadedCount = 0,
totalCount = assetPathArray.length,
loadId = requestLoadId();
function assetLoaded(){
loadedCount++;
if(loadedCount === totalCount){
thisAssetManager.trigger(finishedLoading, loadId);
}
}
for(var i=0; i<assetPathArray.length; ++i){
loadAsset(assetPathArray[i], assetLoaded);
}
return loadId;
};
/**
@method get
@param assetPath {String} Path of the asset to be retrieved.
@return {AssetHandle} The handle for the requested asset.
*/
this.get = function(assetPath){
return assets[assetPath];
};
/**
@method unload
@param assetPath* {String or Array} An array of strings or one or more strings representing paths of assets to be unloaded.
@example
assetManager.unload('music.mp3', 'sprite.png');
@example
var array = ['music.mp3', 'sprite.png'];
assetManager.unload(array);
*/
this.unload = function(/*assetPath, assetPath, ... or [assetPath, assetPath, ...]*/){
var assetPathArray = Array.isArray(arguments[0]) ? arguments[0] : arguments;
for(var i=0; i<assetPathArray.length; ++i){
if(assetPathArray[i] in assets){
unloadAsset(assets[assetPathArray[i]]);
}
}
};
function loadAsset(assetPath, loadedCallback){
function assetDownloaded(asset){
var queue = assetsWaitingToBeLoaded[assetPath];
if(asset){
var assetHandle = new AssetHandle(assetPath, asset);
assets[assetPath] = assetHandle;
assetsBeingLoaded.removeValue(assetPath);
thisAssetManager.trigger(assetLoaded, assetPath);
loadedCallback(assetHandle);
//If the request queue is not empty, then for each request
//increment the reference counter and call the request callback with the asset handle.
if(queue){
for(var i=0; i<queue.length; ++i){
assetHandle._referenceCount++;
queue[i](assetHandle);
}
//Remove the queue.
delete assetsWaitingToBeLoaded[assetPath];
}
}else{
assetsBeingLoaded.removeValue(assetPath);
thisAssetManager.trigger(errorLoadingAsset, assetPath);
loadedCallback(null);
//If the request queue is not empty, then for each request call back with null.
if(queue){
for(var i=0; i<queue.length; ++i){
queue[i](null);
}
//Remove the queue.
delete assetsWaitingToBeLoaded[assetPath];
}
}
}
if(assetPath in assets){
//This asset was loaded already.
assets[assetPath]._referenceCount++;
loadedCallback(assets[assetPath]);
}else if(assetsBeingLoaded.indexOf(assetPath) > -1){
//This asset is being loaded.
//Queue the request to be processed once that's finished.
if(!assetsWaitingToBeLoaded[assetPath]){
assetsWaitingToBeLoaded[assetPath] = [];
}
assetsWaitingToBeLoaded[assetPath].push(loadedCallback);
}else{
//Mark the asset as being loaded, then load it.
assetsBeingLoaded.insert(0, assetPath);
switch( getFileExtension(assetPath) ){
case 'spritesheet' :
downloadSpriteSheet(assetPath, assetDownloaded);
break;
case 'tilelayer' :
downloadTileLayer(assetPath, assetDownloaded);
break;
case 'animation' :
downloadAnimation(assetPath, assetDownloaded);
break;
default:
assetDownloader.download(assetPath, assetDownloaded);
}
}
}
function unloadAsset(assetHandle){
assetHandle._referenceCount--;
if(assetHandle._referenceCount <= 0){
switch( getFileExtension(assetHandle.getPath()) ){
case 'spritesheet' :
freeSpriteSheet(assetHandle);
break;
case 'tilelayer' :
freeTileLayer(assetHandle);
break;
case 'animation' :
freeAnimation(assetHandle);
break;
default:
delete assets[assetHandle.getPath()];
}
thisAssetManager.trigger(assetUnloaded, assetHandle.getPath());
}
}
//=====START composite file functions=====
function downloadSpriteSheet(spriteSheetPath, downloadedCallback){
function spriteSheetDownloaded(spriteSheet){
function imageLoaded(imageHandle){
//Check if the sprite sheet image was loaded successfuly.
if(imageHandle){
//Set the sprite sheet image to the image that was loaded.
spriteSheetObject.spriteSheetImageHandle = imageHandle;
//Return the successfuly created sprite sheet.
downloadedCallback(spriteSheetObject);
}else{
downloadedCallback(null);
}
}
var spriteSheetObject;
//Check if the download was successful
if(spriteSheet){
//Create the sprite sheet from the JSON file.
spriteSheetObject = JSON.parse(spriteSheet);
//Load the sprite sheet image.
loadAsset(spriteSheetObject.spriteSheetImageHandle, imageLoaded);
}else{
downloadedCallback(null);
}
}
//First download the JSON file for the sprite sheet.
assetDownloader.download(spriteSheetPath, spriteSheetDownloaded);
}
function freeSpriteSheet(spriteSheetHandle){
//Unload the sprite sheet image first.
unloadAsset(spriteSheetHandle.getAsset().spriteSheetImageHandle);
//Remove the sprite sheet from assets.
delete assets[spriteSheetHandle.getPath()];
}
function downloadTileLayer(tileLayerPath, downloadedCallback){
function tileLayerDownloaded(tileLayer){
function spriteSheetLoaded(spriteSheetHandle){
//Check if the sprite sheet was loaded successfuly.
if(spriteSheetHandle){
//Set the layer's sprite sheet to the one that was loaded.
tileLayerObject.spriteSheetHandle = spriteSheetHandle;
//Return the successfuly created tile layer.
downloadedCallback(tileLayerObject);
}else{
downloadedCallback(null);
}
}
var tileLayerObject;
//Check if the download was successful
if(tileLayer){
//Create the tile layer from the JSON file.
tileLayerObject = JSON.parse(tileLayer);
//Load the sprite sheet.
loadAsset(tileLayerObject.spriteSheetHandle, spriteSheetLoaded);
}else{
downloadedCallback(null);
}
}
//First download the JSON file for the tile layer.
assetDownloader.download(tileLayerPath, tileLayerDownloaded);
}
function freeTileLayer(tileLayerHandle){
//Unload the sprite sheet first.
unloadAsset(tileLayerHandle.getAsset().spriteSheetHandle);
//Remove the tile layer from assets.
delete assets[tileLayerHandle.getPath()];
}
function downloadAnimation(animationPath, downloadedCallback){
function animationDownloaded(animation){
function spriteSheetLoaded(spriteSheetHandle){
//Check if the sprite sheet was loaded successfuly.
if(spriteSheetHandle){
//Set the animation's sprite sheet to the one that was loaded.
animationObject.spriteSheetHandle = spriteSheetHandle;
//Calculate the total animation time and store it on the animation object.
animationObject.totalDuration = 0;
for(var i=0; i<animationObject.frames.length; ++i){
animationObject.totalDuration += animationObject.frames[i].frameDuration;
}
//Return the successfuly created animation.
downloadedCallback(animationObject);
}else{
downloadedCallback(null);
}
}
var animationObject;
//Check if the download was successful
if(animation){
//Create the animation from the JSON file.
animationObject = JSON.parse(animation);
//Load the sprite sheet.
loadAsset(animationObject.spriteSheetHandle, spriteSheetLoaded);
}else{
downloadedCallback(null);
}
}
//First download the JSON file for the animation.
assetDownloader.download(animationPath, animationDownloaded);
}
function freeAnimation(animationHandle){
//Unload the sprite sheet first.
unloadAsset(animationHandle.getAsset().spriteSheetHandle);
//Remove the animation from assets.
delete assets[animationHandle.getPath()];
}
//=====END composite file functions=====
function getFileExtension(filePath){
var splitFilePath = filePath.split('.');
return splitFilePath[ splitFilePath.length -1 ].toLowerCase();
}
}
//Inherit from the event handling object
AssetManager.prototype = Object.create(EventHandler.prototype);
AssetManager.prototype.constructor = AssetManager;