0

I am making a game, what should happen is, inside a for loop while iterating over an arraylist. Inside each loop I want to add a node to my scene, then I want to wait some time and add another node to the scene. the time between each iteration is also defined in the item of the arraylist, and can be different each iteration.

What i've tried:

    //Circle is my own class, other than these attributes along with getters it has nothing in it.
    //The last number is the time to wait.
    //Also for testing i used a static arraylist, normally the items come from a json file.
    ArrayList<Circle> lvlNodes = new ArrayList<>();
    lvlNodes.add(new Circle(50,50, Color.RED,250,100));
    lvlNodes.add(new Circle(200,100, Color.BLUE,500,250));
    lvlNodes.add(new Circle(900,500, Color.YELLOW,750,500));
    lvlNodes.add(new Circle(400,50, Color.GREEN,1000,500));

   //Iterating over the arraylist and adding the nodes.
   for (int i=0; i<lvlNodes.size(); i++) {
        Circle currentNode = lvlNodes.get(i);

        //Add the node the the anchor pane.
        view.addLvlNode(currentNode, diameter); //diameter is a final value, all nodes in the game are the same and thus only added once at the top of the json file

        //Wait and move on to the next one.
        try {
           Thread.sleep(currentNode.getTimeToNextNode())
        } catch (InterruptedException e) {
            System.out.println(e);
        }
        
    }



  //Adds the next node to the game.
  public void addLvlNode(Circle circle, double diameter) {
    //Create node.
    javafx.scene.shape.Circle lvlNode = new javafx.scene.shape.Circle(circle.getPosX(), 
          circle.getPosY(), diameter/2);

    //Anchor node the the anchorpane.
    AnchorPane.setTopAnchor(lvlNode, (double)circle.getPosY());
    AnchorPane.setLeftAnchor(lvlNode, (double)circle.getPosX());
    anchrLevel.getChildren().add(lvlNode);
    lvlNode.toBack();
  }//addLvlNode.

The Thread.sleep() works, each iteration in the for loop is with the time in between. but the nodes don't get added until the for loop is done iterating.

Is there any way of adding the nodes inside the for loop with the time amount in between?

Niels
  • 306
  • 2
  • 11
  • 5
    Use an animation (e.g. `Timeline`). See [JavaFX periodic background task](https://stackoverflow.com/questions/9966136/javafx-periodic-background-task/60685975#60685975). – Slaw Apr 08 '21 at 16:54

1 Answers1

1

The problem that you're having is that all of your code is running on the FXAT, and while the FXAT is busy running that code, it can't do things like update the screen to actually add the circles to it. So when your code is finished, including all of the Thread.sleep() calls, it then does all of the screen painting - all at once.

Rather than build your custom Circle class, I've just built some arrays to hold the Circles and wait times. Then I put the addLvlNode & sleep into a background job. The background job uses Platform.runLater() to add the nodes onto the Pane on the FXAT:

import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;

import java.util.ArrayList;

public class CircleMaker extends Application {

    private Pane mainPain;

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        ArrayList<Circle> circles = new ArrayList<>();
        ArrayList<Integer> waitTimes = new ArrayList<>();
        circles.add(new Circle(50, 50, 125, Color.RED));
        waitTimes.add(1000);
        circles.add(new Circle(200, 200, 250, Color.BLUE));
        waitTimes.add(2500);
        circles.add(new Circle(900, 500, 375, Color.YELLOW));
        waitTimes.add(5000);
        circles.add(new Circle(400, 50, 500, Color.GREEN));
        waitTimes.add(5000);
        Thread waitingThread = new Thread(() -> {
            for (int idx = 0; idx < 4; idx++) {
                Circle thisCircle = circles.get(idx);
                Platform.runLater(() -> addLvlNode(thisCircle));
                try {
                    Thread.sleep(waitTimes.get(idx));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        mainPain = new Pane();
        primaryStage.setScene(new Scene(mainPain, 1000, 800));
        primaryStage.show();
        waitingThread.start();
    }

    private void addLvlNode(Node circle) {
        mainPain.getChildren().add(circle);
        circle.toBack();
    }

}

I also stretched out the wait times by a factor of 10 so that it was easier to see that the circles are each painted in turn with a pause between them.

You could probably use Timeline to do this, but since you said that the Circle data was eventually going to be JSON from an external source, that implies to me that you've probably got some kind of non-JavaFX mechanics happening so it's probably best to just use a background thread to do whatever you need. You can see from this example what the basic framework would be.

DaveB
  • 1,836
  • 1
  • 15
  • 13