You could do this:
for (Obstacle obstacles : obstacleList) {
// Should always update before rendering, or your objects will be a frame behind
obstacles.update();
obstacles.render();
// If we overlap and the obstacle is alive, we want to hurt the player, kill the obstacle and start a timer
if (obstacles.getCollision().overlaps(player.getCollision())
&& obstacles.getAlive() == true && player.getTimedOut == false) {
// Remove life as usual
currentLives--;
// Kill off the obstacle, this will now stop this statement from executing next loop iteration
obstacles.setDead();
// Set the timer
player.setTimedOut();
// If the player is in timeout mode, call revive
}else if(player.getTimedOut()){
// There is a problem with this and I will tell you below
player.revive();
}
}
}
However…this way is quite ineffective. The reason being is that the timing mechanism is dependent on the loop. Which is bad, like so.
You have 1001 obstacles, the player collides with the first one and the revive code starts getting called for the remaining 1000 iterations of the for loop. Now because of this:
reviveTime += 1;
This is going to go from 0 to 1000 almost instantaneously. Meaning the time out is non existent. Say you have 50 obstacles instead, the timeout will last longer.
This is inconsistent.
What I suggest you do is, set a flag inside the loop to indicate the player is dead and handle the reviving outside of the loop in the players update method. This will also allow you to clean up the for loop a little as well.
Like so:
for (Obstacle obstacles : obstacleList) {
// Should always update before rendering, or your objects will be a frame behind
obstacles.update();
obstacles.render();
// We know the player is dead and can not collide, why check the rest of the block? To the next obstacle we go!
if(player.isDead() == true) continue;
// We can remove the extra check for the timed out, the code will never reach this statement if the player is dead anyway
// We can also have better flow control of the program here and remove nesting another if statement
// If we don't overlap or the obstacle is dead, we just continue to the next obstacle, removing nested if statement
if (obstacles.getCollision().overlaps(player.getCollision()) == false || obstacles.getAlive() == false) continue;
// Remove life as usual
currentLives--;
// Kill off the obstacle, this will now stop this statement from executing next loop iteration
obstacles.setDead();
// Set that the player is dead, we can just remove the timedOut code all together, we don't need it.
player.setDead(true);
}
}
Now in the players update method:
public void update(float delta){
// If the player is dead, we want to call revive
if(isDead())
revive();
// Rest of your player code here
}
And the changed revive call ofc! Because we now have a proper flag
public void revive(){
// You might want to change this to a proper time, if you are say running the game at 60fps then this
// would take 10000/60 = 166 frames/60 = 2.77 seconds to reach 10000. If you are using LibGDX use delta time
// if not, use nanotime for now.
reviveTime += 1;
if(reviveTime >= 10000){
// We are no longer dead! Reset the flag and the timing mechanism. This will hand control back to the first
// if statement in the loop so we can start checking for collisions again, yay!
setDead(false);
reviveTime = 0;
}
}
This is how I would do it, if you are not quite sure what is happening with the continue keyword you can read more about it HERE and HERE.
However I will briefly explain what is happening:
Say you are walking happily down the street and you come to a busy cross road with no pedestrian crossing, in your mind you are doing this:
for(Check check : checks){
if(check.isLeftClear() == true && check.isRightClear() == true){
cross();
}
}
Now in your mind you know that if both sides are clear, you cross and if not you wait and check again. However, you also know that if the left is not clear then it is not safe to cross, same goes for the right. So why do we have to check if both conditions are true when we can just check if one is false?
The answer lies with this:
for(Check check : checks){
if(check.isLeftClear() == false || check.isRightClear() == false) continue;
cross();
}
So what we are doing here is, if left is not clear, there is no point in checking the right so just go to the next check. If left is clear we then check the right, if right is not clear we continue. If both are clear we can then ignore the continue statement and move onto the rest of the block.
What benefit does this have? Well imagine you have a whole bunch of conditions in there that we don’t want to nest together (and we are not at a crossing with a pedestrian crossing, and it’s raining
):
for(Check check : checks){
// Follow the green cross code!
if(check.isGreenManOn() == true){
// Better double check anyway, some crazy ass drivers out there
if(check.isLeftClear() == true && check.isRightClear() == true){
// Make sure there is not a big dirty puddle in front of you
if(check.isThereAPuddle() == true){
stepToTheLeft();
}else{
cross();
}
}
}
}
Now you can see how messy it gets rather quickly, look at all those nested checks. We can easily tidy this up like so:
for(Check check : checks){
// Follow the green cross code!
if(check.isGreenManOn() == false) continue;
// Better double check anyway, some crazy ass drivers out there
if(check.isLeftClear() == false || check.isRightClear() == false) continue;
// Make sure there is not a big dirty puddle in front of you
if(check.isThereAPuddle() == true){
stepToTheLeft();
// Now we can do some more flow control here, what if by the time we stepped left the lights changed
// or a crazed driver is coming? We can continue to the next check before crossing, or don't. Your funeral.
continue;
}
cross();
}
And to neaten things up even further, when checking boolean statements you can simply just do
bool isDead = true;
if(isDead)
// Do something
You don’t have to explicetly state == true or == false with booleans, you can can call the functions/field directly and the program knows what to do. You can do !isDead to check for false:
for(Check check : checks){
// Follow the green cross code!
if(!check.isGreenManOn()) continue;
// Better double check anyway, some crazy ass drivers out there
if(!check.isLeftClear() || !check.isRightClear()) continue;
// Make sure there is not a big dirty puddle in front of you
if(check.isThereAPuddle()){
stepToTheLeft();
// Now we can do some more flow control here, what if by the time we stepped left the lights changed
// or a crazed driver is coming? We can continue to the next check before crossing, or don't. Your funeral.
continue;
}
cross();
}
I hope this has helped, not only with your problem but with general program flow lol. I enjoyed writing it, so someone might enjoy reading it :).
EDIT: You can also apply this in your revive code if you like:
public void revive(){
// You might want to change this to a proper time, if you are say running the game at 60fps then this
// would take 10000/60 = 166 frames/60 = 2.77 seconds to reach 10000. If you are using LibGDX use delta time
// if not, use nanotime for now.
reviveTime += 1;
// The return keyword exits out of the "outermost" block of code, in this case it is the entire method.
if(reviveTime <= 10000) return;
// We are no longer dead! Reset the flag and the timing mechanism. This will hand control back to the first
// if statement in the loop so we can start checking for collisions again, yay!
setDead(false);
reviveTime = 0;
}
Or you could do it this wait, to remove an extra line lol:
public void revive(){
// The return keyword exits out of the "outermost" block of code, in this case it is the entire method.
if(reviveTime += 1 <= 10000) return;
// We are no longer dead! Reset the flag and the timing mechanism. This will hand control back to the first
// if statement in the loop so we can start checking for collisions again, yay!
setDead(false);
reviveTime = 0;
}
And that is one big ass post, sorry lol.