Why is my while loop not working in paintComponent? - java

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.

Related

Infinite loop inside paint() method in an Applet does not let me interact with the buttons displayed

What I am trying to do is an Appled which throws 2 threads, each running a counter which increases itself via an infinite loop
I then use a while(true) in the Applet's paint() method, which continuously paints the counters, the problem is that I have also 2 buttons, each intended to stop each thread, but the infinite loop in the paint() method doesn't let me neither click none of them nor close the Applet's window nor anything
Here a screenshot followed by the code
btw I'm certain the problem is the paint() loop as if I disable the loop I can interact with the buttons, but the counters are obviously not updated, and weird thing is that I put the mouse cursor over the buttons to show it took the form like when you want to resize a windows but the imprpant didn't capture it :/
http://i.imgur.com/PJnDI4u.png
public class MainApplet extends Applet implements ActionListener {
private static final long serialVersionUID = -2500043816999861110L;
private Font fuente;
private Button bUno, bDos;
private HiloContador hUno, hDos;
public void init() {
setBackground(Color.LIGHT_GRAY);
fuente = new Font("Verdana",Font.BOLD,26);
bUno = new Button("Parar");
bUno.addActionListener(this);
bDos = new Button("Parar");
bDos.addActionListener(this);
bUno.setSize(40,20);
add(bUno);
bDos.setSize(40,20);
add(bDos);
hUno = new HiloContador(20);
hUno.start();
hDos = new HiloContador(40);
hDos.start();
}
#SuppressWarnings({ "deprecation", "static-access" })
public void actionPerformed(ActionEvent e) {
if(e.getSource().equals(bUno)){
hUno.parar();
bUno.setLabel("1 parado");
}else if (e.getSource().equals(bDos)){
hDos.parar();
bDos.setLabel("2 parado");
}
}
public void paint(Graphics g) {
while (true){
g.clearRect(1,1,getSize().width,getSize().height); //dibuja la ventana
g.setFont(fuente);
g.drawString(hUno.getContador()+"",40,60);
g.drawString(hDos.getContador()+"",100,60);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
in case it helps anyone, solved deleting the infinite loop and adding this method
Timer timer = new Timer();
timer.schedule( new TimerTask() {
public void run() {
repaint();}
}, 0, 1000);

Game loop completely freezes my program

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.

Java: Perform Action Every X Seconds

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();
}
}
}

Repainting Continuously in Java

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.

Using awt Paint in Multi threading

I have a school project and i want to dedicate this for New Year, the project that i came up is a Text-Firework, i am using characters and symbols as the Explosion particles, and just constantly changing their X and Y position inside Paint().
I am confused on how to use Paint and Thread together. The problem is it's not painting on the screen or maybe the thread is not starting. (i cant really tell, im sorry). the problem is i dont get any error, it just doesn't work :(
the code is a little bit long i think, thank you for reading it.
How it should Works: When a user click, a Firework Thread will be started on the mouse position,
this Firework class has a paint loop for recreating the incremental explosion. so basically, i want the user to create multiple explosions thats why i made it a Thread.
here is the main applet:
public class TBFireworks extends Applet implements MouseListener
{
public void init()
{
setBackground( Color.black );
addMouseListener( this );
}
public void mouseEntered( MouseEvent e ) { }
public void mouseExited( MouseEvent e ) { }
public void mousePressed( MouseEvent e ) { }
public void mouseReleased( MouseEvent e ) { }
public void mouseClicked( MouseEvent e )
{
new Firework( e.getX(),e.getY(), this);
}
}
and the Firework Thread class:
class Firework extends Thread
{
Point center = new Point(0,0);
int blastRadius = 10;
Point posIncrement = new Point(0,0);
Applet applet;
public Firework(int positionX, int positionY, Applet apple)
{
center.x = positionX;
center.y = positionY;
applet = apple;
new Thread(this).start();
}
public void run()
{
while(blastRadius > 0)
{
applet.paint(applet.getGraphics());
try {
this.sleep(1000/20);
} catch (InterruptedException e) { ; }
}
}
public void paint(Graphics g)
{
if(blastRadius > 0)
{
Point[] fakeFire = {new Point(20,20),new Point(20,30),new Point(30,20)};
ApplyNextPos(fakeFire,posIncrement);
g.setColor(Color.red);
for(int xaa=1; xaa<5; xaa++) // draw the formation
{
for(int zaa=0;zaa<fakeFire.length;zaa++)
{
fakeFire[zaa] = GetQuadrant(xaa,center,fakeFire[zaa]);
}
for(int yaa=0;yaa<fakeFire.length;yaa++)
{
g.drawString("*",fakeFire[yaa].x,fakeFire[yaa].y);
}
}
posIncrement.incrementPos(5);
blastRadius--;
}
}
}
First, you seem not to be using your paint-method in the FireWork thread, you call the applet's paint method instead.
I'm a bit rosty in the applet and AWT stuff, but if it were Swing (I guess it not that different), I would suggest another approach. Painting should (can?) only be done in the EDT (Event Dispatch Thread). When the user clicks, you create a similar object to FireWork, and add that to a list. Then you start ONE thread (if not already started, that continously calls repaint on some panel. Then in the paint-method of your panel you loop the list of all fireworks and draw them.
This will also be more memory efficient, since you only use one thread.
The paint method should (normaly) only be called by the GUI when the corresponding component or part of it needs to be (re-)painted. It should not be called by the application (if not from inside another paint method).
The paint method is called to draw the actual state of the component. The state should be changed by another method/thread. The repainting of the component is forced by calling repaint.
An incomplete, untested example:
public class Animation extends Canvas {
private final List<Firework> fireworks = new ArrayList<Firework>();
public void start() {
Thread t = new Thread(new Runnable() {
#Override
public void run() {
while (true) {
step();
Thread.sleep(STEP_TIME); // InterruptedException ?
}
}
});
t.start();
}
#Override
public void paint(Graphics g) {
super.paint(g);
for (Firework fw : fireworks)
fw.draw(g); // draw the Firework
}
private void step() {
for (Firework fw : fireworks)
fw.step(); // update/move the Firework
repaint();
}
// methods for adding/deleting Fireworks, synchronization?
}

Categories