Disposing Asset Manager in LibGDX - java

Here is my OOP setup:
Class MainGame extends Game:
...
public void create () {
assets = new Assets(); // my custom Assets object (constructor creates manager and AssetDescriptors
assets.load(); // loads Asset Descriptors into manager
assets.getManager().finishLoading();
batch = new SpriteBatch();
setScreen(new PlayScreen(this, assets));
}
...
public void dispose () {
assets.dispose(); // disposes asset manager
super.dispose();
batch.dispose();
}
Class PlayScreen implements Screen:
public PlayScreen(MainGame game, Assets assets) {
this.assets = assets;
this.game = game;
background = assets.getManager().get(assets.getBackground());
hero = new HeroSprite(290, 1100, this, assets);
// Create Game camera and Hub also
}
public void render(float delta) {
// Clear screen
// Use game batch to draw to screen
}
public void dispose() {
assets.dispose();
}
Class HeroSprite:
public HeroSprite(int x, int y, PlayScreen screen, Assets assets){
this.assets = assets;
this.screen = screen;
// Animation Textures
heroWalking = assets.getManager().get(assets.getWalking()); // This is an Asset Descriptor
walkAnime = new Animation(heroWalking) // my own custom animation class which takes the spritesheet and returns the appropriate frames
...
}
// This does not contain a dispose method
Sorr if there's a lot of code there I tried to minimize as much as possible. My main question is if I am disposing correctly?
It seems ok, my game runs fine. But when I quit the game and re-enter a black screen shows. This happens both on Desktop and Android.
A solution that works is to clear the memory on Android using Super Cleaner App. Since it's a memory issue I figures it has something to do with me disposing incorrectly.
EDIT : Revealing my Assets Class
public class Assets {
private AssetManager manager;
private AssetDescriptor<Texture> background;
private AssetDescriptor<Texture> wood;
public AssetManager getManager() {
return manager;
}
public AssetDescriptor<Texture> getBackground() {
return background;
}
public AssetDescriptor<Texture> getWood() {
return hero;
}
public Assets(){
manager = new AssetManager();
background = new AssetDescriptor<Texture>("textures/static/bg.png", Texture.class);
hero = new AssetDescriptor<Texture>("textures/static/hero.png", Texture.class);
...
}
public void load(){
manager.load(background);
manager.load(hero);
...
}
public void dispose(){
manager.dispose();
}
}

Screen.dispose() does not get called automatically, you should manually call dispose when you exit a screen and not needing the assets anymore. But obviously when you need them again you need to load them again and you need a new AssetManager for that. So if your assets won't take up that much you should just keep it in memory.
On exit however you need to dispose. Game.dispose() does get called automatically. Here you should call getScreen().dispose() and have everything needing disposing in the dispose() method of the effective Screen.
Since currently it does not look like you are disposing between screens it might be because you have a static AssetManager, you should show your Assets() class.

Related

How to handle AssetManager in multiple screens

public static AssetManager assets = new AssetManager(); should be avoided, however how do I handle an AssetManager in multiple screens? Is this a good solution?
public class myGame {
assetMananger manager = new AssetManager();
...
public assetManager getAssetMananger(){
return manager;
}
}
public class GameScreen implements Screen{
private AssetManager manager;
public GameScreen(myGame game){
manager = game.getManager();
}
public void render(){
manager.load(...);
}
Gdx.app.getApplicationListener() return ApplicationListener instance. You can typecast to your implemented class and then easily access any method or data member of that class.
In this way :
((GdxTest)Gdx.app.getApplicationListener()).assets // <-- You can use from where you want in your project.
ApplicationListener implemented class :
public class GdxTest extends ApplicationAdapter {
public Assets assets;
#Override
public void create() {
assets= new Assets();
}
....
#Override
public void dispose() {
assets.dispose();
}
}
Assets class that handle all assets/resources of Game
public class Assets implements Disposable {
public AssetManager assetManager;
public Assets(){
assetManager=new AssetManager();
}
...
#Override
public void dispose() {
assetManager.dispose();
}
}
Either load all resources in create() method and show splash screen while all your data are loading to AssetManager. (best way for small project)
else for big projects having lots of resources unload unnecessary resources -> load new resources, while loading show loading screen to user.
My approach is always like this:-
Create my own manager class that receive instance if AssetsManager and load all game assets when game is initially loaded
public class Assets implements Disposable{
public static final Assets instance = new Assets();
private AssetManager manager;
// Individual assets
public PlayerAssets playerAssets;
private Assets(){}
public void init(AssetManager manager){
this.manager = manager;
manager.load(..);
manager.finishLoading();
TextureAtlas atlas = manager.get(..);
playerAssets = new PlayerAssets(atlas);
}
public void dispose(){
manager.dispose();
}
/** Sample Inner class for catching loaded game assets */
public PlayerAssets{
public final AtlasRegion legs;
PlayerAssets(TextureAtlas atlas){
legs = atlas.find(..);
}
}
}
On your game class you load all game assets.
public class MyGame extends Game {
#Override
public void create(){
Assets.instance.init(new AssetManager()); // loading your resources
setScreen(new GameScreen());
}
}
Update from the comment below
Disposing your assets. You might want to create an abstract class for all your screen as follows
public abstract class AbstractScreen implements Screen{
protected Game game;
public AbstractScreen(Game game){
this.game = game;
}
}
. . . . . .
public void dispose(){
Assets.instance.dispose();
}
Then all of your Screens will extend this class. eg
public class MenuScreen extends AbstractScreen {
public MenuScreen(Game game){
super(game);
}
}
Calling screen instance dispose method will dispose all of the resources,
you can override it for places where you might prefer different behaviour
eg
#everride
public void dispose(){
// super.dispose();
// Release other screen resources
}
Where to call screen dispose is definitely up to you.
Also you might want to show loading screen when you load the assets to avoid blank screen due to loading delays
If you override dispose as above disposing a screen wont dispose the assets

Android Libgdx assetManager: asset not loaded

I am having a weird issue with just loading my assets, using the assetManager. My assetManager is a separate class I made to make everything shorter and more neat. My Asset class isn't static. I have all the assets loaded in a non static assetDescriptor.
I load my splash screen which in tern loads all my assets while that is displaying. when I call the other screen the assets I have loaded can not be loaded and causes my game to crash. The splash screen does load but when it assigns a new screen it crashes.
I have spent 2 days on this issue. trying more then 15 variations. To no prevail.
My error message is:
com.badlogic.gdx.utils.GdxRuntimeException: Asset not loaded: stuff.png
at com.badlogic.gdx.assets.AssetManager.get(AssetManager.java:144)
at com.badlogic.gdx.assets.AssetManager.get(AssetManager.java:167)
at com.nectar.pewdybird.mainMenu.<init>(mainMenu.java:71)
at com.nectar.pewdybird.pewdyBird.render(pewdyBird.java:68)
at com.badlogic.gdx.backends.android.AndroidGraphics.onDrawFrame(AndroidGraphics.java:459)
at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1523)
at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1240)
Splash.class:
public Assets assets;
public void create(){
assets = new Assets();
assets.load();
}
public void render(){
if(assets.update()) {
setScreen(new mainMenu(this));
dispose();
} else {
//Splash Screen
gl.glClearColor(0,0,0,1);
gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
//Draws SplashScreen
batch.end();
}
}
public void dispose(){
this.screen.dispose();
this.batch.dispose();
}
mainMenu.class
public Assets assets;
private Texture Stuff;
public mainMenu(Splash game){
assets = game.assets;
Stuff = game.assets.manager.get(game.assets.stuff);
}
Asset.class
public AssetManager manager = new AssetManager(new InternalFileHandleResolver());
public final AssetDescriptor<Texture> stuff =
new AssetDescriptor<Texture>("stuff.png", Texture.class);
public void load(){
manager.load(stuff);
//12 More loads
manager.finishLoading();
}
public boolean update(){
return manager.update();
}
Thank you for reading and possibly helping with my maybe simple issue.
Look at your mainMenu class. You're creating a new Assets object, that don't have any asset so instead of creating a new Assets object use Assets object of Splash because you loaded assets on that object.
public Assets assets;
public Texture Stuff;
public mainMenu(){
assets = new Assets(); // why are you creating new Assets here
Stuff = assets.manager.get(assets.stuff);
}
Actually you're creating object of mainMenu by using parameterised constructor in Splash class
public mainMenu(Splash splash){
assets = splash.assets; // instead of creating new take reference of Splash class assets
Stuff = assets.manager.get(assets.stuff);
}

Error on Java libgdx with AssetsManager

So I start to try to do some game in java with libgdx, and I am trying to use the AssetsManager. but it keeps giving an error. I will put the code (I will try to resume the code) and the output.
The game goes through the loading screen fine but when it reaches the game screen it stops and gives an error, I do believe it's because the assets.
If someone knows why I am getting this error.
DesktopLauncher.java
public class DesktopLauncher {
public static void main (String[] arg) {
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
new LwjglApplication(new pressme(), config);
}
}
pressme.java
public class pressme extends Game {
LoadingScreen loadingscreen;
AssetsManager assetsmanager;
Assets assets;
#Override
public void create() {
assets = new Assets();
assetsmanager = new AssetsManager();
assetsmanager.load();
assets.other();
loadingscreen = new LoadingScreen(this);
setScreen(loadingscreen);
}
}
LoadingScreen.java
public class LoadingScreen implements Screen {
Assets assets;
AssetsManager assetsmanager;
private final pressme game;
public GameScreen game_screen;
public LoadingScreen(final pressme game){
assets = new Assets();
assetsmanager = new AssetsManager();
this.game = game;
}
#Override
public void show() {
assetsmanager.load();
}
private void update(float delta){
System.out.println(progress);
if(assetsmanager.manager.update()) {
game.setScreen(new GameScreen(game));
}
progress = assetsmanager.manager.getProgress();
}
}
GameScreen.java
public class GameScreen implements Screen {
pressme game;
Assets assets;
AssetsManager assetsmanager;
public GameScreen(pressme game){
this.game = game;
assets = new Assets();
assetsmanager = new AssetsManager();
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor( 0.5F, 0.5F, 0.5F, 0.5F);
Gdx.gl.glClear( GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT );
camera.update();
generalUpdate(touch, camera);
batch.setProjectionMatrix(camera.combined);
assets.load();
batch.begin();
batch.draw(assets.back, 0, 0);
batch.end();
}
}
Assets.java
public class Assets {
AssetsManager assetsmanager;
public Sprite back;
public void load(){
assetsmanager = new AssetsManager();
assetsmanager.load();
back = new Sprite(assetsmanager.manager.get(assetsmanager.back, Texture.class));
}
}
AssetsManager.java
public class AssetsManager {
public AssetManager manager = new AssetManager();
public String back = "back.png";
public void load(){
manager.load(back, Texture.class);
}
}
Output
LOADINGGG
0.0
0.0
0.015151516
0.015151516
0.030303031
.
.
.
0.969697
0.9848485
0.9848485
Exception in thread "LWJGL Application" java.lang.NullPointerException
at com.badlogic.gdx.graphics.g2d.SpriteBatch.draw(SpriteBatch.java:586)
at com.david.pressme.GameScreen.render(GameScreen.java:244)
at com.badlogic.gdx.Game.render(Game.java:46)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication.mainLoop(LwjglApplication.java:223)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:124)
EDIT
RC Thank you for the tip
So After I put the "assets.load()" still with some error. I dont know if the "assets.load()" it is on the right place
LOADINGGG
0.0
0.0
0.015151516
0.015151516
0.030303031
.
.
.
0.969697
0.969697
0.9848485
0.9848485
Exception in thread "LWJGL Application" com.badlogic.gdx.utils.GdxRuntimeException: Asset not loaded: back.png
at com.badlogic.gdx.assets.AssetManager.get(AssetManager.java:144)
at com.david.pressme.Assets.load(Assets.java:254)
at com.david.pressme.GameScreen.render(GameScreen.java:172)
at com.badlogic.gdx.Game.render(Game.java:46)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication.mainLoop(LwjglApplication.java:223)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:124)
EDIT2
RC, Spylot ty for the tips, it was what you said
So its resolved, i puting
"assets.load" on "public void show()"
this way when the screen its load he loads the assest on the begin, and i take it off from the
"public void render(){"
because he was always loading the assests because its a loop (I think so), then on
"Assets.java public load(){" i put "assetsmanager.load();"
Thanks Rc for the tips. Then on the same place I put the
"assetsmanager.manager.finishLoading();"
so it loads all assets before do something. Thank you spylot for the tip.
Thank you all who help or try to help much appreciate.
You should be aware that when you're loading assets, you cannot use them until you call finishLoading. finishLoading blocks all calls trying to grab onto the asset until they are fully loaded in the memory.
I highly suggest you to read the AssetManager API.
So in short, after each time you use load() call, you should finish it off using manager.finishLoading()

Assets wont get rendered after disposing and reloading them

In my game I have currently two screens. The MenuScreen and the GameScreen. Through the Play option in the menu the player can switch to the GameScreen and start the Gameplay and with Escape he can get back to the MenuScreen. I dispose the used Assets when I switch to the other Screen in the hide() method and load the needed Assets for the new Screen in the constructor of the Screen I switch to. The problem is that the Textures and Sound Effects aren't rendered/played when I switch back.
For example when I start the game in the MenuScreen, then switch to the GameScreen everything is fine. But when I switch back to the MenuScreen the MenuScreen is just a black window. When I then switch to the GameScreen again it's black too except for the BitmapFont.
Maybe there is a fundemental flaw in the way I handle this. I tried to leave out as much unnecessary things as I can from the code I post here, but I fear that it's still too much.
RessourceLoader Class:
public class RessourceLoader {
public static AssetManager manager;
public static void create() {
manager = new AssetManager();
}
public static void loadMenuScreen() {
manager.load("gfx/menuBackground.png", Texture.class);
}
public static void getMenuScreen() {
menuBackground = manager.get("gfx/menuBackground.png", Texture.class);
}
public static void disposeMenuScreen() {
menuBackground.dispose();
}
public static void loadGameScreen() {
// load GameScreen Assets through AssetManager
}
public static void getGameScreen() {
// get GameScreen Assets through AssetManager
}
public static void disposeGameScreen() {
// dispose all GameScreen Assets
}
public static void dispose() {
manager.dispose();
}
}
MenuScreen Class:
public class MenuScreen implements Screen {
// Game starts in the MenuScreen
// Instance of game
private PHGame game;
// Orthographic camera
private OrthographicCamera cam;
public MenuScreen(PHGame phGame) {
game = phGame;
RessourceLoader.loadMenuScreen();
RessourceLoader.manager.finishLoading();
RessourceLoader.getMenuScreen();
cam = new OrthographicCamera();
cam.setToOrtho(true, 640, 480);
game.batcher.setProjectionMatrix(cam.combined);
}
#Override
public void render(float delta) {
// Fills background with black to avoid flickering
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// Begin Drawing
game.batcher.begin();
// Draw Menu
// Stop drawing
game.batcher.end();
// Pressing Space confirms currently selected menu item
if (GameKeys.isPressed(GameKeys.SPACE)) {
game.setScreen(new GameScreen(game));
}
// Update Key Presses
GameKeys.update();
}
#Override
public void hide() {
dispose();
}
#Override
public void dispose() {
RessourceLoader.disposeMenuScreen();
}
}
GameScreen Class:
public class GameScreen implements Screen {
// Instance of game
private PHGame game;
private GameWorld world;
private GameRenderer renderer;
private float runTime;
public GameScreen(PHGame phGame) {
game = phGame;
RessourceLoader.loadGameScreen();
RessourceLoader.manager.finishLoading();
RessourceLoader.getGameScreen();
world = new GameWorld(game, this);
renderer = new GameRenderer(world, game);
}
#Override
public void render(float delta) {
// runTime is the amount of time the game is running
runTime += delta;
// Updates the Game World
world.update(delta);
// Renders everything
renderer.render(runTime);
// Update Key Presses
GameKeys.update();
}
#Override
public void hide() {
dispose();
}
#Override
public void dispose() {
RessourceLoader.disposeGameScreen();
}
}
GameRenderer Class:
public class GameRenderer {
// Instance of PHGame
PHGame game;
// Instance of Game World
private GameWorld world;
// Orthographic Camera
private OrthographicCamera cam;
// If true hitbox's will be shown
private boolean showHitbox;
// Game Objects
private Player player;
public GameRenderer(GameWorld world, PHGame game) {
this.game = game;
this.world = world;
player = world.getPlayer();
cam = new OrthographicCamera();
cam.setToOrtho(true, 640, 480);
showHitbox = false;
game.batcher.setProjectionMatrix(cam.combined);
}
public void render(float runTime) {
// draw objects and hud
}
}
If there are any questions regarding my problem I'll try to answer then as good as I can.
Refer to the github article 'managing your assets'. AssetManagers should not be static. 'This typically would cause black/missing textures or incorrect assets.'
After you dispose your asset manager it can no londer be used. Instead use manager.unload to unload assets. manager.unload("gfx/menuBackground.png");
EDIT:
I also didn't see any overriden show() methods. If you want your assets back you will need to load your assets in the screen's show method every time.

LibGdx Screen: render and lifecycle

I have a problem, I think its related to Screen render and its lifecycle.
Basically I have two screens (Menu and Game). In GameScreen render method i call World.update and after that my Render. In hide method (of GameScreen) i dispose of the SpriteBatch from Redner class.
So when I change the screen from Game to Menu (within World.update) Java crashes. As far as I can tell, the dispose is making the crash.
So my question is, when i set a new screen in the middle of the render cycle, is that render cycle still going to finish with its old screen? Meaning, am I calling batch.dispose before the rendering was finished, and that is why i get the problem?
Thank you for all the help
public class GameScreen extends AbstractGameScreen {
private static final String TAG = GameScreen.class.getName();
private WorldController worldController;
private WorldRenderer worldRenderer;
private boolean paused;
public GameScreen(Game game) {
super(game);
}
#Override
public void render(float deltaTime) {
// Do not update game world when paused
if (!paused) {
// Update game world by the time that has passed since last render time
worldController.update(deltaTime);
}
// Sets the clear screen color to: Cornflower Blue
Gdx.gl.glClearColor(0x64 / 255.0f, 0x95 / 255.0f, 0xed / 255.0f, 0xff / 255.0f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// Render game world to screen
worldRenderer.render();
}
#Override
public void resize(int width, int height) {
worldRenderer.resize(width, height);
}
#Override
public void show() { // Similar as create method
worldController = new WorldController(game);
worldRenderer = new WorldRenderer(worldController);
Gdx.input.setCatchBackKey(true);
}
#Override
public void hide() { // Similar to dispose method
worldRenderer.dispose();
Gdx.input.setCatchBackKey(false);
}
#Override
public void pause() {
paused = true;
}
#Override
public void resume() {
super.resume();
// Only called on Android
paused = false;
}
}
That's basically correct. The screen that calls setScreen from within its render method will have hide called on itself and then will continue through the rest of its code in its render method. So you are killing your sprite batch right before trying to draw with it.
So don't call dispose from within your hide method. In fact, it is probably bad practice for a Screen to ever call dispose on itself. You can reserve that for the Game class that owns it. For example, you could do something like this in your game class:
#Override
public void render() {
super.render();
if (getScreen() != gameScreen && gameScreen != null) {
gameScreen.dispose();
gameScreen = null;
}
}
By the way, you should probably put the SpriteBatch in your Game subclass and let all the different screens share it. It's a fairly big object to be allocating and deallocating for no reason.

Categories