I am creating a JavaFX application about a game where two armies with units fight until one army wins. My wish is to make the GUI have a label which updates every second to inform the user about the most recent event (for instance: "Red attacks Blue" then "Blue attacks Red"). The problem is my method which simulates the battle is too fast - everything gets executed and finished in milliseconds unless I use Thread.sleep, but using it causes the JavaFX program to freeze and not respond until the method is done. What alternatives from Thread.sleep can I use to give me the desired result, and is it possible to implement the solution directly into the simulate() method?
Current code for my battle simulation:
public Army simulate() {
Army winner = null;
while(armyOne.hasUnits() || armyTwo.hasUnits()) {
if(!armyOne.hasUnits()) {
winner = this.armyTwo;
return winner;
}
else if(!armyTwo.hasUnits()) {
winner = this.armyOne;
return winner;
}
Unit attackerOne = armyOne.getRandom();
Unit defenderOne = armyOne.getRandom();
Unit attackerTwo = armyTwo.getRandom();
Unit defenderTwo = armyTwo.getRandom();
attackerOne.attack(defenderTwo);
if(defenderTwo.getHealth() <= 0) {
recentEvent = attackerOne.getName() + " attacks " + defenderTwo.getName() + ", killing them!";
System.out.println(recentEvent);
armyTwo.remove(defenderTwo);
//Program gets put to sleep for 2 seconds
try {
Thread.sleep(2000);
} catch (InterruptedException exception) {
Thread.currentThread().interrupt();
}
} else {
recentEvent = attackerOne.getName() + " attacks " + defenderTwo.getName() + ", leaving them at "
+ defenderTwo.getHealth() + " HP!";
System.out.println(recentEvent);
//Program gets put to sleep for 2 seconds
try {
Thread.sleep(2000);
} catch (InterruptedException exception) {
Thread.currentThread().interrupt();
}
}
if(!armyTwo.hasUnits() && armyOne.hasUnits()) {
winner = this.armyOne;
return winner;
}
attackerTwo.attack(defenderOne);
if(defenderOne.getHealth() <= 0) {
recentEvent = attackerTwo.getName() + " attacks " + defenderOne.getName() + ", killing them!";
System.out.println(recentEvent);
armyOne.remove(defenderOne);
//Program gets put to sleep for 2 seconds
try {
Thread.sleep(2000);
} catch (InterruptedException exception) {
Thread.currentThread().interrupt();
}
} else {
recentEvent = attackerTwo.getName() + " attacks " + defenderOne.getName() + ", leaving them at "
+ defenderOne.getHealth() + " HP!";
System.out.println(recentEvent);
//Program gets put to sleep for 2 seconds
try {
Thread.sleep(2000);
} catch (InterruptedException exception) {
Thread.currentThread().interrupt();
}
}
if(!armyOne.hasUnits() && armyOne.hasUnits()) {
winner = this.armyTwo;
return winner;
}
}
return winner;
}
Code where I handle the "start battle" button in Controller which uses the method:
//Handling "Start battle" Button event.
startBattleButton.setOnAction(event -> {
Battle battle = new Battle(leftArmy, rightArmy, terrainBox.getValue());
Army winner = battle.simulate();
if(winner.equals(leftArmy)) {
winnerArmyLabel.setText(winner.getName() + " wins!");
leftUnitAmount.setText("Units: " + winner.getAllUnits().size());
rightUnitAmount.setText("Units: 0");
resetBattleButton.setVisible(true);
startBattleButton.setDisable(true);
}
if(winner.equals(rightArmy)) {
winnerArmyLabel.setText(winner.getName() + " wins!");
rightUnitAmount.setText("Units: " + winner.getAllUnits().size());
leftUnitAmount.setText("Units: 0");
resetBattleButton.setVisible(true);
startBattleButton.setDisable(true);
}
});