Java: Perform Action Every X Seconds - java

I've got a working Java program and I would like to draw an object on the display every X seconds. What is the best way to do this? I was thinking of using a for loop and some sleep statements, but I'm curious if there is an easier or more efficient way to go about this.
Thanks.

The simplest way would be to use a javax.swing.Timer
Timer timer = new Timer(X, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
// Update the variables you need...
repaint();
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
You might also like to have a read through
The Event Dispatching Thread
Concurrency in Swing
So you can understand why you should never use a while (true) { Thread.sleep(X) } call in Swing (inside the EDT)

ScheduledExecutorService might help here. The Javadoc shows example usage. Don't forget to call the shutdown method when you're finished.

Using Thread, this will draw a rectangle on the screen every XMilSeconds. This will stop after 5 runs. Edit the xMilSeconds for slower runs, and j > 4 for how many runs before stoping. It does freeze though, that I can't fix.
int i = 0;
private long xMilSeconds = 300;
private boolean paint;
public boolean running = true;
public void paint(Graphics g)
{
super.paint(g);
if(paint)
{
for(;i < i+1;)
{
g.drawRect(i+49,i+49,i+299,i+99);
g.setColor(Color.RED);
g.fillRect(i+49,i+49,i+299,i+99);
}
paint = false;
}
}
public void run()
{
while(running)
{
try
{
Thread.sleep(xSeconds);
paint = true;
repaint();
i++;
j++;
if(j > 4)
{
running = false;
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
}

Related

Why is my Java game crashing when I add a game loop?

I've recently started to try to learn java. I thought maybe making small games would be the best way to get familiar with java, so that's what I'm doing. So, I started my first game, and it's looking good. It's just a grid with a player in the center. Once I tried adding player movement I realized I'd need a game loop to check for key presses. That's what I tried. All I can say is that I've never made a game loop before, so this is what I attempted:
public static void main(String[] args) {
GamePanel panel = new GamePanel();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createGui();
double ns = 1000000000.0 / 60.0;
double delta = 0;
long lastTime = System.nanoTime();
long timer = System.currentTimeMillis();
while (true) {
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
while (delta >= 1) {
panel.checkKeys();
delta--;
}
}
}
});
}
Here's the issue... When I run this, my game just crashes. It doesn't even let me close out of the game. What is going on? Just in case, I will show more of the GamePanel code (please tell me if you need any more code).
public GamePanel() {
setLayout(new GridLayout(tiles_count, tiles_count));
setBackground(Color.gray);
setFocusable(true);
addKeyListener(this);
tile_size = 40;
}
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_A) {
System.out.println("A PRESSED");
player.x += 40;
left = true;
}
}
#Override
public void keyReleased(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_A) {
System.out.println("A RELEASED");
left = false;
}
}
#Override
public void keyTyped(KeyEvent e) {
}
public void checkKeys() {
if(left) {
System.out.println("LEFT");
} else {
System.out.println("!LEFT");
}
}
The game structure is like this: I have a Main class, which runs a GamePanel class as the main(String args) {} function. This issue might have to be with the fact that im using the run method in the Main class instead of the GamePanel class.
What am I doing wrong? Any help is very much appreciated!
Your while loop will never exit, and thus your program can never repaint, etc.
However, for a game loop like this, you do want an infinite loop --- you just want to run it in a different thread. In this way, the rest of your program is not blocked by the infinite loop, however you are still able to run your game loop tasks.
To do this, we make a new thread, and run our loop in there instead. Note also that after each loop we sleep for a little while (ideally you'd want to do this with time deltas, etc, this is just an example) --- this means that we cap the amount of frames per second, meaning that we don't overwork the processor for no good reason.
Thread t = new Thread(new Runnable(){
public void run() {
while (true) {
// your update logic here
try {
Thread.sleep(16);
}
catch(InterruptedException) {}
}
}
});
t.start();
I believe the issue is that you have an infinite loop. This occurs when the condition for your loop will always remain true. while(True). You either need to implement a break; statement which will terminate the loop or use a condition in your while loop that will run the loop until a certain condition is met while(some-condition == true). Hopefully, this helps.
https://codesnippets.fandom.com/wiki/Infinite_Loop

How to pause program execution without blocking a Timer (Java Swing)?

I have a Timer that shows an animation on the screen when a game event takes place. What I want to do is have the main execution pause until this animation finishes, but everything I try seems to block the Timer from running. I've tried using Thread.sleep() and calling wait() and notify() on a lock object, but with the same result. The Timer listener's actionPerformed() is never called.
This code sets up the timer:
protected void showMovingEffect(int steps, Direction dir, AnimatedImageSet imgs) {
effectsPane.runAnimation(imgs, dir, steps);
waiting = true;
while (waiting) {
synchronized(animationWaitLock) {
try {
animationWaitLock.wait();
} catch (InterruptedException e) {}
}
}
}
And the Timer and listener inside the EffectsPane object:
private void runAnimation(AnimatedImageSet ais, Direction dir, int iters) {
System.out.println("run");
imgs = ais;
top = 0;
left = 0;
topStep = dir.getRowIncrement() * 10;
leftStep = dir.getColIncrement() * 10;
iterations = iters;
index = 0;
active = true;
timer.start();
}
public void actionPerformed(ActionEvent e) {
System.out.println(index);
top += topStep;
left += leftStep;
index++;
currentImage = imgs.getImage(index);
repaint();
if (index == iterations) {
active = false;
timer.stop();
synchronized(animationWaitLock) {
animationWaitLock.notify();
}
waiting = false;
}
}
The System.out.println call at the start of the actionPerformed() is never called, so either the wait() call is also pausing the Timer, or something is blocking it. If I comment out the sleep/wait lines in showMovingEffect(), the animation runs, but the program does not pause.
One approach would be to display a modal dialog while the animation proceeds. As discussed here, user interaction will be foreclosed, but background GUI updates in response to the javax.swing.Timer will continue. You can allow the user to dismiss the dialog at any time, as shown here, but you may want to abandon the animation at that point.
no input from the user is needed.
You can block user interaction without displaying a dialog by entering a SecondaryLoop after the animation starts.
SecondaryLoop loop = Toolkit.getDefaultToolkit()
.getSystemEventQueue().createSecondaryLoop();
timer.start();
loop.enter();
When the animation concludes, exit() the SecondaryLoop:
public void actionPerformed(ActionEvent e) {
…
if (index == iterations) {
timer.stop();
loop.exit ();
…
}
}
You can try this:
timer.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// your code
}
});

Why is my while loop not working in paintComponent?

When I run this code, I see nothing but a blank(white) Panel and I would like to know why.
Here is my code:
Graph.java
public class Graph extends JPanel {
private static final long serialVersionUID = -397959590385297067L;
int screen=-1;
int x=10;
int y=10;
int dx=1;
int dy=1;
boolean shouldrun=true;
imageStream imget=new imageStream();
protected void Loader(Graphics g){
g.setColor(Color.black);
g.fillRect(0,0,x,y);
x=x+1;
y=y+2;
}
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
while(shouldrun){
Loader(g);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Do not ever call Thread.sleep() on the Event Dispatch Thread!!!
This causes the thread that actually redraws the screen and makes controls responsive to stop doing anything.
For animations, use a Timer. Don't worry about writing the while loop yourself, just tell the Timer to fire every so often, and change the values of x and y inside that timer. Something like:
// this is an **inner** class of Graph
public class TimerActionListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
x += dx;
y += dy;
}
}
// snip
private final Timer yourTimer;
public Graph() {
yourTimer = new Timer(2000, new TimerActionListener());
timer.start();
}
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.black);
g.fillRect(0,0,x,y);
}
You never change the state of shouldrun within the loop -- so it will never end.
Also, never call Thread.sleep(...) within a painting method. This method is for painting and can never be put to sleep, else the GUI will be put to sleep, will be frozen.
First of all, your paintComponent method should only handle all painting and nothing else (if possible). You should not implement your program loop within paintComponent.
The blank screen can be caused by a number of reasons. You can easily debug it manually by commenting off certain section of your codes and run it. See whether it is still blank.
At least from what I see here, your paintComponent will give your problems.
If you want an animation, you can:
Use a swing timer
Create a loop in a new thread (not Event Dispatch Thread). Your loop will look something like this:
As below:
while(running){
update();
render();
try(
Thread.sleep(1000/fps);
)catch(InterruptedException ie){
ie.printStackTrace();
}
}
Note: To make a proper loop for animation, you will need more than that.

deprecated repaint() method on mac

I try to make a standard form for simple games, but every time I try to compile it on my mac, it says:
move.java:31: error: unreachable statement
repaint();
^
Note: move.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
1 error
When I tried to compile it on a windows computer, it worked just fine! I can't find anything about fixing this, and according to the java website the repaint() method isn't deprecated! This is my code:
import java.awt.*;
public class move extends java.applet.Applet implements Runnable {
Image osI;
Graphics osG;
Thread runner;
char currkey;
int x;
int y;
public void init() {
x = 0;
y = 1;
setBackground(Color.yellow);
osI = createImage(size().width, size().height);
osG = osI.getGraphics();
}
public void start() {
if (runner == null) {
runner = new Thread(this);
runner.start();
}
}
public void run() {
while (true) {
y = 1;
}
repaint();
try { Thread.sleep(1000); }
catch (InterruptedException e) { }
}
public void stop() {
if (runner != null) {
runner.stop();
runner = null;
}
}
public boolean keyDown(Event evt, int key) {
switch (key) {
case Event.DOWN:
x = 1;
break;
}
repaint();
return true;
}
public void paint(Graphics g) {
if (x == 1) {
g.drawString("x is 1!!!", 150, 150);
} else {
g.drawString("x is geen 1!!!", 150, 150);
}
if (y == 1) {
g.drawString("y is 1!!!", 150, 175);
} else {
g.drawString("y is geen 1!!!", 150, 175);
}
g.drawImage(osI, 0, 0, this);
osG.setColor(getBackground());
osG.fillRect(0, 0, size().width, size().height);
osG.setColor(getForeground());
}
}
You error isnt saying repaint() is deprecated. In fact, using deprecated methods inst an error at all.
Its because of your infinite loop:
while (true) {
y = 1;
}
repaint();
The error is because you have unreachable code
while (true) {
y = 1;
}
means that the repaint() method will never be called as the loop never ends.
This while-loop...
while (true) {
y = 1;
}
repaint();
Will never exit, therefore, repaint can NEVER be called
Thread#stop is deprecated...
runner.stop();
You should never use it. Instead, create a AtomicBoolean flag which you can use in your while-loop and while it's value is true, keep looping. In your stop method, set the AtomicBoolean's value to false and the loop will exit.
See Java Thread Primitive Deprecation for reasons why stop (and other Thread methods are deprecated)
Applet#size is deprecated...
osG.fillRect(0, 0, size().width, size().height);
Use getWidth and getHeight
java.awt.Applet is 15+ years out of date. Seriously, instead, you should be, at the very least, using JApplet, but even then, I would recommend moving your core logic to a JPanel and simply adding that to what ever container you want to use, apart from gaining double buffering support without any extra work, you also gain the flexibility to decide how to use the component, as frankly, there are better approaches then using an applet now days.

Rectangle colouring logic

I have one three rectangles in my canvas. I wanted to change the colours of three rectangles
in a slow manner one by one.
For example: When starting the application, user should be able to see three rectangles with the same colour (blue).
After 2 secons that rectangles colour should change to red.
Again after 2 secons the next rectangles colour should get changed.
The last one is also done the same way, that means after 2 seconds of the 2nd rectangle.
I wrote in my own way. But it is not working. All the rectanlges are changed together. I want one by one.
Could anyone give me the logic.
final Runnable timer = new Runnable() {
public void run() {
//list of rectangles size =3; each contain Rectangle.
for(int i = 0 ; i < rectangleList.size();i++){
if(rectangleListt.get(i).getBackgroundColor().equals(ColorConstants.blue)){
try {
rectangleList.get(i).setBackgroundColor(ColorConstants.yellow);
Thread.sleep(1500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//rectSubFigureList.get(i).setBorder(null);
}/*else{
rectSubFigureList.get(i).setBackgroundColor(ColorConstants.blue);
}*/
}
You're likely calling Thread.sleep inside of Swing's event thread or EDT (for event dispatch thread), and this will cause the thread itself to sleep. Since this thread is responsible for all of Swing's graphics and user interactions, this will in effect put your entire application to sleep, and is not what you want to have happen. Instead, read up on and use a Swing Timer for this.
References:
Swing Timer tutorial
Swing Event Dispatch Thread and Swingworker tutorial
To expand on Hidde's code, you could do:
// the timer:
Timer t = new Timer(2000, new ActionListener() {
private int changed = 0; // better to keep this private and in the class
#Override
public void actionPerformed(ActionEvent e) {
if (changed < rectangleList.size()) {
rectangleList.setBackgroundColor(someColor);
} else {
((Timer) e.getSource()).stop();
}
changed++;
}
});
t.start();
You can set a Timer:
// declaration:
static int changed = 0;
// the timer:
Timer t = new Timer(2000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// Change the colour here:
if (changed == 0) {
// change the first one
} else if (changed == 1) {
// change the second one
} else if (changed == 2) {
// change the last one
} else {
((Timer) e.getSource()).stop();
}
changed ++;
}
});
t.start();

Categories