I have a Java program that uses threads. In my run method, I have:
public void run() {
while(thread != null){
repaint();
System.out.println("hi");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
break;
}
}
}
public void paintComponent(Graphics g) {
// painting stuff
}
The problem is that the run method is executed, but the paintComponent section is not called. If this is not the right way to keep repainting the component, then how should I repaint it?
Cal repaint from a Swing Timer. That will not block the GUI, and will happen at whatever interval specified in the timer. Of course, by the nature of Swing/AWT painting, if the timer is set to repeat too fast, calls to paint might be coalesced (effectively ignored).
Also, make sure the method is an override using:
#Override
public void paintComponent(Graphics g){
You should only repaint a component when you need to (ie, when you update it).
Depending on what you're doing, you might also be interested in this. This is taken from Killer Game Programming in Java by Andrew Davison. He talks about active rendering. Your game loop is effectively:
public void run()
{
while (running)
{
gameUpdate(); // game state is updated
gameRender(); // render to a buffer
paintScreen(); // draw buffer to screen
try
{
Thread.sleep(20);
}
catch (InterruptedException e) {;}
}
}
And, the implementation of paint screen is (defined by a subclass of JComponent):
private void paintScreen()
{
final Graphics2D g2d;
try
{
g2d = (Graphics2D) this.getGraphics();
if (g2d != null && (backbuffer != null))
{
g2d.drawImage(backbuffer, 0, 0, null);
}
Toolkit.getDefaultToolkit().sync(); // sync the display on some systems [1]
g2d.dispose();
}
catch (Exception e)
{
;
}
}
From the book:
[Note 1] The call to Tookkit.sync() ensures that the display is promptly updated. This is required for Linux, which doesn't automatically flush its display buffer. Without the sync() call, the animation may be only partially updated, creating a "tearing" effect.
You have to call paint(g) for a heavy-weight container such as a JFrame. You call paintComponent(g) for light-weight containers like a JButton. See if that works.
Related
I want to draw in some Activity. I've used threads with SurfaceViews, but it doesn't push changes at all! Here you can see my previous things:
Canvas canvas = null;
try {
canvas = this.surfaceHolder.lockCanvas();
synchronized (canvas) {
this.gameSurface.update();
this.gameSurface.draw(canvas);
}
} catch (Exception e) {
// Do nothing.
} finally {
if (canvas != null) {
canvas.drawColor(Color.BLACK);
this.surfaceHolder.unlockCanvasAndPost(canvas);
}
}
try {
sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
(Thread handling example - I've checked, it'd drown only first time, but then it had not pushed any changes). So, should I change SurfaceView to something else, or this is a good solution and I have to find errors?
I've decided to stay on clear android engine. I've fixed all my problems with canvas redrowing. If anyone have some issues: just push changes inside SurfaceView to itself, just like this:
public void update() {
invalidate();
Canvas canvas = getHolder().lockCanvas(null);
this.draw(canvas);
getHolder().unlockCanvasAndPost(canvas);
}
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.
Original Question
I'm currently working on a simple application that displays a map and will later implement pathfinding logic for units. I've implemented the map and view so far and everything runs just fine until I implemented the game loop.
With the game loop enabled, the program just freezes. I can't close the window anymore and the map isn't presented, even though the game loop is executed just fine. I've used this game loop twice in the past and never had any problems until now.
Edit: The game loop continues to execute just fine while everything else freezes.
Here are the two functions involved:
public GameController() {
paused = true;
frame = new GameFrame(this);
map = new Map(500, 500);
mvm = new MapViewModel(map.getMap(), map.getWidth(), map.getHeight());
//TODO: gameLoop() currently breaks the game.
gameLoop();
}
public void gameLoop() {
double tickTime, lastTick;
for (;;) {
tickTime = System.nanoTime();
lastTick = tickTime;
//Repaints the frame
update();
while (tickTime - lastTick < NANOSECONDS_PER_UPDATE) {
try {
Thread.sleep(1);
} catch (InterruptedException ignored) {}
tickTime = System.nanoTime();
}
}
}
edit2: I'm using Swing. The actual painting happens in the paintComponent method of my GamePanel (JPanel):
#Override
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
//Paints the map
painter.paintMap(g2, controller.getMvm());
}
Obviously, if you have any further questions feel free to ask. Thanks in advance.
Solution
Here's the code I'm using now, GameController and update haven't changed.
public void gameLoop() {
timer = new Timer(MILLISECONDS_PER_UPDATE, updater);
timer.start();
}
updater is an ActionListener that I have added as a private variable to the class.
private ActionListener updater = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("test2");
update();
}
};
You could add updater locally but I prefer it this way.
You tell us nothing about what GUI library you might be using, but assuming that it might be Swing, it looks like you're running some long-running code on the Swing event thread, the main thread responsible for doing all Swing graphics and for interacting with the user. If so, then the code will prevent Swing from redrawing the GUI, freezing your application.
My suggestions are:
Don't do this. Don't run any long-running code on the main event thread.
Instead consider using a Swing Timer to do your "game loop".
Or if you must use your while loop and Thread sleep, do it off of the event thread, but then be sure that all Swing calls that mutate the state of Swing objects be done on the event thread.
For more on Swing threading, please read Concurrency in Swing.
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();
}
}
}
I want to be able to wait for Swing's repaint completion.
Example:
frame.repaint();
wait_for_repaint_to_finish();
//work
I have something like this:
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
try {
frame.repaint();
} catch (Exception e) {
e.printStackTrace();
}
}
});
Is this the correct way to do it?
The question is why do you need something like this? Why would you ever repaint the entire frame and wait for it to be complete. If we know what you are attempting to do we can probably give a better suggestion.
repaint() just schedules painting to be done. The RepaintManager will potentially consolidate multiple paint requests and do them at one time to make paintng more efficient.
Having said that, if you really need to force a repaint you can use
JComponent.paintImmediately(...);
But this should only be used as a last resort and not to fix a design problem.
You can override paintComponent():
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
...
// repaint finished here
}