Reputation: 97
So this is the method i'm currently using to make the countdown work.
/**
* Starts the count-down
* Triggers the handleFinish method
* Sets the user under time pressure, which actually is a good way to provocate the user to start over again, to beat the score already achieved
*/
public void startCountDown() {
countDownText.setVisible(true);
timer.schedule(new TimerTask() {
@Override
public void run() {
Platform.runLater(new Runnable() {
public void run() {
if(hasFinished)
return;
countDown--;
countDownText.setText("Time left:" + countDown);
if (countDown < 1)
handleFinish();
}
});
}
}, 1000, 1000); //Every 1 second
}
handleFinish method
/**
* Action after the timer runs out
* Disables the button, and sets a new highscore if the achieved score is greater than the current highscore
* @param highScore
*/
public void handleFinish() {
mainButton.setDisable(true);
mainButton.setText("Disabled.");
if(score > highScore) {
highScore = score;
saveData();
notificationText.setText("Notification: New highscore! " + highScore);
notificationText.setVisible(true);
resetStats();
countDownText.setVisible(false);
System.out.println("New Highscore");
return;
} else {
appearDialogue("You didn't beat your highscore! :( Try it again!");
resetStats();
}
hasFinished = true;
}
resetStats method
/**
* Resets the stats
* Changes the text of the button, sets the boolean and int @param hasStarted/ @param score to default (false) (0)
* Sets the position of the button to the default one
*/
public void resetStats() {
timer.purge();
mainButton.setText("Click to start..");
hasStarted = false;
score = 0;
mainButton.setLayoutX(201);
mainButton.setLayoutY(157);
countDown = 20;
scoreText.setVisible(false);
mainButton.setDisable(false);
System.out.println("STATS RESET");
}
calculatePosition is also a timer (TimerTask)
My problem is now, that they start "multi-threading", like more and more instances of the objects. (Like instead, of one timer counting down, there are 2 instances running, so - 2 seconds every 1 second). Same goes with the calculate position problem.
instance.purge();
purge just resets the timer if i'm using
instance.cancel();
it throws this error at me:
Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at javafx.fxml.FXMLLoader$MethodHandler.invoke(Unknown Source)
at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(Unknown Source)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(Unknown Source)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(Unknown Source)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
at com.sun.javafx.event.EventUtil.fireEventImpl(Unknown Source)
at com.sun.javafx.event.EventUtil.fireEvent(Unknown Source)
at javafx.event.Event.fireEvent(Unknown Source)
at javafx.scene.Scene$ClickGenerator.postProcess(Unknown Source)
at javafx.scene.Scene$ClickGenerator.access$7900(Unknown Source)
at javafx.scene.Scene$MouseHandler.process(Unknown Source)
at javafx.scene.Scene$MouseHandler.access$1500(Unknown Source)
at javafx.scene.Scene.impl_processMouseEvent(Unknown Source)
at javafx.scene.Scene$ScenePeerListener.mouseEvent(Unknown Source)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(Unknown Source)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(Unknown Source)
at com.sun.glass.ui.View.handleMouseEvent(Unknown Source)
at com.sun.glass.ui.View.notifyMouse(Unknown Source)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$141(Unknown Source)
at com.sun.glass.ui.win.WinApplication$$Lambda$37/1109371569.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at sun.reflect.misc.Trampoline.invoke(Unknown Source)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at sun.reflect.misc.MethodUtil.invoke(Unknown Source)
... 31 more
Caused by: java.lang.IllegalStateException: Timer already cancelled.
at java.util.Timer.sched(Unknown Source)
at java.util.Timer.schedule(Unknown Source)
at MainController.startCountDown(MainController.java:233)
at MainController.start(MainController.java:91)
... 40 more
Somehow when this error occurs, the calculatePosition task does work normally (doesn't multiply itself)
Help is appreciated! :)
kind regards, Yasin
Upvotes: 0
Views: 417
Reputation: 45456
Instead of a TimerTask
you just can use a Timeline
. Read more here.
This is my short version of your game:
private static final Integer TOTALTIME = 20;
private final IntegerProperty count = new SimpleIntegerProperty(TOTALTIME);
private final Label timerLabel = new Label();
private final Button btn = new Button("Start countdown");
@Override
public void start(Stage primaryStage) {
timerLabel.textProperty().bind(count.asString());
timerLabel.setVisible(false);
final Timeline timeline = new Timeline();
timeline.setCycleCount(TOTALTIME);
KeyFrame keyFrame = new KeyFrame(Duration.seconds(1), e->count.set(count.get()-1));
timeline.getKeyFrames().add(keyFrame);
timeline.setOnFinished(e->handleFinish());
btn.setOnAction(e->{
btn.setDisable(true);
timerLabel.setVisible(true);
timeline.playFromStart();
});
VBox root = new VBox(20,timerLabel,btn);
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
private void handleFinish() {
timerLabel.setVisible(false);
btn.setDisable(false);
resetStats();
}
private void resetStats() {
count.set(TOTALTIME);
}
Note that you don't need Platform.runLater()
, since everything is executed on the JavaFX application thread.
Upvotes: 1