Pregunta

I am writing an game in java with Libgdx and have a question about how to have multiple instances of the same object in an ArrayList without abusing the garbage collector. this is the code of my main game state. I am still really new, so I assume my coding habits are downright awful:

package com.frog.game;

import java.util.ArrayList;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator;
import com.badlogic.gdx.math.MathUtils;
import com.frog.entities.Frog;
import com.frog.entities.Grass;
import com.frog.entities.LilyPad;
import com.frog.gamestates.GameState;
import com.frog.managers.GameStateManager;
import com.frog.managers.Jukebox;
import com.frog.managers.Save;
import com.frog.managers.SimpleDirectionGestureDetector;
import com.frog.managers.SpeedManager;
import com.frog.game.Game;



public class PlayState extends GameState {
private SpriteBatch sb;
private Frog frog;
private BitmapFont font;
private ArrayList<LilyPad> lilypads;
private Grass grass;

private float hopY;
public boolean tmp;
private SpeedManager speedManager;
private float speed;
private float[] placement;

public PlayState(GameStateManager gsm) {
    super(gsm);
}


@SuppressWarnings("deprecation")
public void init() {
    speedManager = new SpeedManager();
    lilypads = new ArrayList<LilyPad>();
    sb = new SpriteBatch();
    frog = new Frog();
    frog.init();
    grass = new Grass();
    grass.init();
    hopY = Game.SIZE * 0.8f;
    placement = new float[] {
            0,
            Game.SIZE,
            Game.SIZE * 2
        };

    addPad();

    FreeTypeFontGenerator gen = new FreeTypeFontGenerator(
            Gdx.files.internal("PressStart2P-Regular.ttf")
            );
    font = gen.generateFont((Game.WIDTH / 10));
    gen.dispose();

    Gdx.input.setInputProcessor(new SimpleDirectionGestureDetector(new SimpleDirectionGestureDetector.DirectionListener() {

        @Override
        public void onUp() {
            // TODO Auto-generated method stub
        }

        @Override
        public void onRight() {
            frog.moveRight();

        }

        @Override
        public void onLeft() {
            frog.moveLeft();

        }

        @Override
        public void onDown() {

        }
    }));

}

This is the method I use to add another lilypad at the top of the screen. I make it appear in one of three places randomly. Since the main function of these lilypads is to scroll down the screen, instances of the lilypad are being added and removed from the array non stop. I know this kills because each time I add a new lilypad, I have to run init() for it, otherwise I will get a nullpointerexception. init() instantiates a bunch of objects in order to make that lilypad render, such as textures, the default Y value etc. Is there any way I could run the init() method once for the entire arraylist, even though I am constantly adding & removing them? I have considered wrapping the same lilypads around the screen when they hit the bottom, so I would only need like 7, and then I wouldn't have to add or remove anymore, but would have to rework a big chunk of code for that, and consider it a last resort. The game already runs smooth, it just has a few barely noticeable stutters here and there that I would like to avoid.

private void addPad() {
        lilypads.add(new LilyPad(placement[MathUtils.random(0, 2)], 300, false));
        lilypads.get(lilypads.size() - 1).init();
}

public void update(float dt) {


    speed = speedManager.speed(dt);
    speedManager.update(dt);

//      update grass
    if(!grass.shouldStop()) {
        grass.update(dt, speed);
        frog.introPlay(speed);
    }

//              update lily pads
    for(int i = 0; i < lilypads.size(); i++) {
        lilypads.get(i).update(dt, speed);

This is where I call the addPad() method. It basically says if the last added pad is fully visible on screen, add the next.

        if(lilypads.get(i).getY() < (Game.HEIGHT - Game.SIZE) && lilypads.get(i).past()) {
            addPad();
        }

//          hop frog

        if(lilypads.get(i).getY() < hopY  && lilypads.get(i).past2()) {
//              play hop
            if(lilypads.get(i).getX() > frog.getX() - Game.SIZE / 2 && lilypads.get(i).getX() < frog.getX() + Game.SIZE / 2){
                frog.incrementScore();
                Jukebox.play("hop");
                lilypads.get(i).splash();
                frog.play(speed);
            } else {
                Jukebox.stopAll();
                Save.gd.setTempScore(frog.getScore());
                gsm.setState(GameStateManager.GAMEOVER);
                return;
            }
            return;
        }

        if(lilypads.get(i).getY() < (-Game.SIZE)){
            lilypads.remove(i);
        }
    }

}

public void draw() {

//      draw grass
    if(!grass.shouldStop()) {
        grass.draw(sb);
    }

//      draw pads

    for(int i = 0; i < lilypads.size(); i++){
        lilypads.get(i).draw(sb, true);
    }

//      draw frog
    frog.draw(sb, speed, true);

    sb.setColor(1, 1, 1, 1);
    sb.begin();
    font.draw(sb, Long.toString(frog.getScore()), 
            Game.WIDTH * 0.92f -font.getBounds(Long.toString(frog.getScore())).width, Game.HEIGHT * 0.958f);
    sb.end();
}

public void handleInput() {

}

public void dispose() {
    sb.dispose();
    Jukebox.stopAll();
}

Thanks for the support.

¿Fue útil?

Solución

Use libGDX's custom collections for that. You can find all of them here.

In your case you want to use an Array. They are optimized to not cause the garbage collector to kick in, for example by re-using the iterators.

With a standard ArrayList the following piece of code in your render method would create a new Iterator in every frame.

for(LilyPad lilyPad : lilipads){
    lilypad.draw(sb, true);
}

In general try to avoid using new whenever you can. This wiki article might help you with it and it explains how pooling works.

Another common trick to avoid GC especially when working with Vector2 or Vector3 is keeping a private final Vector2 tmp = new Vector2() and always only using tmp.set(x, y) to change it. This principle could also be applied to any other custom class that you are creating yourself to hold some data.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top