'instanceof' being inconsistent - java

Hey programming student here.
I have a JavaFX application. I have a pane, and I want to remove all rectangles from it. Here is the code:
public Pane yard = new Pane();
...
for(int i = 0; i < yard.getChildren().size(); i++)
{
if(yard.getChildren().get(i) instanceof Rectangle)
{
yard.getChildren().remove(i);
}
}
This works fine, sometimes. Other times it simply fails to remove anything. Thank you for your help.

You're removing items as you move forward with the index, causing you to not consider certain items for removal.
You should solve this by using an Iterator instead:
Iterator<?> it = yard.getChildren().iterator();
while(it.hasNext()) {
if(it.next() instanceof Rectangle) {
it.remove();
}
}
If you're using Java 8 you can do this instead which is more readable:
yard.getChildren().removeIf(Rectangle.class::isInstance);

This is because you are calling remove within your for loop. You can't do that. If your for loop starts with 10 entries, the item at position 6 might be a rectangle to start with. However if you remove the 5th entry in your list, what was at position 6 will be at position 5 and be skipped over.
Example:
yard.getChildren[0] = square
yard.getChildren[1] = square
yard.getChildren[2] = rectangle
yard.getChildren[3] = rectangle.
you can see the item at index 2 will be removed, meaning that the size of yard.getChildren is now 3. Your counter i will also be 3 at this point. Therefore the for loop will stop, and the last rectangle wont be removed.

This is happening because you are removing items from your list in a loop.
If you need to remove items in a loop you should be using an iterator see:
Calling remove in foreach loop in Java

When you remove item from collection then number of items in collection decreases too. So, you have to anticipate it and decrease working index to reflect such change:
public Pane yard = new Pane();
...
for(int i = 0; i < yard.getChildren().size(); i++)
{
if(yard.getChildren().get(i) instanceof Rectangle)
{
yard.getChildren().remove(i--);// i-- is the change
}
}

Related

Is there any other way to implement setOnMouseClicked on JavaFX

I had to create a matrix in javaFX. I created it without any problem with GridPane. The next thing is to create like "buttons" on the right side of the matrix, these buttons will move +1 element of the matrix to the right. Like this:
110 <-
101 <- //ie: I clicked this button
100 <-
The result:
110 <-
110 <-
100 <-
The way I handle this bit-shifting-moving was with circular linked list. I don't have any problem with that I think you can ommit that part. I use this method:
private void moveRowRight(int index){
//recives a index row and moves +1 the elements of that row in the matrix.
}
cells is the matrix
The problem is that, first the matrix can be modified by the user input i.e. 5x5 6x6 7x7, so the number of buttons will also change. I tried using a BorderPane(center: gridpane(matrix), right: VBox()) and this is the part of the code how I added some HBox inside the Vbox (right part of the border pane) and using the setOnMouseClicked.
private void loadButtonsRight(){
for(int i = 0; i < cells[0].length ; i++){
HBox newBox = new HBox();
newBox.getChildren().add(new Text("MOVE"));
newBox.prefHeight(50);
newBox.prefWidth(50);
newBox.setOnMouseClicked(e -> {
moveRowRight(i);
});
VBRightButtons.getChildren().add(newBox); //where I add the HBox to the VBox (right part of the Border Pane)
}
}
}
But then there's this problem.
Local variables referenced from lambda expression must be final or effectively final
It seems that I cannot implement lambda with a value that will change. Is there any way to help me to put "buttons" that depends of the matrix size and that uses the method I've created?
The message tells you all you need to know to fix the problem:
Local variables referenced from lambda expression must be final or effectively final
Assign your changing variable to a final constant and use the constant value in the lambda instead of the variable:
final int idx = i;
newBox.setOnMouseClicked(e ->
moveRowRight(idx);
);
If you wish to understand this more, see the baeldung tutorial
https://www.baeldung.com/java-lambda-effectively-final-local-variables

Game-Checking for collisions and deleting object from ArrayList [duplicate]

This question already has answers here:
Iterating through a Collection, avoiding ConcurrentModificationException when removing objects in a loop
(31 answers)
Closed 6 years ago.
I am doing some kind of arkanoid, and I got stuck. The way I do it is through JFrame, JPanel and with Timer. So what I do each timer update is
this
public class Controller implements ActionListener{
#Override
public void actionPerformed(ActionEvent e) {
ball.move();
desk.move();
deskCollision();
squareCollision();
repaint();
}
}
I created Arraylist of squares, and I printed them. When I check the collision with squares and ball, it works. So now I want to remove a specific square, when a ball hits it and change direction of a ball. Firstly, I tried it without any kind of a loop, like this.
if(ListOfSquares.get(24).getBounds2D().intersects(ball.getBounds2D())){
ball.dy = 1;
ball.dx = -1;
ListOfSquares.remove(24);
}
This works as well. But as I want to make a loop which will go trough all of the squares and always remove the specific square, I am lost. I have done it like this, but
it ends up with a bug -- Exception in thread "AWT-EventQueue-0" java.util.ConcurrentModificationException --
for(Square square : ListOfSquares){
int index = ListOfSquares.indexOf(square);
if (ball.getBounds2D().intersects(square.getBounds2D())) {
if(ball.dx == -1 && ball.dy == -1){
ball.dy = 1;
ball.dx = -1;
ListOfSquares.remove(index);
}
//etc...
}
}
Thanks for your help.
Iterator may help you:
Iterator<String> iter = ListOfSquares.iterator();
while (iter.hasNext()) {
Square squ = iter.next();
if (someCondition)
iter.remove();
}
Refference:How to avoid "ConcurrentModificationException" while removing elements from `ArrayList` while iterating it?

Issue with object spawning when touching the screen [LibGDX]

In my game there's raining meteorites of differnet sizes. I've tried to make it so that if you touch a bigger meteorite, it will be removed (from an array), and a smaller meteorite object will take it's place on the same place as the bigger was removed.
The problem I have is that if you touch a bigger meteorite, it will get removed and go through all other meteorite types until it gets to the smallest one and completely dissapears... all in the same touch. I obviously don't want that. Not sure how to solve this problem either.
Here's the code that handles input:
private void handleInput() {
Iterator<FallingItem> iter = items.meteorites.iterator();
while (iter.hasNext()) {
FallingItem item = iter.next();
if (Gdx.input.justTouched()) {
gameCam.unproject(touchPoint.set(Gdx.input.getX(), Gdx.input.getY(), 0));
if (item.getClass() == SmallMeteorite.class && item.getBounds().contains(touchPoint.x, touchPoint.y)) {
meteoriteDestroyed.play(0.5f); //play sound
iter.remove(); //removes item from array when it's no longer needed
item.dispose(); //dispose meteorite texture to clear up memory
score += 20; //add to score
} else if (item.getClass() == MediumMeteorite.class && item.getBounds().contains(touchPoint.x, touchPoint.y)) {
meteoriteDestroyed.play(0.5f);
iter.remove();
item.dispose();
score += 10;
items.meteorites.add(new SmallMeteorite(item.getBounds().getX(), item.getBounds().getY()));
} else if (item.getClass() == LargeMeteorite.class && item.getBounds().contains(touchPoint.x, touchPoint.y)) {
meteoriteDestroyed.play(0.5f);
iter.remove();
item.dispose();
score += 10;
items.meteorites.add(new MediumMeteorite(item.getBounds().getX(), item.getBounds().getY()));
}
}
}
}
You're adding the items to the array so they still get iterated on. Instead, you need to mark them somehow so they can be changed after the iteration. There are a few ways to do this.
One way would be to keep a separate List. Clear it before doing this loop, and then add items that need to be replaced with smaller ones to the separate list. When the loop is done on the main list, then you can iterate the separate list to add the smaller versions to the main list.
But LibGDX has an even easier way. Instead of using a List to store your meteorites, use LibGDX's SnapshotArray class. It lets you iterate on a copy of the list, so your changes to the array don't take effect until you're done with iterating:
FallingItem[] array = snapshotArray.begin();
for (int i=0; i<snapshotArray.size; i++){ //important to use snapshotArray.size, not array.length
FallingItem item = array[i];
//...
//if (something) {snapshotArray.removeIndex(i, true); snapshotArray.add(new Something());}
}
array.end();
But before you even try that, think about whether you actually need separate classes for SmallMeteorite, MediumMeteorite, etc. There is definitely such as thing as over-encapsulation. If these objects all behave the same but simply have different parameters, they should probably be a single class whose parameters are modified to represent the different sizes. Then, instead of removing and replacing them in the array, you would only need to change their parameters.
Side note: it's wasteful that you check justTouched and do the unproject inside the loop instead of outside it. A lot of redundant repetition there.

Creating a random item generator in java,to use in a libgdx project

I'm trying to create random items in a libgdx project.I'm relatively new to Java,but here is the code I've come up with for the method.
I've been at this for a week now,and figured I'd ask here for an answer.
I've been trying to come up with something that works first.So please do forgive the shabby code.
The number parameter of the method is the number of items that will be created.
The item just needs to have a random x positon,which is generated within the constraints of the width of the container.
The game is as bottom up scroller,with different platforms being generated.
private Item[] generateRandomItems(int number){
Money[] items=new Money[number];
for(int i=0;i<number;i++){
Random r=new Random();
int x =r.nextInt(120)+3;//136 is the width of the container to which the item is to be generated
Money tempitem=generateMoney(x);//generateMoney() just returns a new instance of the Money class with the created x passed in as a param.
if(i!=0) {
for (int j=0;j<i;j++) {
boolean failed=true;
while (failed) {
//getItem() returns the bounding rectangle/circle f the item
if (!Intersector.overlaps(tempitem.getItem(), items[j].getItem())) {
failed = false;
items[i] = tempitem;
}else{
Random random= new Random();
int newX=random.nextInt(120)+3;
tempitem=generateMoney(newX);
}
}
}
}else{
items[i]=tempitem;
}
}
return items;
}
I don't know if this is a correct way to do it or not,but the created Items do collide sometimes.I've been trying to find what's wrong with the code for sometime now.Any suggestions to improve the code are also appreciated.
Edit::I Know that the code is unnecessarily complicated.This is my first attempt at procedural generation.So please do forgive me.
Instead of generating a new random position if there is a collision, you should move it deliberately left or right until there is no collision. To illustrate the problem reusing random generation after each collision, if you have 10 slots and 9 slots are already filled, it could take a long time to find that open slot using random generation as you would be almost certain to hit the same object numerous times. However, if you keep track of where you’ve checked and deliberately move to a new location each time, then the worst case scenario is you’d hit each object one time before finding the empty slot.
You can check how much of an overlap there is and move the object by that amount to clear the object, then check to make sure it didn’t collide with another object next to it, if it did then keep moving it over until there is a free spot. If you hit the edge of the screen, move to the opposite side and keep moving until you find a free spot.
Also, as good coding practice you should avoid hard coding numbers (like 120+3) into method calls. Since you use the same value in multiple places, if you decide to change the width of your container to 500, then you have to change it in all those places...and if you forget to change one you’re opening yourself up for a nightmare of bug hunting. Instead you can either set an integer such as containerWidth=120 or set the container width directly using container.setWidth(120) and then use container.getWidth() each time you call your random method to get the width of the container the random value is being constrained too. Either way will work fine, whichever is better for your workflow. And I know you said this was quick and sloppy code just to get it going, so you may already be aware of this.
Thanks for the answers.I now know that checking each generated item for collision without saving the previously generated item is bad.
But I got the previous code working after some help,and wanted to share it with anyone who would need it in the future.
I moved the checking part into a new method,and added a new flag to see if the item was generated correctly,after checking for collision from all the items before it.
private Item[] generateRandomItems(int number){
Money[] items=new Money[number];
for(int i=0;i<number;i++){
Random r=new Random();
int x =r.nextInt(120)+3;
Money tempitem=generateMoney(x);
if(i>0) {
boolean generated=false;
while (!generated) {
boolean f = checkIfItemOverlapsWithPrevious(items, tempitem);
if (!f) {
items[i] = tempitem;
generated = true;
} else {
Random random = new Random();
int newX = random.nextInt(120) + 3;
System.out.println("Collided");
tempitem = generateMoney(newX);
}
}
}else{
items[i]=tempitem;
}
}
return items;
}
private boolean checkIfItemOverlapsWithPrevious(Money[] items, Money tempitem) {
for(Money item :items){
if(item!=null) {
if (Intersector.overlaps(tempitem.getItem(), item.getItem())) {
return true;
}
}
}
return false;
}

Trying to avoid a Force close after a bullet goes off screen

So I'm just messing around learning to create a Space invaders type game. I can get the bad guys to move, Great!!. Hero moves, Great!! Bullets move, Great!! However I try to remove my bullets once they leave the screen as to not eat up all resources and it force closes on me once it gets rid of the bullet. It goes off the screen. Hits the int of -2 and then we use the remove() and boom. Force Close.
Here is my code. I'm wondering if they access the size() at the same time and just cause a force close because of it.
//I removed everything that doesn't pertane to the bullets.
public class GameScreen{
Bullet bullet = world.bullet;
public GameScreen(Game game) {
super(game);
world = new World();
}
//Draws our bullets.
int bulletLength = bullet.bullets.size();
for(int i = 0; i < bulletLength; i++) {
Placement part = bullet.bullets.get(i);
x = part.x * 32 + 11;
y = part.y * 32;
g.drawPixmap(Assets.bullet, x, y);
}
Class that holds my bullets.
public class Bullet {
public List<Placement> bullets = new ArrayList<Placement>();
public Bullet() {
}
public void shoot(int x, int y){
bullets.add(new Placement(x,y));
}
public void advance(){
int len = bullets.size(); // gets all bullets.
for(int i = 0; i < len; i++) {
bullets.get(i).y --;
if (bullets.get(i).y <= -2){//removes them once they are off the screen.
bullets.remove(i);
}
}
}
This is what I use to keep track of placement.
package com.learning.planecomander;
public class Placement {
public int x, y;
public Placement(int x, int y) {
this.x = x;
this.y = y;
}
}
When going through your list to remove bullets, you can remove bullets from a list but that affects the list immediately instead of after your loop is done. Since you are traversing to the length of the list at the start, you are going off the end of the list since you've removed elements. An example is probably more helpful than that description.
Let's say you have a list with three bullets (which I'll call a, b, c to make the example easier). On a pass through the list, a and c are fine but b needs to be removed.
i = 0;
bullets[0] = a;
bullets[1] = b;
bullets[2] = c;
First loop goes fine, second loop starts like this
i = 1;
bullets[0] = a;
bullets[1] = b;
bullets[2] = c;
We remove b, but the loop keeps going
i = 2;
bullets[0] = a;
bullets[1] = c;
OH CRAP ARRAYINDEXOUTOFBOUNDS! PROGRAM CRASHES!
The way to solve this is to use a temp list to store the bullets that need to be removed, and then once your update loop is finished, make a call to bullets.removeAll(temp)
Doing two passes is a good answer. It makes the loops simpler and easy to understand. If you'd like to do it in one pass though, iterate through the list in reverse order, and when you remove a bullet you can go to the next one and not worry about blasting past the end of the ArrayList.
Alternatively, you can keep your bullets in a linked list, and run through the list with an Iterator, which you can also use to remove items from the list with. Removing from the beginning middle or end of an linked list is always a constant time operation. Whereas removing from the beginning of an ArrayList can be more expensive. If you need random access to the elements in the list, then they can be inefficient. Keep in mind though, if you're only dealing with a handful of objects, then it doesn't really matter.
For bonus points, you might want to put all of your objects in a list, and then have your central loop process them all and have your game objects respond polymorphically to calls like dead?, think, move, draw or whatever you think is appropriate.

Categories

Resources