我有一个Sprite类:
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);
}
}
在创建任何精灵之前,我确保Sprite类引用了将要渲染的精灵池:
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);
}
}
所以现在当我创建一个新的精灵时,它会自动渲染,而不必手动将它添加到ArrayList中。
问题是sprite可以在for循环运行期间处理,因此不会为该特定帧渲染一些sprite。
我不希望向后循环这些项目,例如:
for (int i = sprites.size() - 1; i >= 0; i--)
sprites.get(i).render(batch);
因为这会使我的精灵失灵(应该在顶部的精灵将被覆盖)。
到目前为止,我唯一的解决方案是使用另一个ArrayList来跟踪要处理的对象,如下所示:
public class Sprite {
public static ArrayList<Sprite> sprites = null, garbage = null;
//everything else the same
public void dispose() {
garbage.add(this);
}
}
然后在渲染过程中,我首先从sprite中删除所有垃圾,然后渲染精灵:
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);
这种方法有效,但我觉得它效率低下。 (有两个arraylists?然后做两个for循环?)
有没有什么方法可以循环Sprite,而不会在Sprite处理时跳过Sprite?我不想向后循环Sprites,因为它会搞乱订单。
我现在使用同步列表测试了这个,但我无法正常工作。
Testable code,(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();
}
}
}
答案 0 :(得分:2)
如果您只需要处理remove
,请使用Iterator
之类的
Iterator<Sprite> itr = sprites.iterator();
while (itr.hasNext()) {
// do something with itr.next()
// call itr.remove() to remove the current element in the loop
}
Iterator
提供remove
方法从列表中删除任何项目,但请确保不向列表中添加任何新项目,否则会抛出ConcurrentModificationException
。
答案 1 :(得分:2)
首先,如果从多个线程访问sprites
列表,则需要同步对它的访问或使用线程安全数据结构(例如CopyOnWriteArrayList或CopyOnWriteArraySet)。
Sprite.dispose
可以直接从sprites
中删除。
答案 2 :(得分:0)
这对我有用clicky:
import java.util.*;
class Ideone {
public static class Sprite {
public static ArrayList<Sprite> sprites = null;
public int age = 0;
private boolean removed = false;
public Sprite() {
sprites.add(this);
}
public boolean render() {
if (removed)
return false;
age++;
if (age > 30)
dispose();
return true;
}
public void dispose() {
removed = true;
}
}
private static ArrayList<Sprite> sprites = 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() {
Iterator<Sprite> iterator = sprites.iterator();
while (iterator.hasNext())
if (!iterator.next().render())
iterator.remove();
}
}
所以,当应该删除一个精灵时,我设置一个快速布尔值,告诉主渲染循环用迭代器删除它。我将不得不调整它以使用同步列表(这样添加项目不会引发异常)。