I have a Sprite class:
public class Sprite {
public static ArrayList<Sprite> sprites = null;
private Texture texture = null;
public Sprite(Texture texture) {
sprites.add(this);
this.texture = texture;
}
public void render(SpriteBatch batch) {
batch.draw(texture);
}
public void dispose() {
sprites.remove(this);
}
}
Before I create any sprites, I make sure that the Sprite class has a reference to the pool of sprites that are going to be rendered:
public class Main extends ApplicationAdapter {
private static ArrayList<Sprite> sprites = new ArrayList<Sprite>();
public Main() {
Sprite.sprites = sprites;
new Sprite("texture.png");
}
@Override
public void render(SpriteBatch batch) {
for (int i = 0; i < sprites.size(); i++)
sprites.get(i).render(batch);
}
}
So now when I create a new sprite, it will automatically render without me having to manually add it into the ArrayList.
The problem is that sprites can be disposed during the time that for loop is running, and thus some sprites won't be rendered for that specific frame.
I do not wish to loop through the items backwards such as:
for (int i = sprites.size() - 1; i >= 0; i--)
sprites.get(i).render(batch);
Because this will render my sprites out of order (sprites that should be on top will be covered).
My only solution so far has been to have another ArrayList that keeps track of which objects to dispose, as such:
public class Sprite {
public static ArrayList<Sprite> sprites = null, garbage = null;
//everything else the same
public void dispose() {
garbage.add(this);
}
}
And then during render, I first remove all the garbage from sprites, then render sprites:
for (int i = 0; i < garbage.size(); i++)
sprites.remove(garbage.get(i));
garbage.clear();
for (int i = 0; i < sprites.size(); i++)
sprites.get(i).render(batch);
This method works, but I feel it is inefficient. (Having two arraylists? Then doing two for loops?)
Is there any way I can loop through Sprites without it skipping a Sprite when a Sprite is disposed? I don't want to loop through Sprites backwards though because it will mess up the order.
I've now tested this with a synchronized list, but I am unable to get this working properly.
Testable code, (with java.util.ConcurrentModificationException
)
import java.util.*;
class Ideone {
public static class Sprite {
public static List<Sprite> sprites = null;
public int age = 0;
public Sprite() {
synchronized(sprites) {
sprites.add(this);
}
}
public void render() {
age++;
if (age > 30)
dispose();
}
public void dispose() {
synchronized(sprites) {
sprites.remove(this);
}
}
}
private static List<Sprite> sprites = Collections.synchronizedList(new ArrayList<Sprite>());
public static void main(String[] args) {
Sprite.sprites = sprites;
new Sprite();
new Sprite();
for (int i = 0; i < 60; i++)
render();
}
public static void render() {
synchronized(sprites) {
Iterator<Sprite> iterator = sprites.iterator();
while (iterator.hasNext())
iterator.next().render();
}
}
}