0
\$\begingroup\$

Hello I am trying to delay a part of the processes in my game, but it happend that I use one of my variables in an inner class so I have to make it final, but by making it final I can't assign new value when I find it. All I am asking for is to explain me how to do the delay without making my variable final. Here is my code:

public class TomatoScript implements IActorScript {


private CompositeActor tomato;
private static final int speed = 100;
private BitmapFont bFont;
private Batch bch;
ArrayList<FruitScript> fruits;
private final int dmg = 10;
private int hp = 50;
private Rectangle bounds;
private int state = 0;
private FruitScript collidetFruit;

private boolean dead() {
    return hp <= 0;
}

private void setHp(int dmg) {
    hp = hp - dmg;
}


public TomatoScript(Batch b, BitmapFont bf, ArrayList<FruitScript> fruits) {
    this.bch = b;
    this.bFont = bf;
    this.fruits = fruits;
}

@Override
public void init(CompositeActor entity) {
    this.tomato = entity;
    tomato.setPosition(200, 129);
    bounds = new Rectangle(tomato.getX(), tomato.getY(), tomato.getWidth(), tomato.getHeight());
    bch.begin();
    bFont.draw(bch, Integer.toString(hp), tomato.getX() + 20, tomato.getY() + 95);
    bch.end();


}

@Override
public void act(float delta) {
    switch (state) {
        case 0:
            tomato.setX(tomato.getX() + speed * delta);
            bounds.setX(tomato.getX());
            bch.begin();
            bFont.draw(bch, Integer.toString(hp), tomato.getX() + 20, tomato.getY() + 95);
            bch.end();
            if (tomato.getX() >= 1090) {
                tomato.remove();
            }
            if (collisionFound()) {
                state++;
            }
            break;
        case 1:
            if (collisionAct(collidetFruit)) {
                state = 0;
            }
            break;


    }


}


private boolean collisionFound() {
    for (FruitScript f : fruits) {
        if (f.getBounds().overlaps(bounds)) {
            collidetFruit = f;
            return true;
        }

    }
    return false;

}

private boolean collisionAct(FruitScript f) {
    com.badlogic.gdx.utils.Timer.schedule(new com.badlogic.gdx.utils.Timer.Task() {
        @Override
        public void run() {
            f.setCol(true);//here it says I should make the "f" final.
            f.setHp(dmg);//here also.
            setHp(f.getDmg());
        }
    },2);

    if (f.dead()) {
        fruits.remove(f);
        return true;
    }
    if (dead()) {
        if (!f.dead()) {
            f.setCol(false);
            tomato.getScripts().remove(this);
            tomato.remove();
        }
        return false;
    }

    return false;
}

@Override
public void dispose() {
}}

I've tried to make it final, but it ruins everything it's supposed to stop the actors do the damage taking and then the one alive to continue, but it was doing it too fast in a blink of an eye. When you make the f final, it can't get the new item it should collide with. I hope someone can help :)

\$\endgroup\$
  • \$\begingroup\$ Did you tried to change your method signature to "private boolean collisionAct(final FruitScript f)" ? \$\endgroup\$ – elenfoiro78 Dec 19 '15 at 14:53
  • \$\begingroup\$ @elenfoiro78 yes I tried \$\endgroup\$ – nitheism Dec 20 '15 at 10:41
  • \$\begingroup\$ but as I explained it does not work right and and disposing my actor from the scene goes wrong \$\endgroup\$ – nitheism Dec 20 '15 at 15:22
  • \$\begingroup\$ In fact, if the Timer wants to have f as final is because it needs to be sure that it will still be alive or point to the same thing the time it does its operations. \$\endgroup\$ – elenfoiro78 Dec 20 '15 at 15:31
1
\$\begingroup\$

As wrote in my comment, f should be final because the timer created with an enclosure need to be sure that f will remain the same during the run() execution.

I would try to make a class deriving com.badlogic.gdx.utils.Timer.Task (lets say you call it myTimerTask) with a creator that takes the FruitScript as parameter and override run().

When in collisionAct(), you instanciate a new MyTimerTask(f) and pass it to com.badlogic.gdx.utils.Timer.Schedule().

This way you should avoid enclosure restriction.

Hope it helps.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Thanks mate guess the whole thing was to make it separate :) \$\endgroup\$ – nitheism Dec 20 '15 at 16:53
  • \$\begingroup\$ but it seems that it also delays my actor removals any ideas on this? \$\endgroup\$ – nitheism Dec 20 '15 at 17:02
0
\$\begingroup\$

Solution 1

Well, let's look at the LibGDX application lifecycle:

  • new LwgjlApplication(parametershere)
  • create()
  • while(true && time since last frame >= 1000000000 / fps) render()
  • dispose()

Now if we look at this logically then most LibGDX applications run at 60 FPS but they also run on a sort of timer. The timer system that LibGDX uses is the Display.sync() method from LWJGL but we can't use that as we don't want to drop FPS

If we go deeper, what does Display.sync() use? It uses the base System.nanoTime() timing system which allows for precise nanosecond reactions in your program and it's built-in to Java!

How do we use this to do our delay then? We can use a few variables and an if statement to do the work for us easily. An example of this is posted below:

public class Timer {
    public static void main(String[] args) {
        long timeOfIncident = 0, timeTillReaction = 1000000000;
        boolean condition = true, timeOfIncidentSet = false, reactionOccurred = false;

        while(!reactionOccurred) {
            if(condition && !timeOfIncidentSet) {
                timeOfIncident = System.nanoTime();
                timeOfIncidentSet = true;
            }

            if(System.nanoTime() >= timeOfIncident + timeTillReaction) {
                System.out.println("Reaction in 1 second / 1 billion nanoseconds!");
                reactionOccurred = true;
            }
        }
    }
}

Solution 2

We can use the default LibGDX Timer class:

float delayInSeconds = 4;
SpriteBatch exampleBatch = new SpriteBatch();
BitmapFont font = new BitmapFont();

Timer.schedule(new Task() {
    @Override
    public void run() {
        exampleBatch.begin();
        font.draw(exampleBatch, "Draws after 4 seconds!", Gdx.graphics.getWidth() / 2,
                Gdx.graphics.getHeight() / 2);
        exampleBatch.end();
    }
}, delayInSeconds);

Hope this helps!

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.