0

I'm working on a project, similar to the mobile Game "Clash Royale", currently the program is moving the Character on the Anchorpane through an Animationtimer, to simulate a battle between two Characters I'm using an Thread, who is checking the damage & health of the two participants

The error appears when dragging multiple characters on the board

Exception in thread "JavaFX Application Thread" java.lang.IllegalThreadStateException
at java.base/java.lang.Thread.start(Thread.java:789)
at clash.royale/clash.royale.model.ActiveCards.fight(ActiveCards.java:157)
at clash.royale/clash.royale.model.ActiveCards$1.handle(ActiveCards.java:141)
at javafx.graphics/javafx.animation.AnimationTimer$AnimationTimerReceiver.lambda$handle$0(AnimationTimer.java:57)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at javafx.graphics/javafx.animation.AnimationTimer$AnimationTimerReceiver.handle(AnimationTimer.java:56)
at javafx.graphics/com.sun.scenario.animation.AbstractMasterTimer.timePulseImpl(AbstractMasterTimer.java:357)
at javafx.graphics/com.sun.scenario.animation.AbstractMasterTimer$MainLoop.run(AbstractMasterTimer.java:267)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:515)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:499)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulseFromQueue(QuantumToolkit.java:492)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$11(QuantumToolkit.java:320)
at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
at java.base/java.lang.Thread.run(Thread.java:829)   

CODE

public class ActiveCards extends Thread {
double health;
double damage;
int path;
ProgressBar healthBar;
Pane whole = new Pane();
static ArrayList<ActiveCards> active = new ArrayList<>();
boolean enemydetected = false;
boolean samewidths;
boolean sameheights;
Pane enemie;
boolean exit = false;

boolean started = false;


ArrayList<ActiveCards> onBoardCards = new ArrayList<>();

AnimationTimer move = new AnimationTimer() {
    private long lastUpdate = 0;
    private long walk = 0;

    @Override
    public void handle(long l) {

        if(l-walk >= 800_000_000.0) {
            if (whole.getRotate() == -3 || whole.getRotate() == 0 && !enemydetected) {
                whole.setRotate(5);
            }else if (whole.getRotate() == 5) {
                whole.setRotate(-3);
            }
            walk = l;
        }

        if (l - lastUpdate >= 12_000_000) {

            if (!enemydetected) {
                if (whole.getLayoutX() < 240) {
                    whole.setLayoutX(whole.getLayoutX() + 1.5);
                }

                if (whole.getLayoutY() <= 150) {
                    path = 60;
                } else if (whole.getLayoutY() > 150) {
                    path = 240;
                }

                if (whole.getLayoutY() > path - 2 && whole.getLayoutY() < path + 1) {
                    if (whole.getLayoutX() < 500 && whole.getLayoutX() >= 240) {
                        whole.setLayoutX(whole.getLayoutX() + 1.5);
                    }
                } else if (whole.getLayoutY() < path) {
                    whole.setLayoutY(whole.getLayoutY() + 1.5);
                } else if (whole.getLayoutY() > path) {
                    whole.setLayoutY(whole.getLayoutY() - 1.5);
                }


                for (ActiveCards a : active) {
                    double underX = whole.getLayoutX() - 95;
                    double overY = whole.getLayoutY() - 95;
                    double overX = whole.getLayoutX() + 95;
                    double underY = whole.getLayoutY() + 95;

                    if (a.whole != whole && !a.enemydetected && !enemydetected) {
                        if (a.whole.getLayoutY() >= overY && a.whole.getLayoutY() <= whole.getLayoutY() && a.whole.getLayoutX() >= underX && a.whole.getLayoutX() <= overX) {
                            enemydetected = true;
                            synchronized (a) {
                                a.enemydetected = true;
                            }
                            a.started = true;
                            enemie = a.whole;
                            a.enemie = whole;
                        } else if (a.whole.getLayoutY() <= underY && a.whole.getLayoutY() >= whole.getLayoutY() && a.whole.getLayoutX() >= underX && a.whole.getLayoutX() <= overX) {
                            enemydetected = true;
                            synchronized (a) {
                                a.enemydetected = true;
                            }
                            enemie = a.whole;
                            a.enemie = whole;
                            a.started = true;
                        }
                    }




                }
            }

            if (enemydetected) {

                //Reset movement Rotation
                if(whole.getRotate() == 5 || enemie.getRotate() == -5 ||whole.getRotate() == -5 || enemie.getRotate() == 5){
                    whole.setRotate(0);
                    enemie.setRotate(0);
                }

                if (whole.getLayoutY() < enemie.getLayoutY() - 1) {
                    whole.setLayoutY(whole.getLayoutY() + 1);
                } else if (whole.getLayoutY() > enemie.getLayoutY() + 1) {
                    whole.setLayoutY(whole.getLayoutY() - 1);
                } else {
                    samewidths = true;
                }


                if (whole.getLayoutX() < enemie.getLayoutX() - 32) {
                    whole.setLayoutX(whole.getLayoutX() + 1);
                } else if (whole.getLayoutX() > enemie.getLayoutX() + 32) {
                    whole.setLayoutX(whole.getLayoutX() - 1);
                } else {
                    sameheights = true;
                }

                if (sameheights && samewidths && !started) {
                        started = true;
                        onBoardCards = active;
                    try {
                        fight();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }

        lastUpdate = l;
    }
};

public void fight() throws InterruptedException {
    if(!this.isAlive()) {
        this.start();
    }
}


ActiveCards(int health, int damage, Image p, double x, double y, Pane playable) {
    ImageView iv = new ImageView();
    iv.setImage(p);
    iv.setFitHeight(30);
    iv.setFitWidth(30);
    whole.setPrefWidth(30);
    whole.setPrefHeight(30);
    whole.setLayoutX(x - 130);
    whole.setLayoutY(y - 70);

    healthBar = new ProgressBar();
    healthBar.setLayoutY(33);
    healthBar.setPrefHeight(10);
    healthBar.setPrefWidth(32);
    healthBar.setProgress(1);
    whole.getChildren().add(healthBar);
    whole.getChildren().add(iv);


    playable.getChildren().add(whole);
    playable.toFront();
    this.health = health;
    this.damage = damage;
}


public void run() {
        int i = 0;
            while (onBoardCards.size() > i && !exit) {
                ActiveCards a = onBoardCards.get(i);
                i++;
                if (a.whole == enemie) {

                    double setHealthBar = damage / a.health;
                    double setHealthBarThis = a.damage / health;
                    while (a.health > 0 && health > 0) {

                        Platform.runLater(new Runnable() {
                            @Override
                            public void run() {
                                a.healthBar.setProgress(a.healthBar.getProgress() - setHealthBar);
                            }
                        });

                        a.health = a.health - damage;
                        try {
                            TimeUnit.SECONDS.sleep(1);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                        if (a.health > 0) {
                            Platform.runLater(new Runnable() {
                                @Override
                                public void run() {
                                    healthBar.setProgress(healthBar.getProgress() - setHealthBarThis);
                                }
                            });
                            health = health - a.damage;
                            try {
                                TimeUnit.SECONDS.sleep(1);
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                        }
                    }

                    if (a.health <= 0) {
                        a.whole.setVisible(false);
                        a.whole.setDisable(true);
                        a.move.stop();
                        active.remove(a);
                        enemydetected = false;
                        this.started = false;
                        exit = true;
                    }
                    if (health <= 0) {
                        whole.setVisible(false);
                        whole.setDisable(true);
                        active.remove(this);
                        move.stop();
                        a.enemydetected = false;
                        a.started = false;
                        exit = true;
                    }
                }
            }
        }

}

  • 5
    There seems to be a whole lot of unrelated code so I didn't read it thoroughly but here's a guess: are you by any chance trying to restart a thread that already ended? Note the Javadoc on `Thead.start()`: "It is never legal to start a thread more than once. _In particular, a thread may not be restarted once it has completed execution._" (emphasis by me) – Thomas Oct 13 '22 at 07:27
  • 3
    In addition to the previous comment: the JavaDoc for [Thread.start()](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Thread.html#start()) explicitly says: _Throws: IllegalThreadStateException - if the thread was already started._ – Thomas Kläger Oct 13 '22 at 07:52
  • 4
    I'd advise getting rid of your own threads completely and run everything on the JavaFX thread. Rewrite your algorithm, remove all of the synchronization and runlater scheduling, remove the errors in updating the scene graph from another thread, decrease the complexity, remove the errors in thread life cycle management, etc. Look into Timeline, or enhance your [game loop](https://www.gameprogrammingpatterns.com/game-loop.html) to be able to work out when something should occur based on the time and action it (Timeline is probably easier...). – jewelsea Oct 13 '22 at 09:58
  • 4
    Study, [JavaFX periodic background task](https://stackoverflow.com/a/60685975/1155209) and [eden coding periodic background tasks](https://edencoding.com/periodic-background-tasks). – jewelsea Oct 13 '22 at 10:01
  • @jewelsea I tried using the Animationtimer for the fighting as well, but that freezes the UI so i tried a seperate thread for that – JavaVariable Oct 15 '22 at 16:34

0 Answers0