Set a delay in libGDX - java

I have now tried to set up a delay in libGDX in three different ways.
First I tried to use a Timer, but if I restart the activity, the timer won't start again. This is a known issue when using GestureDetector: https://github.com/libgdx/libgdx/issues/2274
Then I tried setting up a timer using Gdx.graphics.getDeltaTime in my render method, but this doesn't work for me as I have set it to non-continous rendering. Described in answer #2 set a delay in libgdx game
Finally I tried using a while loop and System.getCurrentTimeMilliseconds, however this prevented the application from recognizing a tap while the while loop was looping.
I have also heard of DelayAction, but how does one implement that into the code? https://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/actions/DelayAction.html
Is there another way of setting a delay? How do one implement DelayAction? Or how does one fix the Timer bug in libGDX?

Inspired by Barodapride's comment, I found a solution where I make a new thread, and put the while loop here. This code will wait for 1000 ms, and then run foo() on the main thread.
new Thread(new Runnable() {
#Override
public void run() {
long time = System.currentTimeMillis();
while (System.currentTimeMillis() < time + 1000){}
Gdx.app.postRunnable(new Runnable() {
#Override
public void run() {
foo();
}
});
}
}).start();

Well i just create an interface like this:
public interface TimeEvents {
public void handleTime(float secondsToEvent,Events event, Object obj);
public void resetTimer();
public void handleEvent(Events event,Object obj);
}
and create a class implementing it for the game entity i want, the Events for my case is an enum with the event to process (Like wait for x seconds, walk for x seconds, fire for x seconds..), the object is the instance of target object i want to handle by event..
public class FooTimeEvents implements TimeEvents{
[...]
private float timeSeconds = 0;
#Override
public void handleTime(float secondsToEvent,Events event, Object obj){
timeSeconds +=Gdx.graphics.getRawDeltaTime();
if(timeSeconds > secondsToEvent){
timeSeconds-=secondsToEvent;
handleEvent(event,obj);
}
}
#Override
public void handleEvent(Events event,Object obj){
switch (event) {
case EVENT_FOO_1:
executeEventFoo1((Foo1Obj)obj);
break;
case EVENT_FOO_2:
executeEventFoo2((Foo2Obj)obj);
break;
default:
break;
}
}
[...]
you call the handleTime on render method of the entity, and the event will only execute each timeSeconds it was set to..

You can use a RunnableAction with a DelayAction. Here are the steps you would take:
Instantiate a RunnableAction and provide it with a Runnable:
RunnableAction runnableAction = Actions.run(new Runnable(){
public void run(){
// Put whatever you want to do here
}
}
Instantiate a DelayAction and provide it with your RunnableAction:
DelayAction delayAction = Actions.delay(secondsOfDelay, runnableAction);
Now in your render or update method you need to tell your delayAction to 'act':
delayAction.act(delta);
Once the delay has passed the code in your runnable should run. I'm sorry if this syntax isn't exactly correct but this should be the easiest way to go. Let me know if something doesn't work for you and I can help.

Related

can't get sequential behavior Java Swing [duplicate]

basically, I have this code which was initially working with console i/o now I have to connect it to UI. It may be completely wrong, I've tried multiple things although it still ends up with freezing the GUI.
I've tried to redirect console I/O to GUI scrollpane, but the GUI freezes anyway. Probably it has to do something with threads, but I have limited knowledge on it so I need the deeper explanation how to implement it in this current situation.
This is the button on GUI class containing the method that needs to change this GUI.
public class GUI {
...
btnNext.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
controller.startTest(index, idUser);
}
});
}
This is the method startTest from another class which contains instance of Question class.
public int startTest() {
for (int i = 0; i < this.numberofQuestions; i++) {
Question qt = this.q[i];
qt.askQuestion(); <--- This needs to change Label in GUI
if(!qt.userAnswer()) <--- This needs to get string from TextField
decreaseScore(1);
}
return actScore();
}
askQuestion method:
public void askQuestion() {
System.out.println(getQuestion());
/* I've tried to change staticaly declared frame in GUI from there */
}
userAnswer method:
public boolean userAnswer() {
#SuppressWarnings("resource")
Scanner scanner = new Scanner(System.in);
if( Objects.equals(getAnswer(),userInput) ) {
System.out.println("Correct");
return true;
}
System.out.println("False");
return false;
}
Thanks for help.
You're correct in thinking that it related to threads.
When you try executing code that will take a long time to process (eg. downloading a large file) in the swing thread, the swing thread will pause to complete execution and cause the GUI to freeze. This is solved by executing the long running code in a separate thread.
As Sergiy Medvynskyy pointed out in his comment, you need to implement the long running code in the SwingWorker class.
A good way to implement it would be this:
public class TestWorker extends SwingWorker<Integer, String> {
#Override
protected Integer doInBackground() throws Exception {
//This is where you execute the long running
//code
controller.startTest(index, idUser);
publish("Finish");
}
#Override
protected void process(List<String> chunks) {
//Called when the task has finished executing.
//This is where you can update your GUI when
//the task is complete or when you want to
//notify the user of a change.
}
}
Use TestWorker.execute() to start the worker.
This website provides a good example on how to use
the SwingWorker class.
As other answers pointed out, doing heavy work on the GUI thread will freeze the GUI. You can use a SwingWorker for that, but in many cases a simple Thread does the job:
Thread t = new Thread(){
#Override
public void run(){
// do stuff
}
};
t.start();
Or if you use Java 8+:
Thread t = new Thread(() -> {
// do stuff
});
t.start();

SwingWorker, Cancel-Button doesn't work

I have a window, with a Start- and Stop-Button. The Start-Button starts the algorithm and the stop-button should stop it. I use SwingWorker do run the algorithm in the background and normally calling worker.cancel(true) should stop the algorithm running. I also have a Label, that visualize the Status, e.g. if I press "Stop", then the Labeltext changes to "stopped", so the Problem isnt on the actionLister of the Button.
My code looks like this:
public class MainWindow extends JFrame implements ActionListener, WindowListener
{
// Some code, like generating JFrame, JButtons and other stuff not affencting the task.
Worker worker = new Worker();
public void actionPerformed(ActionEvent e)
{
boolean isStarted = false;
// Start Button
if (e.getSource() == this.buttonStart)
{
if(!isStarted)
{
System.out.println("start");
labelSuccess.setText("Mapping started!");
this.setEnabled(true);
worker.execute();
isStarted = false;
}
}
// Stop Button
if (e.getSource() == this.buttonStop)
{
labelSuccess.setText("Mapping stopped!");
worker.cancel(true);
}
}
class Worker extends SwingWorker<Void, Void> {
#Override
protected Void doInBackground() throws Exception {
if(!isCancelled())
{
mapp();
Thread.sleep(60);
if (isCancelled()) {
System.out.println("SwingWorker - isCancelled");
}
}
return null;
}
}
At this Point, pressing the Stop-Button causes just a change of the Label-Text, but the algorithm in the background is still running. This now bothers me for quite a while and I just can't get it going.
Thanks a lot for any help, much appreciated.
edit1: I generate a new instance of worker now outside of actionPerformed, so now there is no new Worker generated on every mouse click.
Maybe if you use while instead of if on doInBackground() method of Worker class you will solve your problem. You must to put out of the while loop the mapp(), because you only want to invoke it one time. You should do something like this:
class Worker extends SwingWorker<Void, Void> {
#Override
protected Void doInBackground() throws Exception {
mapp();
while(!isCancelled()){
Thread.sleep(60);
}
System.out.println("SwingWorker - isCancelled");
return null;
}
This link could be useful to understanding how to use SwingWorker.
EDIT:
As you can see on another questions like this or this, using SwingWorker has some problems to manage the cancel method, because this method Attempts to cancel execution of this task. This attempt will fail if the task has already completed, has already been cancelled, or could not be cancelled for some other reason, like Oracle explains, and those "some other reasons" are discussed on the links I've posted.
You can do solve your problem using directly Threads. Your code would be something like this:
public class MainWindow extends JFrame implements ActionListener, WindowListener
{
// Some code, like generating JFrame, JButtons and other stuff not affencting the task.
final Thread th1 = new Thread(new Runnable() {
#Override
public void run() {
mapp();
}
});
public void actionPerformed(ActionEvent e)
{
boolean isStarted = false;
// Start Button
if (e.getSource() == this.buttonStart)
{
if(!isStarted)
{
System.out.println("start");
labelSuccess.setText("Mapping started!");
this.setEnabled(true);
th1.start();
isStarted = false;
}
}
// Stop Button
if (e.getSource() == this.buttonStop)
{
labelSuccess.setText("Mapping stopped!");
th1.stop();
}
}
This solutions uses the method stop(), which is deprecated, but it works. I've tried using interrupt(), but I don't know why the thread ran till finish the execution of mapp(). Obviously, using stop() is not the best method but it works stopping the mapp() execution before it finishes.
I recommend you to learn more about SwingWorker, Thread and Task to find the best solution to your problem.
Your problem is there is no loop in the worker: if you want to cancel a process using a flag, that process should check the flag from time to time, so if your method Worker.mapp() has to be stopped, check the flag there, no just before and after calling it.

Calling run() method of TimerTask

For the problem I am solving, I have to run a series of calls at periodic intervals. To achieve this, I have implemented TimerTask. However, I also want to notify the timertask sometimes and need to call the same methods when certain conditions are met even if the timer did not expire. My code looks similar to this.
//File TimerTaskA.java
public class TimerTaskA extends TimerTask
{
#Override
public void run()
{
processEvent1();
processEvent2();
processEvent3();
}
}
//File ProcessEventManager.java
public class ProcessEventManager
{
public TimerTaskA timerTask;
public ProcessEventManager()
{
initTimerTask();
}
public void initTimerTask()
{
Timer timer = new Timer("TimerTaskA", true);
timerTask == new TimerTaskA();
timer.schedule(timerTask , 0, 10000);
}
public void conditionalTask()
{
long time = System.currentTimeMillis();
// some condition statement. here it happens to be time in millisecs ends with 2 or 3.
if (time%10 == 2 || time%10 == 3)
timerTask.run();
}
}
In the ProcessEventManager.conditionalTask() method is it correct to call TimerTask's run() method directly to get through this situation? Is there a better way design wise to solve something like this?
The processEvent methods might be time consuming methods, and I do not want the thread running ProcessEventManager to be blocked while executing those methods. For the TimerTask to take care of running those methods in both the cases when timer expires as well as the condition in ProcessEventManager.conditionalTask is satisfied, what is the best way to do it?
Basically, yes, it is possible to do as you wrote, but a clearer way will be to call some processing method from inside the TimerTask, and when you want to perform this operation, call it directly, not through the TimerTask object.
public class TimerTaskA extends TimerTask
{
public void doCoolThings()
{
processEvent1();
processEvent2();
processEvent3();
}
#Override
public void run()
{
doCoolThings();
}
}
in the other class, when needed:
timerTask.doCoolThings();
The reason as I see it, is mainly because the purpose of run is to serve as the thread (or caller) entry point, not to do a specific task.

Java: How can I time simple events to happen after X seconds?

I'm playing around with Java and I've got myself a class for an NPC in a game. One method is called when they collide with another object:
public void collided_in_to(Entity ent) {
if(ent.equals(game.player)) {
this.speak = "Ouch!";
}
}
What I want to do, which I figured was going to be simple, is set this.speak to "" after a given amount of seconds. Coming from a web background, I was looking for an equivalent of Javascripts setTimeout().
I've tried using various timer snippets, such as using Swing timers, but in that case it seemed like every timer would call the same public void actionPerformed(ActionEvent e) method, and so with multiple timers for different events I had no way to differentiate between them. Others used inline anonymous classes, but then I have no way to pass non-final parameters to it.
Is there something I'm missing for this use case, where I want very small simple things to happen after a set time? (Instance method called, variable set, etc.)
Thanks!
How about writing you own simple Timer? I would think of something like this :
public class Timer {
long start = 0;
long delay;
public Timer(long delay) {
this.delay = delay;
}
public void start() {
this.start = System.currentTimeMillis();
}
public boolean isExpired() {
return (System.currentTimeMillis() - this.start) > this.delay;
}
}
Then instantiate the Timer class as a class member and call start() when you want to start the timer.
In your method you call
public void collided_in_to(Entity ent) {
if(ent.equals(game.player)) {
if(this.timer.isExpired()) this.speak = "";
else this.speak = "Ouch!";
}
}
If you're using a game loop you could simply make a seconds passed verification.
Have you considered threads? Thread.sleep() can be used fairly effectively to time it.

Does SwingWorker has to be a nested class?

I'm wondering if SwingWorker has to be a nested class within my main GUI. I'd rather make it an external class to keep the GUI clear from any of my programs logic.
I tried to make the SwingWorker class external, which works fine for the process, unfortunately I can't access any of my GUI fields from the SwingWorker class.
Whenever I try to access an attribute, such like a label or whatever from within SwingWorker's done() method I get a nullPointer exception.
Any advice would be much appreciated!
First of all thank you very much Jeff! Works fine so far, even though I could not follow you on the second option you presented.
One of my background tasks calculates a certain size (long value), so it would be nice to get that value from my GUI.
You suggested to work with getters and setters but unfortunately I've got no idea on how to implement them in the SwingWorker class.
I tried it like this:
public void setSize(long totalSize) {
this.totalSize = totalSize;
}
public long getTotalSize() {
return totalSize;
}
The setter is invoked at the end of the doInBackground() method. Unfortunately I can't use the get() method from my GUI.
final MySwingWorker w = new MySwingWorker();
Runnable r = new Runnable() {
public void run() {
// do something with w.get()
}
};
w.setRunnable(r);
w.execute();
The object creation of "w" does not work in my case as the constructor requires an object of Runnable.
Am I missing something?
Please go easy on me, it's the first time I work with SwingWorker. :)
Again, thank you very much for your help!
You can make the SwingWorker an external class. However, just like any other class, if it can't see the variables (e.g. the label you want to set), of course it won't be able to set it. One thing you could do is pass the worker a Runnable that it executes when it is complete.
public class MySwingWorker extends SwingWorker {
private final Runnable r;
public MySwingWorker(Runnable r) {
this.r = r;
}
public void doInBackground() {...}
public void done() { r.run(); }
}
Now from the GUI, you might do something like
Runnable updateLabel = new Runnable() {
public void run() {
label.setText("myValue");
}
};
SwingWorker w = new MySwingWorker(updateLabel);
w.execute();
This gets a bit trickier if you want to use the result of the SwingWorker, though it is possible. Rather than passing the Runnable to the swing worker's constructor, you would have a setter method and then it would be something like:
final MySwingWorker w = new MySwingWorker();
Runnable r = new Runnable() {
public void run() {
// do something with w.get()
}
};
w.setRunnable(r);
w.execute();
In either case, the Runnable is functioning similarly to a closure that is executed when the worker is finished.

Categories