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.
Related
I'm currently working on a tile collision system for an RPG style game and it mostly working except for some inconsistency with rectangle intersection.
protected void tileCollision()
{
AnimatedSprite player = findPlayer();
for(int i = 0; i < _sprites.size(); i++)
{
AnimatedSprite spr = _sprites.get(i);
for(int j = 0; j < tileWithinRange.length; j++)
{
Tile tile = tileWithinRange[j];
if(tile != null)
{
if(tile.getBounds().intersects(player.getBounds()))
{
player.setCollided(true);
tileCollision(player, tile, -1, -1);
} else
{
player.setCollided(false);
}
}
}
}
}
When I first collide with a tile upon launching the game, it always returns true, but if I move along a column of tiles, I start getting false returns and then after a while I only get false returns.
Here is an image of the player intersecting with a tile
There is an obvious intersection here, yet in this scenario, the variable collided returned false.
What is going wrong that the intersection isn't always registering?`
There is too little info here to really know what's happening, but something looks fishy here:
if(tile.getBounds().intersects(player.getBounds()))
{
player.setCollided(true);
tileCollision(player, tile, -1, -1);
} else
{
player.setCollided(false);
}
Since you are checking multiple tiles against the player in a loop, what if the player collides with the first tile, but doesn't collide with the second tile? You'd end up calling player.setCollided(false); even though he collided with a former tile, overwriting that true collision state with a false one. That might explain the behavior you're getting where you're getting only false returns after a while (perhaps because of the order in which you are checking the tiles makes it so you keep overwriting true states with false states).
I'm not sure if that's desirable or not to have these kinds of side effects, but it is a bit confusing at the very least to have this kind of collision state first being turned on and then overwritten with off within the same loop. If that's undesirable behavior, perhaps what you're after needs you to break out of the loop if a collision occurs. Or perhaps what you want is something more like this:
player.setCollided(false);
for(int j = 0; j < tileWithinRange.length; j++)
{
Tile tile = tileWithinRange[j];
if(tile != null && tile.getBounds().intersects(player.getBounds()))
{
player.setCollided(true);
tileCollision(player, tile, -1, -1);
}
}
}
Or maybe your bounds/intersection function is genuinely malfunctioning -- it's too hard to tell with so little code/info (we don't know how these are implemented).
It's good to be able to isolate your code and learn how to construct test cases to test out individual functions in little pieces independently. You want to eliminate suspects through a process of elimination with a testing procedure that allows you to figure out, "Okay, this part works perfectly in every case, let's move on to the next thing." Otherwise it becomes a guessing game trying to figure out what went wrong. One of the easiest ways to lose a time of time/productivity in development is to write a bunch of code first and then try to narrow down what went wrong in hindsight, instead of testing each little babystep. Mistakes get more expensive the later you discover them and the more suspects you have to go through in an investigation.
I ended up rewriting the method into a boolean which made it return true when colliding and false when not. Here is the new code.
public boolean tileCollision() {
AnimatedSprite player = findPlayer();
for(int j = 0; j < tileWithinRange.length; j++) {
Tile tile = tileWithinRange[j];
if(tile != null) {
if(tile.getTileBounds().intersects(player.getBounds()) ) {
return true;
}
}
}
return false;
}
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;
}
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
}
}
Im creating an 'Avoid the Blocks' game, and for this i need to move a character around a grid (2D array) using the keys GHKJ. Every x amount of turns (decreasing as the level increases) a shadow must appear, then that shadow becomes a block and if the player moves into that bloack they lose a life.
Most of this is done for me other than the seemingly simple taski of getting the blocks to appear, here is my code so far for the falling blocks:
public void rocked(){
int rockInit = turn;
if(rockInit > 1){
int save = turn;
System.out.println(turn + " ");
B.board[ran.nextInt(12)][ran.nextInt(12)] = shadow;
if(save == turn - 3){
B.board[rockLocX][rockLocY] = rock;
}
}
}
The system.println is simply for debugging purposes, checking the values are being accesed. Turn is increased by 1 for every move the player makes, ran.nextInt(12) is a randomly generated number between 0 and 11, and B.board is the playing board.
It looks like you're never changing "save" after you initialize it to "turn". So then when you check if(save == turn-3), it will always be false, and so never move the block in. If you want to keep track of how many turns have passed, I would recommend a private instance variable "int turnsPassed" that you can increment each turn. Then for each level, you can check if (turnsPassed % x == 0), where x is as you've described it. Hope that helps!
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.