Removing Object from ArrayList causes lag - java

I have an ArrayList which holds Planes (enemies) on my android game. These planes move from one side of the screen to the other and the user has to dodge them. When a plane's x value becomes less than -50 it is removed from the ArrayList. When this happens, it causes all the planes on the screen currently to 'jump' slightly. They disappear for a few milliseconds and then are re-drawn but 2px behind where they are supposed to be.
Here is the paint method, where planes is the ArrayList
public void onDraw(){
bg1.onDraw(c);
bg2.onDraw(c);
chopper.onDraw(c);
score.onDraw(c);
// PAINTS THE PLANE OR DELETES IF OFF SCREEN
for (int i = 0; i < planes.size(); i++) {
Plane p = planes.get(i);
if(p.getX()<-50){
planes.remove(p);
}else{
p.onDraw(c);
if (p.getX() < 170) {
detectPlaneCollision(p, c);
}
}
}
}
Is there a way of fixing this? Should I use a different data structure?
Thanks
Tom

I think your problem is nothing to do with your choice of data structure, but instead because you are modifying your List while you're looping over it.
Imagine you have three planes in your list - [P1,P2,P3].
On the first iteration of your loop i is 0, you process P1
P1 has getX() < -50 so you remove it, making the list now [P2,P3]
On the next iteration of the loop i is now 1 so you process P3.
This means P2 never gets processed and so will never be drawn, making it disappear briefly.
Try using an Iterator which will allow you to safely remove items while looping over the List.
Iterator<Plane> i = planes.iterator();
while (i.hasNext()) {
Plane p = i.next();
if (p.getX() < -50) {
i.remove();
} else {
p.onDraw(c);
if (p.getX() < 170) {
detectPlaneCollision(p, c);
}
}
}
Although, since you don't care about the order in which you process your Plane obejcts you could consider storing them in a Set rather than a List. A Set doesn't have to worry about maintaining an order of items as you add and remove them.

If you do a lot of removing and adding from a list, consider using a LinkedList. An ArrayList is more suitable, if there's very little change in the dataset.
Although you must also consider the fact that getting elements from a LinkedList is somewhat slower than from an ArrayList, so keep that in mind when using a LinkedList.

I think there is a bug in that code.
What if i==0 and planes[0] gets removed?
planes[n] will move into position planes[n-1]. E.g. planes[1] will move into position planes[0].
Then i gets incremented, so i==1.
Therefore the original planes[1] (which is now planes[0]) will be skipped.

Related

Removing items from ArrayList upon collision (Java)?

As a preface, I have searched the forums but found nothing relating to my specific situation. I just started learning Java about a week ago, and this is my first foray into object oriented programming.
I'm building a basic game (think somewhat like Space Invaders when it comes to mechanics). I have a "Projectile" class, a "FallingThings" class (which is the parent class to the classes I have for objects falling down (Money, Friend, Enemy)). Each projectile that is shot is stored in an ArrayList, and as is every instance of Money, Friend, and Enemy (each in their own ArrayList).
The problem happens when I implement collision detection. First off, it takes multiple bullets to make the collision mechanism work (I think I might have just messed up some numbers here but I'm not 100% sure). Now, sometimes after I fire multiple bullets (and they are long gone), a collision is detected without me firing another bullet and a FallingThings object disappears from the screen. Also, at other times, multiple objects disappear at once. The weirdest thing is that all this is inconsistent, and not always reproducible in the same fashion. However, my collisions with the player character work perfectly.
Anyone have some ideas as to how I can fix this? My code is below:
Method in the "FallingThings" class (the ArrayLists are defined in the StartingClass (the main class)).
public void checkBulletCollision(Rectangle rectangle) {
for (int j = 0; j < Character.getProjectiles().size(); j++) {
Projectile p = (Projectile) Character.getProjectiles().get(j);
if (p.isVisible() == true) {
for (int i = 0; i < StartingClass.getMoneys().size(); i++) {
Money m = (Money) StartingClass.getMoneys().get(i);
if (m.getR().intersects(rectangle)) {
m.remove(i);
System.out.println("bullet collision");
}
}
for (int i = 0; i < StartingClass.getEnemies().size(); i++) {
Enemy e = (Enemy) StartingClass.getEnemies().get(i);
if (e.getR().intersects(rectangle)) {
e.remove(i);
}
}
for (int i = 0; i < StartingClass.getFriends().size(); i++) {
Friend f = (Friend) StartingClass.getFriends().get(i);
if (f.getR().intersects(rectangle)) {
f.remove(i);
}
}
}
}
}
My update method for projectiles:
public void update() {
y -= speedY;
if (y < 0) {
visible = false;
}
rectangle.setBounds(getRectangle());
}
I've been trying to fix this for the entire day, and still can't get a proper implementation. I have tried using ListIterator, but that caused the program to freeze and a typecasting error to be thrown.
Thank you so much for the help! =)
I suspect you are having problems because you are removing items from the list by index while looping over that list. Removing items causes the index counter to get out of allignment. Try use an actual Iterator. No need to even think about indexes. The Iterator has a remove method for these situations..
Iterator<Enemy> iterator = StartingClass.getEnemies().iterator();
while (iterator.hasNext()) {
Enemy e = iterator.next();
if (e.getR().intersects(rectangle)) {
iterator.remove();
}
}
I don't understand what m.remove does, you get the item from the list but then call "remove" on the item. do you want to delete it from the List instead?
StartingClass.getMoneys().remove(i);
Looks like the issue is that you are letting all types of loops to run. So even after removing a Money with your Projectile, you are on to removing and Enemy and a Friend also with the same Projectile - which is possible if that Projectile is fired from anything bigger or from a .44 magnum.
Anyway, to me, looks like you need to break your iteration once you remove one item with one projectile.. so your code should be like(Use generics and enhanced for loops) :
foreachprojectile:
for( Projectile projectile : Character.getProjectiles()){
if (projectile.isVisible() == true) {
Iterator<Money> iterator = StartingClass.getMoneys().iterator();
while (iterator.hasNext()) {
Money m = iterator.next();
if (m.getR().intersects(rectangle)) {
iterator.remove();
projectile.setVisible(false); // or any other method that does similar
break foreachprojectile; //Found a hit, so do not
//look for any more hits with current projectile
}
}
//And so on..
}
}
Above code probably will get done what you are looking for; but this logic can be encapsulated better to clearly communicate the intent.
I figured out what was wrong and fixed the problem. It turns out that the issue was not in my implementation of the for loops (although using a listIterator is still more efficient and less error-prone, so I've still opted to change that aspect of my code), but rather in the implementation of my collision detection. I forgot that the projectile and rectangle (for the projectile) are two different objects, and as such, the rectangle was not moving with the projectile, resulting in all sorts of problems.
I determined the issue by painting the rectangle to the screen to check how it behaved, and sure enough, it just stayed in one place, not moving with the projectile. I made some changes to the projectile's update() method so that when called, the rectangle moves with the bullet. This resulted in proper collision detection and a functioning program!
Thank you so much to everyone for all the help, I learned a lot here about writing efficient code and debugging techniques! I really appreciate it! =)

How to hard-code legal moves for fast lookup?

I have created a gameboard (5x5) and I now want to decide when a move is legal as fast as possible. For example a piece at (0,0) wants to go to (1,1), is that legal? First I tried to find this out with computations but that seemed bothersome. I would like to hard-code the possible moves based on a position on the board and then iterate through all the possible moves to see if they match the destinations of the piece. I have problems getting this on paper. This is what I would like:
//game piece is at 0,0 now, decide if 1,1 is legal
Point destination = new Point(1,1);
destination.findIn(legalMoves[0][0]);
The first problem I face is that I don't know how to put a list of possible moves in an array at for example index [0][0]. This must be fairly obvious but I am stuck at this for some time. I would like to create an array in which there is a list of Point objects. So in semi-code: legalMoves[0][0] = {Point(1,1),Point(0,1),Point(1,0)}
I am not sure if this is efficient but it makes logically move sense than maybe [[1,1],[0,1],[1,0]] but I am not sold on this.
The second problem I have is that instead of creating the object at every start of the game with an instance variable legalMoves, I would rather have it read from disk. I think that it should be quicker this way? Is the serializable class the way to go?
My 3rd small problem is that for the 25 positions the legal moves are unbalanced. Some have 8 possible legal moves, others have 3. Maybe this is not a problem at all.
You are looking for a structure that will give you the candidate for a given point, i.e. Point -> List<Point>.
Typically, I would go for a Map<Point, List<Point>>.
You can initialise this structure statically at program start or dynamically when needing. For instance, here I use 2 helpers arrays that contains the possible translations from a point, and these will yield the neighbours of the point.
// (-1 1) (0 1) (1 1)
// (-1 0) (----) (1 0)
// (-1 -1) (0 -1) (1 -1)
// from (1 0) anti-clockwise:
static int[] xOffset = {1,1,0,-1,-1,-1,0,1};
static int[] yOffset = {0,1,1,1,0,-1,-1,-1};
The following Map contains the actual neighbours for a Point with a function that compute, store and return these neighbours. You can choose to initialise all neighbours in one pass, but given the small numbers, I would not think this a problem performance wise.
static Map<Point, List<Point>> neighbours = new HashMap<>();
static List<Point> getNeighbours(Point a) {
List<Point> nb = neighbours.get(a);
if (nb == null) {
nb = new ArrayList<>(xOffset.length); // size the list
for (int i=0; i < xOffset.length; i++) {
int x = a.getX() + xOffset[i];
int y = a.getY() + yOffset[i];
if (x>=0 && y>=0 && x < 5 && y < 5) {
nb.add(new Point(x, y));
}
}
neighbours.put(a, nb);
}
return nb;
}
Now checking a legal move is a matter of finding the point in the neighbours:
static boolean isLegalMove(Point from, Point to) {
boolean legal = false;
for (Point p : getNeighbours(from)) {
if (p.equals(to)) {
legal = true;
break;
}
}
return legal;
}
Note: the class Point must define equals() and hashCode() for the map to behave as expected.
The first problem I face is that I don't know how to put a list of possible moves in an array at for example index [0][0]
Since the board is 2D, and the number of legal moves could generally be more than one, you would end up with a 3D data structure:
Point legalMoves[][][] = new legalMoves[5][5][];
legalMoves[0][0] = new Point[] {Point(1,1),Point(0,1),Point(1,0)};
instead of creating the object at every start of the game with an instance variable legalMoves, I would rather have it read from disk. I think that it should be quicker this way? Is the serializable class the way to go?
This cannot be answered without profiling. I cannot imagine that computing legal moves of any kind for a 5x5 board could be so intense computationally as to justify any kind of additional I/O operation.
for the 25 positions the legal moves are unbalanced. Some have 8 possible legal moves, others have 3. Maybe this is not a problem at all.
This can be handled nicely with a 3D "jagged array" described above, so it is not a problem at all.

Best way to put 20 elements in a coordinate system with neighbouring elements unique

This question is regarding libGDX, but I think it's in fact more Java/algorithm related.
Part of my game includes placing 20 elements out of predefined 30 elements list on a screen (so effectively a coordinate system) in 20 partially-predefined places.
By partially predefined I mean that they are predefined for each screen, but there can be dozens of screens, so they can be as well treated as random.
The elements will be selected randomly, but the elements close to each other must be unique. By close I mean in range of some arbitrary defined distance X. Effectively each place will have around 3 'close neightbours'.
The best way I can think of so far is as follows:
Calculate the distance between all places. If a given distance between A and B is lower than X put two entries in a map - one (A,B) and one (B,A)
Now start filling the places with elements
For each place create a list with all neightbours using the map from point 1 (let's call it N-list)
For each place create a temporary list with all possible (30) elements (let's call it E-list)
Get a random element from E-list
Iterate through N-list. For each place from the list get an element currently there (if there's any). For this a (place, element) map is needed, so it will be filled as the algorithm progresses.
If the found element is equal to the current random element remove this element from E-list and this place from N-list and come back to point 5
Proceed until all places are filled
Step 1 is in fact a separate algorithm, that probably can be tweaked, ex. if we calculated the A->B distance we don't need to calculate B->A, but that needs an additional map to store calculation info, etc.
I would like to know what you think of this way and if you have any ideas for a better one.
Thanks in advance for your answers.
P.S. Perhaps the terms I used could be better chosen, but I'm not a native speaker and I don't know English math terms :-)
Ok, I think I understood your solution and this is what I thought of initially. But I think it can be slightly optimized by eliminating extra pairs and maps (or maybe not :)
First, create a map of locations where key is location position (or the location itself) and value is a list of location's parents who fall within the close range. Yes it will have multiple parents, not children, it is actually the same but parents are more fitting here as we'll see.
ArrayList<Place> place_list; // your list of places here
ArrayList<Element> element_list; // your list of elements here
HashMap<Place,ArrayList<Place>> parent_map = new HashMap<Place,ArrayList<Place>>;
ArrayList<Place> a;
for (int i = 0; i < place_list.size() - 1; i++) {
Place place1 = place_list.get(i);
for (int j = i + 1; j < place_list.size(); j++) {
Place place2 = place_list.get(j);
int dist = getDistance(place1, place2);
if (dist > DISTANCE_THRESHOLD) continue;
// if this place is within range,
// add parent place to its list and put/update it to the map
a = parent_map.get(place2);
if (a == null) a = new ArrayList<Place>();
a.add(place1);
parent_map.put(place2, a);
}
}
Now we have a map of all places that have parents. Next we do the following: if place does not have parents, it can choose any random element freely. If it does have parents, it checks what elements parents own and reduces the available set of elements. After the set was reduced, any random element can be chosen from it.
HashMap<Place,Element> used_place_map = new HashMap<Place,Element>(); // key is place, value is assigned element
ArrayList<Element> tmp_element_list;
for (i = 0; i < place_list.size(); i++) {
Place place = place_list.get(i);
a = parent_map.get(place);
if (a == null) { // this place has no parents, use elements freely
tmp_element_list = element_list;
} else { // if it has parents, they have already registered their elements in used_place_map
tmp_element_list = new ArrayList<Element>();
// create list of available elements, lame
for (j = 0; j < element_list.size(); j++) tmp_element_list.add(element_list.get(j));
// now reduce it, very lame, sorry
for (Place pl : a) {
Element used_element = used_place_map.get(pl);
for (j = 0; j < tmp_element_list.size(); j++) {
if (used_element.equals(tmp_element_list.get(j)) {
tmp_element_list.remove(j);
break;
}
}
}
}
// finally, get the random index on (probably reduced) array
int element_id = Random.nextInt(tmp_element_list.size());
Element element = element_list.get(element_id);
// store our choice as future parent
used_place_map.put(place, element);
}

Unexpected behavior with ArrayList.remove()

I have a pizza code that iterates through a list of objects and checks whether they are colliding or not. If one is collided with, it is removed from the ArrayList.
for (int i = 0; i < arrayList.size(); i++) {
Object c = arrayList.get(i);
if (Rect.intersects(foo.getRect(), c.getRect())) { //Android function, checks if the two rectangles are inside each other.
foo.setPosY(c.getPosY() + 11); // Always works.
foo.setVelY(bird.getVelY() * -1); // Always works.
arrayList.remove(i); // Occasionally fails under special circumcisions.
}
}
When opening the app fresh for the first time this works fine. However, if I exit with the back button and then quickly reopen the app, it will all work except occasionally, removing the object from the ArrayList wont happen.
If I close the app and then remove it from the recent apps list, it works. If I exit with the home key, or the back button and then immediately reopen, it sometimes fails to remove the object. I don't understand why this happens, as everything else still works. The two lines of code just before it function just fine. I just don't get it.
I suspect the problem is that you're skipping an element after one call to remove, because you're incrementing i and everything in the list is moving up one element. (It's not really clear what the symptoms are at the moment. If this turns out not to be the problem, it's still a problem.)
So if you call remove(2), the next element you want to look at now has index 2.
Common ways of fixing this:
Work backwards:
for (int i = arrayList.size() - 1; i >= 0; i--)
Use an iterator instead:
for (Iterator<Foo> iterator = arrayList.iterator(); iterator.hasNext(); ) {
Foo c = iterator.next();
if (...) {
iterator.remove();
}
}

How to make a method to move a character in an array?

here's the deal, I have to make a game that resembles PacMan, with a map, points, ghosts, etc.
The whole thing works as an array[8][8], it reads the positions of walls and the initial position of ghosts from a .txt file, PacMan starts at a fixed location and Fruits are random. Any blank space at the beginning of the game gets filled with a simple point pellet.
I've got the map done, it shows it and everything, but I can't seem to come up with a method that allows the player to control PacMan with the keyboard... This is what I've tried so far...
In the Player class
BufferedReader read = new BufferedReader(new InputStreamReader(System.in));
String mov = read.readLine();
if (mov.equals("w"))
{
PacMan.MoveU();
}
It then repeats that for the other movement keys.
The individual Move methods in PacMan class look like this
public static void MoverR()
{
for (int i=0;i<Tablero.length;i++)
{
for (int j=0;j<Tablero.length;j++)
{
if (Tablero[i][j] instanceof PacMan)
Tablero[i][j]=null;
Tablero[i][j+1]=new PacMan();
}
}
}
}
This obviously isn't working, so I'm wondering if anyone can help me with a more efficient way to do this? I really don't mind starting these two classes from scratch...
Thanks.
It always gives me an ArrayOutOfBounds Exception
The ArrayOutOfBounds Exception is caused by Tablero[i][j+1]=new PacMan(); when j == 7, because you try to access to an invalid position (Tablero[i][8]).
anyone can help me with a more efficient way to do this?
You don't need to check the whole array to find out the position of Pacman, you could store the position as a private variable of Pacman, but in that case you shouldn't create a new instance of Pacman every time you need to move it, like you are doing with your current implementation.
One reason why you get ArrayOutOfBoundsException is the fact that in your loop you're moving PacMan to position [i][j +1], where j + 1 may be greater than array length.
You need to check if j + 1 < Tablero.length when you're 'moving' PacMan.
Also you can simply move the same instance of PacMan instead of creating a new one:
...
if (Tablero[i][j] instanceof PacMan) {
if (j + 1 < Tablero.length) {
Tablero[i][j+1] = Tablero[i][j];
Tablero[i][j] = null;
}
}

Categories