package scene2d; import scene3d.Actor3d; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.assets.AssetManager; import com.badlogic.gdx.assets.loaders.resolvers.InternalFileHandleResolver; import com.badlogic.gdx.audio.Music; import com.badlogic.gdx.audio.Sound; import com.badlogic.gdx.graphics.g2d.Animation; import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.TextureAtlas; import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.graphics.g3d.Model; import com.badlogic.gdx.maps.tiled.TiledMap; import com.badlogic.gdx.maps.tiled.TmxMapLoader; import com.badlogic.gdx.scenes.scene2d.ui.Skin; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.ArrayMap; import com.badlogic.gdx.utils.JsonValue; /** Automatic Assets Loading for the Game * <p> * This class automatically loads all the assets in the prescribed folders into the appropriate * class types. All This can be accessed using an neat api. * * <p> * <b>#Important </b> <br> * All asset files must be lowercase only.. otherwise it causes problems with android <br> * 1. All Assets are to be stored in the assets directory <br> * 2. Automatic Asset Loading the directory Structure should be like this <br> * * <p> * icon.png --- your game icon to be displayed on the window<br> * atlas/ --- all your Texture Atlas files .atlas and .png go here<br> * font/ --- all your BitmapFont files .fnt and .png go here<br> * music/ --- all your Music files .mp3 go here<br> * sound/ --- all your Music files .mp3 go here<br> * particle/ --- all your Particle files .part go here<br> * map/ --- all your TMX map files along with tilesets go here<br> * pack/ --- all your image files which are to be packed are to be stored here<br> * so that they are automatically packed by the texture packer and stored in * the atlas folder * * <p> * All the assets are read from their particular folders and an asset.json file is created with * the filenames: * ex: asset.json * { * "font": "arial.fnt, font1", * "atlas": "image.atlas", * "sound": "click.mp3,gg.mp3", * "music": "title.mp3,"bg.mp3", * } * All assets are accessed this way,<br> * <pre> * <code> * * //To load TextureRegion * TextureRegion cat = Asset.tex("cat"); * * //To load Animation * Animation catAnim = Asset.anim("cat"); * * //To load BitmapFont * BitmapFont font1 = Asset.font("font1"); * * //The music and sound files are automatically cached and can be played by invoking: * Asset.musicPlay("musicname"); * Asset.soundPlay("soundname"); * * //The asset functions will return null for Font, TextureRegion and Animation if the asset cannot be found * </code> </pre> * </p> * @author pyros2097 */ public final class Asset { private static AssetManager assetMan = new AssetManager(); public static Skin skin; public final static ArrayMap<String, String> assetMap = new ArrayMap<String, String>(); public final static ArrayMap<String, Music> musicMap = new ArrayMap<String, Music>(); public final static ArrayMap<String, Sound> soundMap = new ArrayMap<String, Sound>(); public final static ArrayMap<String, BitmapFont> fontMap = new ArrayMap<String, BitmapFont>(); public final static ArrayMap<String, TextureRegion> texMap = new ArrayMap<String, TextureRegion>(); public final static ArrayMap<String, TextureRegion> particleMap = new ArrayMap<String, TextureRegion>(); public final static Array<String> modelMap = new Array<String>(); private static Music currentMusic = null; public static String currentMusicName = ""; //Only file name no prefix or suffix private static Sound currentSound = null; private static boolean readinglock = false; private static boolean updatinglock = false; /* This is to be used by Gdx Studio */ public static String basePath = ""; public static boolean musicOn = true; public static boolean soundOn = false; public static boolean loadAsynchronous = true; private static void clear(){ assetMap.clear(); fontMap.clear(); texMap.clear(); particleMap.clear(); musicMap.clear(); soundMap.clear(); modelMap.clear(); } public static void load(){ if(loadAsynchronous) loadNonBlocking(); //else // loadBlocking(); } /* * This is a blocking load call blocks the display until assets are all loaded. */ public static void loadBlocking(){ clear(); readinglock = true; updatinglock = true; loadAssets(); assetMan.finishLoading(); assetMan.setLoader(TiledMap.class, new TmxMapLoader(new InternalFileHandleResolver())); getAssets(); } /* * This is a non-blocking load call that allows you to display other things while the load is going * on. This is called in the act method of SplashScene so that it is runs in the background * once the assets are all loaded it will automatically stop and call SplashScene.onAssetsLoaded() */ private static boolean loadNonBlocking(){ if(!readinglock){ clear(); loadAssets(); readinglock = true; } // once update returns true then condition is satisfied and the lock stops update call if(!updatinglock) if(assetMan.update()){ assetMan.setLoader(TiledMap.class, new TmxMapLoader(new InternalFileHandleResolver())); getAssets(); updatinglock = true; if(Scene.splashDuration != 0) Scene.nextSceneWithDelay(Scene.splashDuration); else Scene.nextScene(); } return updatinglock; } public static void loadAssets(){ JsonValue sv = Scene.jsonReader.parse(Gdx.files.internal(Asset.basePath+"asset")); for(JsonValue jv: sv.iterator()) assetMap.put(jv.name, jv.asString()); for(String font: assetMap.get("font").split(",")){ if(!font.isEmpty()) assetMan.load(basePath+"font/"+font, BitmapFont.class); } for(String atlas: assetMap.get("atlas").split(",")){ if(!atlas.isEmpty()) assetMan.load(basePath+"atlas/"+atlas, TextureAtlas.class); } for(String sound: assetMap.get("sound").split(",")){ if(!sound.isEmpty()) assetMan.load(basePath+"sound/"+sound, Sound.class); } for(String music: assetMap.get("music").split(",")){ if(!music.isEmpty()) assetMan.load(basePath+"music/"+music, Music.class); } assetMan.load(basePath+"skin/uiskin.json", Skin.class); } public static void getAssets(){ for(String font: assetMap.get("font").split(",")){ if(!font.isEmpty()) fontMap.put(ext(font), assetMan.get(basePath+"font/"+font, BitmapFont.class)); } for(String atlas: assetMap.get("atlas").split(",")){ if(!atlas.isEmpty()) for(TextureAtlas.AtlasRegion ar: assetMan.get(basePath+"atlas/"+atlas, TextureAtlas.class).getRegions()) texMap.put(ar.name, ar); } for(String sound: assetMap.get("sound").split(",")){ if(!sound.isEmpty()) soundMap.put(ext(sound), assetMan.get(basePath+"sound/"+sound, Sound.class)); } for(String music: assetMap.get("music").split(",")){ if(!music.isEmpty()) musicMap.put(ext(music), assetMan.get(basePath+"music/"+music, Music.class)); } skin = assetMan.get(basePath+"skin/uiskin.json", Skin.class); if(Scene.configJson.getBoolean("showFPS")) Scene.setupFps(); } private static String ext(String file){ return file.substring(0, file.indexOf(".")); } public static void save(){ Gdx.files.local(Asset.basePath+"asset").writeString(Scene.json.toJson(assetMap, ArrayMap.class, String.class), false); } /*********************************************************************************************************** * Music Related Global Functions * ************************************************************************************************************/ /** Plays the music file which was dynamically loaded if it is present otherwise logs the name * @param filename The Music File name only * @ex <code>music("title")</code> * */ public static void musicPlay(String filename){ if(Config.isMusic){ if(currentMusic != null) if(currentMusic.isPlaying()) if(currentMusicName == filename) return; else musicStop(); if(musicMap.containsKey(filename)){ Scene.log("Music: playing "+filename); currentMusic = musicMap.get(filename);//Gdx.audio.newMusic(Gdx.files.internal("music/"+filename)); currentMusic.setVolume(Config.volMusic); currentMusic.setLooping(true); currentMusic.play(); currentMusicName = filename; } else{ Scene.log("Music File Not Found: "+filename); } } } /** Pauses the current music file being played */ public static void musicPause(){ if(currentMusic != null) if(currentMusic.isPlaying()){ Scene.log("Music: pausing "+currentMusicName); currentMusic.pause(); } } /** Resumes the current music file being played */ public static void musicResume(){ if(currentMusic != null) if(!currentMusic.isPlaying()){ Scene.log("Music: resuming "+currentMusicName); currentMusic.play(); } else musicPlay(currentMusicName); } /** Stops the current music file being played */ public static void musicStop(){ if(currentMusic != null){ Scene.log("Music: stoping "+currentMusicName); currentMusic.stop(); currentMusic = null; } } /** Sets the volume music file being played */ public static void musicVolume(){ if(currentMusic != null); currentMusic.setVolume(Config.volMusic); } /** Disoposes the current music file being played */ public static void musicDispose(){ if(currentMusic != null); currentMusic.dispose(); } /*********************************************************************************************************** * Sound Related Global Functions * ************************************************************************************************************/ /** Plays the sound file which was dynamically loaded if it is present otherwise logs the name * @param filename The Sound File name only * @ex <code>soundPlay("bang")</code> * */ public static void soundPlay(String filename){ if(Config.isSound){ if(soundMap.containsKey(filename)){ currentSound = soundMap.get(filename); long id = currentSound.play(Config.volSound); currentSound.setLooping(id, false); currentSound.setPriority(id, 99); Scene.log("Sound:"+"Play "+ filename); } else{ Scene.log("Music File Not Found: "+filename); } } } /** Plays the sound file "click" */ public static void soundClick(){ if(Config.isSound && soundMap.containsKey("click")){ currentSound = soundMap.get("click"); long id = currentSound.play(Config.volSound); currentSound.setLooping(id, false); currentSound.setPriority(id, 99); Scene.log("Sound:"+"Play "+ "click"); } } /** Pauses the current sound file being played */ public static void soundPause(){ Scene.log("Sound:"+"Pausing"); if(currentSound != null) currentSound.pause(); } /** Resumes the current sound file being played */ public static void soundResume(){ Scene.log("Sound:"+"Resuming"); if(currentSound != null) currentSound.resume(); } /** Stops the current sound file being played */ public static void soundStop(){ Scene.log("Sound:"+"Stopping"); if(currentSound != null) currentSound.stop(); } /** Disposes the current sound file being played */ public static void soundDispose(){ Scene.log("Sound:"+"Disposing Sound"); if(currentSound != null) currentSound.dispose(); } /*********************************************************************************************************** * BitmapFont Related Functions * ************************************************************************************************************/ /** If key is present returns the BitmapFont that was dynamically loaded * else returns null * @param fontname The BitmapFont name * @return BitmapFont or null * @ex font("font1") or font("arial") * */ public static BitmapFont font(String fontname){ if(fontMap.containsKey(fontname)){ return fontMap.get(fontname); } else{ Scene.log("Font File Not Found: "+fontname); return null; } } /*********************************************************************************************************** * Texture Related Functions * ************************************************************************************************************/ /** If key is present returns the TextureRegion that was loaded from all the atlases * else returns null * @param textureregionName The TextureRegion name * @return TextureRegion or null * */ public static TextureRegion tex(String textureregionName){ if(texMap.containsKey(textureregionName)){ return texMap.get(textureregionName); } else{ Scene.log("TextureRegion Not Found: "+textureregionName); return null; } } /*********************************************************************************************************** * Animation Related Functions * ************************************************************************************************************/ private static Animation anim(String texName, int numberOfFrames, int hOffset) { // Key frames list TextureRegion[] keyFrames = new TextureRegion[numberOfFrames]; TextureRegion texture = Asset.tex(texName); int width = texture.getRegionWidth() / numberOfFrames; int height = texture.getRegionHeight(); // Set key frames (each comes from the single texture) for (int i = 0; i < numberOfFrames; i++) keyFrames[i] = new TextureRegion(texture, width * i, hOffset, width, height); Animation animation = new Animation(1f/numberOfFrames, keyFrames); return animation; } private static Animation anim(String texName, int numberOfFrames, float duration, int hOffset) { // Key frames list TextureRegion[] keyFrames = new TextureRegion[numberOfFrames]; TextureRegion texture = Asset.tex(texName); int width = texture.getRegionWidth() / numberOfFrames; int height = texture.getRegionHeight(); // Set key frames (each comes from the single texture) for (int i = 0; i < numberOfFrames; i++) keyFrames[i] = new TextureRegion(texture, width * i, hOffset, width, height); Animation animation = new Animation(duration, keyFrames); return animation; } /** * Get animation from single textureRegion which contains all frames * (It is like a single png which has all the frames). Each frames' width should be same. * <p> * @param texName * the name of the texture region * @param numberOfFrames * number of frames of the texture Region * @return animation created * * */ public static Animation anim(String texName, int numberOfFrames) { return anim(texName, numberOfFrames, 0); } /** * Get animation from single textureRegion which contains all frames * (It is like a single png which has all the frames). Each frames' width should be same. * <p> * @param texName * the name of the texture region * @param numberOfFrames * number of frames of the texture Region * @param duration * each frame duration on play * @return animation created * * */ public static Animation anim(String texName, int numberOfFrames, float duration) { return anim(texName, numberOfFrames, duration, 0); } /** * There is only single texture region which contains all frames * (It is like a single png which has all the frames). * The texture regions consists of both rows and columns * <p> * * @param textureAtlas * atlas which contains the single animation texture * @param animationName * name of the animation in atlas * @param numberOfFrames * number of frames of the animation * @param numberOfMaximumFramesInTheSheet * maximum number of frame in a row in the sheet * @param numberOfRows * number of rows that the sheet contains * @param indexOfAnimationRow * the row index (starts from 0) that desired animation exists * @param frameDuration * each frame duration on play * @return animation created * * */ public static Animation[] anim(String texName, int rows, int cols, float duration) { TextureRegion texture = Asset.tex(texName); // Set key frames (each comes from the single texture) /*for (int i = 0; i < numberOfFrames; i++) { keyFrames[i] = new TextureRegion( textureRegion, (textureRegion.getRegionWidth() / numberOfMaximumFramesInTheSheet) * i, textureRegion.getRegionHeight() / numberOfRows * indexOfAnimationRow, textureRegion.getRegionWidth() / numberOfMaximumFramesInTheSheet, textureRegion.getRegionHeight() / numberOfRows); }*/ int height = texture.getRegionHeight()/rows; Animation[] animations = new Animation[rows]; for(int i=0;i<rows;i++) animations[i] = anim(texName, cols, i*height); return animations; } /*********************************************************************************************************** * TMX MAP Related Functions * ************************************************************************************************************/ /* * Loads a Tmx map by specifying the map/level no * eg: loadTmx(4) -> returns the TiledMap "map/level4.tmx" * * Note: Tmx Maps must be loaded and unloaded manually as they may take a lot of time to load */ public static TiledMap map(int i){ assetMan.setLoader(TiledMap.class, new TmxMapLoader(new InternalFileHandleResolver())); assetMan.load(basePath+"map/level"+i+".tmx", TiledMap.class); assetMan.finishLoading(); return assetMan.get(basePath+"map/level"+i+".tmx", TiledMap.class); } /* * unloads a Tmx map by specifying the map/level no * eg: unloadTmx(4) -> unloads the TiledMap "map/level4.tmx" * * Note: Tmx Maps must be unloaded manually */ public static void unloadmap(int i){ assetMan.unload(basePath+"map/level"+i+".tmx"); } /* * Load a G3db model from the model directory * @param modelName The name of the modelFile * @example loadModel("ship"); */ public static Actor3d loadModel(String modelName){ assetMan.load(basePath+"model/"+modelName+".g3db", Model.class); assetMan.finishLoading(); return new Actor3d(assetMan.get(basePath+"model/"+modelName+".g3db", Model.class)); } /* * Unload a G3db model which was previously loaded in the assetManager * @param modelName The name of the modelFile that was loaded * @example unloadModel("ship"); */ public static void unloadModel(String modelName){ assetMan.unload(basePath+"model/"+modelName+".g3db"); } /* * Load a Obj model from the model directory * @param modelName The name of the modelFile * @example loadModel("ship"); */ public static Actor3d loadModelObj(String modelName){ assetMan.load(basePath+"model/"+modelName+".obj", Model.class); assetMan.finishLoading(); return new Actor3d(assetMan.get(basePath+"model/"+modelName+".obj", Model.class)); } /* * Unload a Obj model which was previously loaded in the assetManager * @param modelName The name of the modelFile that was loaded * @example unloadModel("ship"); */ public static void unloadModelObj(String modelName){ assetMan.unload(basePath+"model/"+modelName+".obj"); } /*********************************************************************************************************** * LOG Related Functions * ************************************************************************************************************/ /* * Logs all the assets that are loaded and cached */ public static void logAll(){ logTextures(); logFonts(); logSounds(); logMusics(); } /* * Logs all the TextureRegions that are loaded and cached */ public static void logTextures(){ Scene.log("BEGIN logging Textures------------------"); for(String na: texMap.keys()) Scene.log(na); Scene.log("END logging Textures------------------"); } /* * Logs all the BitmapFonts that are loaded and cached */ public static void logFonts(){ Scene.log("BEGIN logging Fonts------------------"); for(String na: fontMap.keys()) Scene.log(na); Scene.log("END logging Fonts------------------"); } /* * Logs all the Sounds that are loaded and cached */ public static void logSounds(){ Scene.log("BEGIN logging Sounds------------------"); for(String na: soundMap.keys()) Scene.log(na); Scene.log("END logging Sounds------------------"); } /* * Logs all the Music that are loaded and cached */ public static void logMusics(){ Scene.log("BEGIN logging Musics------------------"); for(String na: musicMap.keys()) Scene.log(na); Scene.log("END logging Musics------------------"); } /* * Unloads and disposes all the resources except for Tmx Maps * This is called by Sink.exit(); */ public static void unloadAll(){ assetMan.dispose(); } /* * If using in eclipse set basePath to ./bin/ */ public static void setBasePath(String path){ basePath = path; } }