I've been trying for a while to understand the Handlers thingy in order to pause a game for a few seconds.. no luck so far.
I was hoping if someone could walk me through or show me a complete method that can be adapted to my needs.
which are:
I have a pong like game, when the ball hit either sides of the screen, I want to pause for a few seconds to display the score, and then to resume the game.
Thanks!
if you are using it would be easier (and more efficient) to use a library like libgdx or andengine to do this but to do it in your current case i suggest a simple solution
on the update method use a bool to know if the ball is paused
update()
{
if(!isPaused)
{
animateBall();
}
...
}
how to pause the ball
pauseBall()
{
isPaused=true;
Timer t= new Timer();
t.schedule(new TimerTask() {
#Override
public void run() {
isPaused = false;
}
}, fewSeconds);
}
Related
I'm building app for making tests. I have a scene made in SceneBuilder. There is a ImageView under image Label with Question and 3 buttons "A", "B", "C", Texts to buttons and question's Label are taken from DataBase, if you click answer new Image, question and asnwers are loading, everything works, but i want to add a Timer in the corner. When image and question show on the screen there will be "Time to read the question" and countdown from 10 to 0, and then "Time to answer" and again countdown from 10 to 0. If timer ends and there is no answer question and image will change automaticly. But the problem is that, i can do the timer, its counting down, and after this time it change the question but I dont know how to put it into Label. If inside Timer I do something like seconds-- label.setText(seconds) there is no error but when I start app there is a lot of exceptions. Can you help me how I can put this variable which is decrementing in timer after each second to Label ?
public void setTimer() {
timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
if(interval > 0)
{
timeElapsed.setText("Time to read the question: "+interval);
System.out.println(interval);
interval--;
}
else
timer.cancel();
}
}, 1000,1000);
}
Now i have something like this, in console everything is working there is a countdown from 10 to 0 but no effect in scene.
And errors:
Exception in thread "Timer-0" java.lang.IllegalStateException: Not on FX application thread; currentThread = Timer-0
at javafx.graphics/com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:291)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:423)
at javafx.graphics/javafx.scene.Parent$3.onProposedChange(Parent.java:493)
at javafx.base/com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:113)
at javafx.base/com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:108)
at javafx.controls/javafx.scene.control.skin.LabeledSkinBase.updateChildren(LabeledSkinBase.java:271)
at javafx.controls/javafx.scene.control.skin.LabeledSkinBase.lambda$new$11(LabeledSkinBase.java:219)
at javafx.controls/com.sun.javafx.scene.control.LambdaMultiplePropertyChangeListenerHandler.lambda$new$1(LambdaMultiplePropertyChangeListenerHandler.java:49)
at javafx.base/javafx.beans.value.WeakChangeListener.changed(WeakChangeListener.java:89)
at javafx.base/com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:181)
at javafx.base/com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)
at javafx.base/javafx.beans.property.StringPropertyBase.fireValueChangedEvent(StringPropertyBase.java:104)
at javafx.base/javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:111)
at javafx.base/javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:145)
at javafx.base/javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:50)
at javafx.base/javafx.beans.property.StringProperty.setValue(StringProperty.java:65)
at javafx.controls/javafx.scene.control.Labeled.setText(Labeled.java:147)
at gui.controller.StudentTestYesNoController$1.run(StudentTestYesNoController.java:40)
at java.base/java.util.TimerThread.mainLoop(Timer.java:556)
at java.base/java.util.TimerThread.run(Timer.java:506)
The problem is that you are trying to change the UI from a thread other than the application.
This should solve the problems with your current implementation
public void setTimer() {
timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
if(interval > 0)
{
Platform.runLater(() -> timeElapsed.setText("Time to read the question: "+interval));
System.out.println(interval);
interval--;
}
else
timer.cancel();
}
}, 1000,1000);
}
Also, you can take a look at something specific about the javafx - Timeline
JavaFX periodic background task
Adding on to what mr mcwolf said above, i think you need to set
Platform.setImplicitExit(false);
before Platform.runLater(); so that it will run every time the task is called.
Yes, I did search for the answer before I posted this. :)
Made board game, each piece gets roll of die and then moves step by step on squares. Wanted to follow each step with short beep. Added method "beep();" and placed it in handler that moves position of piece on screen.
When I add beep(); game works couple of moves and then simply stops, without any report or error message. View closes on screen and dialog just says "Unfortunately, BoardLS has stopped [Ok]", and nothing else happens.
When I remove beep(); game works ok.
Please, if anyone knows how beep crashes the game, and how to avoid it, tell me as simple as you can. Thanks.
Here's how I made the beep:
private void beep() {
ToneGenerator beepSound = new ToneGenerator(AudioManager.STREAM_MUSIC, 60);
beepSound.startTone(ToneGenerator.TONE_PROP_BEEP, 75);
} // Short sound
and I call it like this:
private final Handler playMove = new Handler() {
#Override
public void handleMessage(Message playMsg) {
beep();
player.setX(pcX[playPos]);
player.setY(pcY[playPos]);
}
}; // Put Player piece to new coordinates
playPos is square number where the piece is or will be,
pcX and pcY are arrays with on-screen coordinates of squares.
handler is in MainActivity,
and called from AsyncTask with playMove.sendEmptyMessage(0);
AsyncTask works directly with global variables, making no problems here.
Just release created ToneGenerator objects using release() method.
private void beep() {
ToneGenerator beepSound = new ToneGenerator(AudioManager.STREAM_MUSIC, 60);
beepSound.startTone(ToneGenerator.TONE_PROP_BEEP, 75);
beepSound.release();
}
I am replicating a classic game, Pong, in Java using JavaFX. I am using java.util.Timer, java.util.TimerTask for the game loop and JavaFX's Canvas for rendering. Is there a way to add double buffering to the Canvas so the animation doesn't flicker? Or should I approach this differently? Bellow is the code. I removed some parts of it, that I think are irrelevant, since the code is around 200 lines long.
Canvas canvas = new Canvas(stageW, stageH);
GraphicsContext gc;
public void start(Stage stage) throws Exception {
Group root = new Group();
gc = canvas.getGraphicsContext2D();
Timer loop = new Timer();
root.getChildren().add(canvas);
loop.schedule(new GameLoop(), 0, 1000 / 60);
stage.setScene(new Scene(root,stageW, stageH));
stage.show();
}
public class GameLoop extends TimerTask {
#Override
public void run() {
draw(gc);
collisionDetect();
ball.move();
}
}
public void draw() {
gc.setFill(Color.BLACK);
gc.fillRect(0, 0, stageW, stageH);
gc.setFill(Color.WHITE);
gc.fillRect(lBat.getX(), lBat.getY(), lBat.getW(), lBat.getH());
gc.fillRect(rBat.getX(), rBat.getY(), rBat.getW(), rBat.getH());
gc.fillRect(ball.getX(), ball.getY(), ball.getW(), ball.getH());
}
You should do this differently.
Timer runs its own thread. You don't need an additional thread for this task.
You are executing modifications to the displayed canvas off of the JavaFX application thread (you should not modify objects in the scene off of the JavaFX thread).
JavaFX has an in-built timer based upon a pulse that is generated for each frame by the JavaFX system. This timer is called an AnimationTimer, you should use that.
You don't need double buffering.
Other higher level facilities such as Timeline or Transitions could also be used, but they are primarily for scene graph objects and you are currently basing your implementation on a Canvas which is not well suited to them.
You could consider switching your implementation from using canvas to the scene graph, which might make the implementation a bit easier, but you can code it either way.
You don't need to double-buffer the canvas as the JavaFX architecture is a delayed drawing architecture. You issue drawing commands and invoke api to adjust the scene graph on the JavaFX application thread, then, when you are done, you relinquish control of the JavaFX application thread. JavaFX will work out internally what needs to be rendered and issue updates to the viewed image using it's internal rendering technology, which just draws complete scenes (or patches the dirty bits). The canvas internal implementation has a command queue which is flushed for each frame to render any changes to the canvas, so you don't get partial updates.
Additionally, given you have a physics based game like Pong, you might want to introduce concepts such as velocity that you apply to moving objects such as the ball and update the object position on each iteration of the callback from the animation timer (this technique is demonstrated in the bouncing ball demo below).
You may be interested in reading a couple of resources:
Background information on game loops in JavaFX
Explanation of the AnimationTimer
Bouncing Ball Demo
Sample AnimationTimer code (from the bouncing ball demo linked):
final LongProperty lastUpdateTime = new SimpleLongProperty(0);
final AnimationTimer timer = new AnimationTimer() {
#Override
public void handle(long timestamp) {
if (lastUpdateTime.get() > 0) {
long elapsedTime = timestamp - lastUpdateTime.get();
checkCollisions(ballContainer.getWidth(), ballContainer.getHeight());
updateWorld(elapsedTime);
frameStats.addFrame(elapsedTime);
}
lastUpdateTime.set(timestamp);
}
};
timer.start();
The best way to achieve 60 fps is using AnimationTimer:
You can extend it through a costume class
public class AnimationClass extends AnimationTimer {
#Override
public void handle(long nano) {
//Code here
}
}
You can implement it instantly with an anonymous class
new AnimationTimer() {
#Override
public void handle(long now) {
}
}.start();
}
A nice example is here.
Im programming a Music Player with a Seekbar.To manage it im using an Handler with a Runnable which does it update. Somehow it is lagging my UI . How can I stop this lag ?
OnCreate:
mHandler = new Handler();
When Im playing a Song:
public static void updateProgressBar() {
mHandler.postDelayed(mUpdateTimeTask, 100);
}
My Runnable:
private static Runnable mUpdateTimeTask = new Runnable() {
public void run() {
try {
long totalDuration = songService.getTotalDuration();
int currentDuration = songService.getCurrentDuration();
// Displaying Total Duration time
player_time_length.setText(""+utils.milliSecondsToTimer(totalDuration));
// Displaying time completed playing
player_time_current.setText(""+utils.milliSecondsToTimer(currentDuration));
// Updating progress bar
int progress = (int)(utils.getProgressPercentage(currentDuration, totalDuration));
SB_song.setProgress(currentDuration);
// Running this thread after 100 milliseconds
mHandler.postDelayed(this, 100);
}catch(Exception e){}
}
};
How can I prevent this lagg in my App ?
The lag is because the Runnable is executing in the UI Thread. To reduce or remove the lag, you have to reduce the amount of work you do inside the Runnable.
One thing that you can do is to remove long totalDuration = songService.getTotalDuration(); from the Runnable and instead, put it outside, like I do in my music player.
I could add more to this answer if you included the "utils" methods that you're using to convert milliseconds to human readable time.
I would say its because its running on the UI Thread which laggs. But you have to use UI elements so another Thread is not possible .. Im right ?
Make sure the text view is not set to wrap_content. That will trigger a layout pass each time you call setText.
I make this code
Timer.schedule(new Task(){
#Override
public void run() {
flyAway();
}
}, 12);
and when my game paused i do this in draw method (Actor)
Timer.instance().stop();
On resume game i do this:
Timer.instance().start();
It work, but when i pause on 2 sec and resume after 35 sec, function flyAway() executed immediately, not counting the remaining 10 seconds.
How i can paused delaySeconds. Thanks.
Sorry for my bad English.
FlyAvay code
//Муха улетает
public void flyAway(){
flyAway = 1;
targetFlyX = Gdx.graphics.getWidth() / 2;
targetFlyY = Gdx.graphics.getHeight() + 300;
this.setColor(1f, 1f, 1f, 0.2f);
this.addAction(sequence(moveTo(targetFlyX, targetFlyY, flySpeed), desctroyFlyAction));
}
Action desctroyFlyAction = new Action(){
public boolean act( float delta ) {
removeFly();
return true;
}
};
public void removeFly(){
rectBounds.set(1, 1, 0, 0);
this.remove();
}
I had this problem and I know this is late but maybe it'll help someone else. The problem is that when a Timer is created, it is given a time on the system's clock to execute rather than a specified amount of time until it executes. So when you call Timer.stop in your pause method, wait 35 seconds, then call Timer.start in the resume method, the time that task was supposed to be completed had already passed so it was executed right away.
My solution was just to save the current system's time on pause, then subtract it from the current time on resume, and add the difference as a delay to the Timer using Timer.delay() before calling Timer.start() again.
public void pause() {
timerDelay = TimeUtils.nanosToMillis(TimeUtils.nanoTime());
Timer.stop();
}
public void resume() {
Timer.delay(TimeUtils.nanosToMillis(TimeUtils.nanoTime()) - timerDelay);
Timer.start();
}