Chris Posted April 3, 2013 Share Posted April 3, 2013 Hi,I am currently (or better: since the last weeks) making up my mind about how to build a asset loader that makes sense to me. The problem I have with most asset loaders is, that they act very much like assets are loaded from a harddrive, instead from the web.They further have the problem that they do not really care about the order and relation of the assets you are trying to load.The most basic ones just get passed a list of files to download and you can access that file contents through keys. While the basic behaviour of these loaders are fine to me, I think they are missing a great number of things: The game might not require ALL assets to get started. Its entirely fine to get a game started and not rendering all decorations and/or particles right from the beginning. Some of you might disagree, but I as a player would be totally okay that some eye-candy appears later during my game progress but I don't have to wait for minutes until I get to play the game. Assets might be required in different resolutions. Right now, we have a landscape of mobile and desktop systems which offer a wide range of display resolutions. The last generations of displays offer resolutions with over 300px per inch which requires games to load assets with nearly the double resolution to appear sharp while being rendered at the same size.This comes at the cost of performance, true - but I think in the future when mobile processing performance increases we don't have to think very much about that. Let our games look great! I think assets should be organized in groups (or "packages"). You have a asset group for your UI, one with basic game elements and maybe one asset group with special assets per game level.Organizing them in groups enables the developer to have a much better overview and being able to re-use assets more easily. I know its hard to detect if a user is currently on a mobile network over 3G, or in a WiFi environment from our JavaScript/Browser context, but bigger games have options/settings, so why shouldn't html5 games offer some graphical settings? Let the user decide if he wants to play with low-resolution assets because he's currently in a train with a slower connection, or let him load the full highRes beauty when he hasa strong connection. The way my loader is (or will be) built, its very easy to switch asset resolutions without any impact on the game code itself. Below you can see my current specs I wrote down to see if my API idea makes any sense to me. I would love to discuss my idea with you and include your thoughts and opinions into the creation process. Have fun with reading my specs, just ask if you have any questions or anything is unclear Edit: If you don't like the boards code formatting, I uploaded the specs as a gist.//Cargo Asset Manager//===================//This file defines the API for a HTML5 asset loader that is mainly designed to be used for games//but could be used for any kind of HTML5 application that requires some kind of asset management.//@version: 1 (April 2nd, 2013)//@author: Christian Engel <[email protected]>//The Cargo asset loader should deliver the following number of features:// * Loading all needed assets (huh, obvious)// * Differenciating between mandatory and optional assets// * Making it easy to switch to lowRes and highRes versions of assets, depending on the environment// * Providing a rich event list to enable the game to react to different situations// * Providing an easy interface for accessing loaded assets//The biggest difference to conventional asset loaders is, that Cargo don't just loads a number//of assets and calls a callback after that has been done. Cargo should enable the developer to//get the game loaded as quickly as possible by loading mandatory assets at first, maybe even with//low resolution, then loading optional assets, or higher resolutions after that.//A player doesn't care if the game does additional downloads in the background after it has been//started, but he DOES care about having a long waiting time at the beginning.//This means that developers need to approach developing their games a bit different. Asset loading//with Cargo will always be asyncronous and deferred and will even continue when the user is//accessing a game menu, or is already playing.//When a user is in the main menu and assets for the first level are not yet fully loaded, the game//needs to show a loading screen again, but the progress could already be very advanced since all//the time the user has spent in the main menu has been used to pre-load the next assets.//Another thing is on-the-fly updating of assets during a running game. Cargo can be configured//or advised to load low-res versions of the assets to get the game running really quick.//When the game is already running, optional assets or high-res assets are loaded and Cargo//notifies the game logic about asset updates and additions via the included event system.//-------------------------------------------------------------------------------------------//At first, require a reference to the cargo asset loader via AMD or commonJS.var cargo = require('cargo');//Set up the cargo object with the defaults you'd like to use.//Heads up! If you require the cargo element again at a later point in your//program flow, you don't need to re-configure it again.cargo.config({ basepathGlobal: 'lib/', //A basepath to be put in front of every relative URL. basepathImage: 'img/', //Basepath for all image files. Appended to global basepath. basepathSound: 'snd/', //Basepath for all sound files. Appended to global basepath. basepathText: 'txt/', //Basepath for all text files. Appended to global basepath. parseJSON: true, //Should the content of *.json files be parsed to objects? Default: true parallels: 5, //How many parallel downloads should cargo do? timeout: 2000 //After how much milliseconds with no response should an asset be considered as timed out? lowResPattern: '{filename}@low.{extension}', //The default schema for low-res versions of images. highResPattern: '{filename}@2x.{extension}', //The default schema for high-res versions of images. cacheBusterPattern: '{filename}.{version}.{extension}', //The pattern for the cache-busting mechanism for where to apply version numbers. imageTypes: ['png', 'jpg', 'gif'], //Extensions for assets to be recognized as an image. soundTypes: ['mp3', 'aac', 'ogg'], //Extensions for assets to be recognized as a sound. textTypes: ['txt', 'html', 'css', 'json', 'js'] //Extensions for assets to be recognized as plaintext.});//About the lowRes and highRes patterns//-------------------------------------//Since Cargo is able to load the same asset in multiple resolutions, the package definitions//have to hint Cargo that other resolutions are available.//The patterns are used to help the developer to not being required to specify the URL for every//version of the asset, but only saying "true" to a version. Cargo then uses the given pattern to//build the URL by itself.//About cache busting//-------------------//Dealing with the browsers cache to load//The idea behind cargo is, that cargo is able to load assets in packages.//For example, you define a package "ui", which you need to create your user interface from,//or you need another package "level1" which contains all assets you need for your first game level.//You can define one or more packages before you start loading them.//Define a single package by passing in its JSON structure, or directly pass an array of package objects,//or a URL to a JSON file containing the package definition(s).cargo.definePackage({ id: 'level1' //The identifier of this package to access it at a later point. mandatory: { 'ship1': { //A detailed asset definition. src: 'player-ship.png', //The URL to the asset. Since this is an image, it will be prefixed with basepathGlobal and basePathImage fsize: 128, //Optional definition of the filesize of the asset. Used to calculate more accurate progress. fsize_hi: 256, //Equivalent to fsize, but for the hiRes version. fsize_lo: 64, //Equivalent to fsize, but for the loRes version. hi: true, //By setting the "hi" property to true, Cargo uses the lowResPattern to guess the filename of the hiRes image. lo: 'player-ship.0.5.png' //The lowRes version is specifically defined here. version: 1 //Optionally specify a version number of the asset and increase it after changes to bust the browser cache. }, 'game_bkg': 'some_background.jpg', //A shorthand asset definition. No low/high res version given. 'bkg_music': 'soundtrack.ogg', //Will be recognized as sound and prefixed with basepathGlobal and basepathSound. 'enemy_defs': 'enemies.json' //If the option parseJSON is set to true, the JSON file will be parsed automatically. }, optional: { //Use this like the "mandatory" property. Optional assets won't be loaded until all mandatory assets are done loading. }});//After a package has been defined, its not instantly loaded.//You may specify a great number of packages before loading anything. The idea is, that you can load one or more packages together.cargo.load({ packages: ['ui', 'base_assets', 'level1'], //This tells cargo which packages have to be loaded to finish the load. optionals: false, //Pass false here, to completely ignore optional assets from loading. incremental: true, //Pass true to automatically increase the resolution. loadHighRes: false, //Setting this to "false" prevents incremental to load highRes assets. progress: progressHandler, finish: doStuff, optionalFinish: doMoreStuff error: errorHandler});//Setting the incremental option to "true" will cause Cargo to download the lowRes version of all mandatory//assets first, then continue with switching to standard, then continue with switching to highRes (if possible).//This will cause a overall bigger traffic impact, but will get the players into the game much quicker//and delivering better graphics after some time.//The cargo.load() method returns a loading object which enables you to react to different events.//In this example, three packages are being loaded. During the process, the progressHandler() function is//being called on every update and gets passed the current loading progress.function progressHandler(percent, calculatedSpeed, calculatedDuration){ $('.loadingLabel').text(percent);}//The calculatedSpeed and calculatedDuration arguments may not contain values at the beginning.//Both are calculated during the download process, when fsize hints are given in the package//definitions. The speed is given in kb/s, the calculated remaining duration is given in seconds.function errorHandler(fails){ $.post('errortracker.php', fails);}//The "fails" attribute is an object that contains the keys of all assets that have been failed//loading, as well as the HTTP statuscodes of the fail(s).//Now lets see how we can actually work with the assets.function doStuff(){ var pkg, ctx; //We're getting a reference to the package object here for faster interaction. pkg = cargo.getPackage('ui'); ctx = $('#canvas')[0].getContext('2d'); //Entering some kind of game loop. myEngine.onFrame(function(){ //This draws the asset "ship1" at the position x:1, y:1 in a canvas. ctx.drawImage(pkg.get('ship1'), 1, 1); });}//Calling the get() method of a package will return the asset with the given key.//Now its getting interesting:pkg.setResoluion('high'); //high, standard, low//This makes cargo download the high resolution version of every asset if neccessary.//After that, the get() method automatically returns the highRes version without further//configuration.//Listen to the changeResolution event to get notified to resolution changes that are//made manually or automatically:pkg.on('changeResolution', function(res){ //Maybe modify your game loop.});//The res attribute can be "low", "standard" or "high"//Handling optional assets//------------------------//Well, the finish handler is being called after all mandatory assets have been loaded.//But whats with the optional assets?//You may have already spotted the "optionalFinish" option for the load() method.//This callback is being called after all optional assets of a package have been loaded.//When the callback has been called, you can be sure that all optional assets are available for use.function doMoreStuff(){ //Prepare your game for using the optional assets}//Optionally, you can check the "optionalsAvailable" boolean property of your package object //to do a quick check inside your game loop:function myGameLoop(){ var pkg, ctx; pkg = cargo.getPackage('game_assets'); ctx = $('#canvas')[0].getContext('2d'); while(doLoop){ ctx.drawImage(pkg.get('player'), 10, 50); if(pkg.optionalsAvailable){ ctx.drawImage(pkg.get('player_decoration'), 10, 50); } }}Thanks for reading it!I am looking forward for your comments greetings,Chris Quote Link to comment Share on other sites More sharing options...
Ezelia Posted April 3, 2013 Share Posted April 3, 2013 such features would be awesome for an asset manager.you should maybe simplify the configuration and provide some usage examples of the most simple scenarios (coz those will be the most used) also, for loading accuracy, you can use XHR when available instead of asking the user to enter the file size ... and for file types it's maybe more accurate to use HEAD requests to read content type instead of relaying on file extension (just an idea ) Quote Link to comment Share on other sites More sharing options...
Chris Posted April 3, 2013 Author Share Posted April 3, 2013 I would have thought about a little build script or something like that to add filesizes to package definition. I am a lazy developer, too and don't want to copy all filesizes over to my package definitions Making XHR HEAD requests are an interesting approach, but I can think that there may be a couple of problems with that:Possibly Slow. The loader would have to make a lot of requests before actually downloading the assets.Maybe the browser doesn't support itMaybe the server doesn't support itI think the filtering by file extension is reliable. I heard that mimetypes are veeeery often misconfigured on webservers I will think about a couple of examples. Also, the configuration isn't that complex! You don't have to specify all options that have been shown in the draft file. The draft only shows all possible options you can tweak.I most cases you would only set your basepaths and leave all other options on their default values. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.