2
\$\begingroup\$

With Scene2D, in the act() method of a Group, under certain circumstances I add actions to some children (eg a moveTo, or a fadeOut), which last a few tenths of seconds. Now I need ALL these action to complete before I start running the code in act() again. Right now I'm using a kludge with a timer, I set a variable actionRunTime before attaching the actions to children, set to the value of how long all actions should take to complete, then in act() I do

public void act(float delta){

  super.act(delta);  // to have the actions run on children

  actionRunTime -= delta;

  if (actionRunTime >= 0) return;

  // ... code to run when no action is running

This looks terrible to me. Is there some cleaner way?

\$\endgroup\$
2
\$\begingroup\$

You can create an Action that is shared across Actors and that counts down a value to zero, then use this state in conjuction with a SequenceAction to find out when a series of Actions have completed.

For example, with an Action like this;

public class CountDownAction extends Action {
    private int count;

    @Override
    public boolean act(float delta) {
        --count;
        return true;
    }

    public void setCount(int value) { count = value; }

    public boolean isComplete() { return count == 0; }
}

You can have a Group where you add Actions to the children and always add them in sequence with the same CountDownAction instance as the last Action;

package com.bornander.sandbox;

import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.scenes.scene2d.Action;
import com.badlogic.gdx.scenes.scene2d.Group;
import static com.badlogic.gdx.scenes.scene2d.actions.Actions.sequence;
import static com.badlogic.gdx.scenes.scene2d.actions.Actions.fadeIn;
import static com.badlogic.gdx.scenes.scene2d.actions.Actions.fadeOut;
import com.badlogic.gdx.scenes.scene2d.ui.Image;

public class MyGroupActor extends Group {
    private final Image actorA;
    private final Image actorB;
    private final Image actorC;

    private final CountDownAction countDown = new CountDownAction();

    public MyGroupActor(Texture texture) {
        actorA = new Image(texture);
        actorB = new Image(texture);
        actorC = new Image(texture);

        addActor(actorA);
        addActor(actorB);
        addActor(actorC);

        actorA.setBounds(0, 0, 200, 200);
        actorB.setBounds(200, 200, 200, 200);
        actorC.setBounds(400, 400, 200, 200);
    }

    @Override
    public void act(float delta) {
        super.act(delta);
        if (countDown.isComplete()) {
            System.out.println("All actions have executed");
        }
    }

    public void addSomeActions() {
        // We have three Actors to track, so set count to 3
        countDown.setCount(3);
        // Note that it is the same instance of the CountDownAction that is being used across all Actors here
        actorA.addAction(sequence(fadeOut(1),            countDown));
        actorB.addAction(sequence(fadeOut(1), fadeIn(1), countDown));
        actorC.addAction(sequence(fadeOut(0.5f),         countDown));
    }
}

In the above example, three difference sets of Actions

  • Fade out over 1 second
  • Fade out over 1 second then fade in over 1 second
  • Fade out over 1/2 second

all run with different execution times, but since the count down runs at the end of all those three, the CountDownAction will report isCompleted() == true only when all of them are done (in this case after 2 seconds) without you having to manually track the execution time.

\$\endgroup\$
1
  • 2
    \$\begingroup\$ That's beautiful, it can be used like some sort of "semaphore" and generalized to all children, so it's really possible to generally check for things like "if there's no action running at this time..." from any part of the code. Cool. \$\endgroup\$
    – persson
    Aug 16 '17 at 10:46

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.