Simply Moving a Circle Across the Screen

  • (2 Pages)
  • +
  • 1
  • 2

19 Replies - 7885 Views - Last Post: 07 July 2011 - 10:15 AM Rate Topic: ****- 1 Votes

#1 Astrumas  Icon User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 11
  • Joined: 05-July 11

Simply Moving a Circle Across the Screen

Posted 05 July 2011 - 09:45 PM

Hello, new to the community but hoping to become more involved, I am a recent college graduate looking to further expand my experience. In the future I hope to be a game developer, but for now I am experimenting and trying new things with the Java language.

My issue this evening is something that I think I understand the reason that is causing the issue, but am unable to figure out a coding solution.

The following code is supposed to draw an oval and move it across the screen. My issue is that instead of a single circle moving across the frame, I get a stream of red. My understanding is that it is redrawing the same circle on top of the previous one as it loops through, so I think I have narrowed it down to something to do with repaint()

public class Pong extends JFrame implements Runnable
{

   int x_pos = 10;
   int y_pos = 100;
   int radius = 20;

   public Pong()
   {
      //Set to exit on close
      this.addWindowListener(new WindowAdapter()
      {
         @Override
         public void windowClosing(WindowEvent we)
         {
            System.exit(0);
         }
      });
      this.setSize(300, 250);
   }
   public void start()
   {
      Thread th = new Thread(this);
      th.start();
   }
   
   public static void main(String args[])
   {
      Pong p = new Pong();
      p.setVisible(true);
      p.start();
   }

   @Override
   public void run()
   {

      //infini loop
      while (true)
      {
         x_pos++;
         repaint();

         try
         {
            //Sleep thread for 20 milliseconds
            Thread.sleep(20);
         }
         catch (InterruptedException ex)
         {
            //do nothing
         }
      }
   }

   @Override
   public void paint(Graphics g)
   {
      g.setColor(Color.RED);
      g.fillOval(x_pos - radius, y_pos - radius, 2 * radius, 2 * radius);
   }
}



Any ideas on how I could somehow "erase" the previous circle and redraw the new one to simulate a moving circle?

Is This A Good Question/Topic? 0
  • +

Replies To: Simply Moving a Circle Across the Screen

#2 pbl  Icon User is offline

  • There is nothing you can't do with a JTable
  • member icon

Reputation: 8324
  • View blog
  • Posts: 31,857
  • Joined: 06-March 08

Re: Simply Moving a Circle Across the Screen

Posted 05 July 2011 - 10:04 PM

You CANNOT mix Thread with a GUI application, unless you really really know what you are doing

You will have to use a Swing.Timer
doing Thread.sleep() the way use it just put your thread to sleep, and the repaint() you called will not execute until your thread is finish so basically

repaint();
Thread.sleep(n);

you ask the GUI to repaint() itself calling the paint() method.
This will be done only when you will exit your run() method as it loops forever never your paint() method will be called

http://download.orac...wing/Timer.html
Was This Post Helpful? 2
  • +
  • -

#3 Astrumas  Icon User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 11
  • Joined: 05-July 11

Re: Simply Moving a Circle Across the Screen

Posted 05 July 2011 - 10:12 PM

View Postpbl, on 05 July 2011 - 10:04 PM, said:

You CANNOT mix Thread with a GUI application, unless you really really know what you are doing


The code used to be part of an applet tutorial that I turned into a GUI application without even thinking of the implications of using Thread within it. I have used it many times before, but not with repaint(), so lesson learned there.

Thanks for your quick response.


Ast
Was This Post Helpful? 0
  • +
  • -

#4 Astrumas  Icon User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 11
  • Joined: 05-July 11

Re: Simply Moving a Circle Across the Screen

Posted 05 July 2011 - 10:44 PM

pbl, I took your advice and implemented a swing timer (hopefully correctly).

   public Pong()
   {
      //Set to exit on close
      this.addWindowListener(new WindowAdapter()
      {

         @Override
         public void windowClosing(WindowEvent we)
         {
            System.exit(0);
         }
      });
      this.setSize(300, 250);

   }

   public void start()
   {
      Timer timer = new Timer(25, new ActionListener()
      {

         @Override
         public void actionPerformed(ActionEvent e)
         {
            x_pos++;
            repaint();
         }
      });

      timer.start();
   }

   public static void main(String args[])
   {
      Pong p = new Pong();
      p.setVisible(true);
      p.start();
   }

   @Override
   public void paint(Graphics g)
   {
      g.setColor(Color.RED);
      g.fillOval(x_pos - radius, y_pos - radius, 2 * radius, 2 * radius);
   }



I still haven't figured out the "streaming" red circle problem though, does anyone have any insight on this?

Also, with implementing a swing timer, how would I stop the timer based on a condition? I tried a infini while loop after the start and before a stop until a value of x_pos was met, but it just kept streaming.


Ast
Was This Post Helpful? 0
  • +
  • -

#5 cfoley  Icon User is offline

  • Cabbage
  • member icon

Reputation: 1939
  • View blog
  • Posts: 4,027
  • Joined: 11-December 07

Re: Simply Moving a Circle Across the Screen

Posted 06 July 2011 - 06:01 AM

You can't rely on the Graphics object giving you a blank canvas each time the component is painted. Basically, you need to point the background (which can simply be a filled white rectangle) as well as the oval.
Was This Post Helpful? 1
  • +
  • -

#6 pbl  Icon User is offline

  • There is nothing you can't do with a JTable
  • member icon

Reputation: 8324
  • View blog
  • Posts: 31,857
  • Joined: 06-March 08

Re: Simply Moving a Circle Across the Screen

Posted 06 July 2011 - 05:59 PM

View PostAstrumas, on 06 July 2011 - 01:44 AM, said:

pbl, I took your advice and implemented a swing timer (hopefully correctly).

   @Override
   public void paint(Graphics g)
   {
      g.setColor(Color.RED);
      g.fillOval(x_pos - radius, y_pos - radius, 2 * radius, 2 * radius);
   }


In your paint() method call your father, that will take care of the background
   @Override
   public void paint(Graphics g)
   {
      super.paint(g);         // <---------------------
      g.setColor(Color.RED);
      g.fillOval(x_pos - radius, y_pos - radius, 2 * radius, 2 * radius);
   }


@cfoley: hey, not the first time I catch you on that one :)
Was This Post Helpful? 2
  • +
  • -

#7 cfoley  Icon User is offline

  • Cabbage
  • member icon

Reputation: 1939
  • View blog
  • Posts: 4,027
  • Joined: 11-December 07

Re: Simply Moving a Circle Across the Screen

Posted 06 July 2011 - 06:15 PM

Remind me again why it's better than drawing my own background? ;)
Was This Post Helpful? 0
  • +
  • -

#8 pbl  Icon User is offline

  • There is nothing you can't do with a JTable
  • member icon

Reputation: 8324
  • View blog
  • Posts: 31,857
  • Joined: 06-March 08

Re: Simply Moving a Circle Across the Screen

Posted 06 July 2011 - 06:42 PM

If somewhere else in the code, the program calls the standard

panel.setBackground(Color)
panel.setBorder(..)
panel.set...

your paint() method does not need to be aware of it, unless you overload setBackgound(), setBorder(), setXXXX
Was This Post Helpful? 2
  • +
  • -

#9 cfoley  Icon User is offline

  • Cabbage
  • member icon

Reputation: 1939
  • View blog
  • Posts: 4,027
  • Joined: 11-December 07

Re: Simply Moving a Circle Across the Screen

Posted 06 July 2011 - 06:52 PM

Oh fair enough. :) Most of the time when I'm drawing on stuff, I want the whole area for my graphics, without all the overhead of non-existent borders etc...

I'll be sure to remember to call super.paint() when I do need that stuff. :-)
Was This Post Helpful? 0
  • +
  • -

#10 Astrumas  Icon User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 11
  • Joined: 05-July 11

Re: Simply Moving a Circle Across the Screen

Posted 06 July 2011 - 07:55 PM

First, thank both of you for your responses, they have been very helpful in my understanding.

View Postpbl, on 06 July 2011 - 05:59 PM, said:

In your paint() method call your father, that will take care of the background


I have tried what you said, but now the majority of the time the frame is white, and occasional quick flashes of the red oval. My only guess is that the background and oval is being written too fast. If i lower the timer occurrence it reduces this, but of course ruins the purpose of the ball moving at a decent speed.
Was This Post Helpful? 0
  • +
  • -

#11 pbl  Icon User is offline

  • There is nothing you can't do with a JTable
  • member icon

Reputation: 8324
  • View blog
  • Posts: 31,857
  • Joined: 06-March 08

Re: Simply Moving a Circle Across the Screen

Posted 06 July 2011 - 08:06 PM

Screen is refresh 60 times a second so about every 16.xxx milli second
As your drawing does almost nothing a timer of 25 milli should be more than OK

Their is no danger than 2 paint() are called at the same time and as by default JPanel use double buffering the explanation is somewhere else.

Unless your PC is really really busy doing something else no reason it wouldn't work.
You can try a timer of 50 and do
x_pos += 2;
instead of
x_pos++;
not a human eyes will see the difference

If you really want the ultimum you will have to use a Canvas I wrote 2 tutorials about it

http://www.dreaminco...way-to-repaint/
http://www.dreaminco...to-repaint-bis/
Was This Post Helpful? 1
  • +
  • -

#12 wizardcloak  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 37
  • Joined: 22-December 09

Re: Simply Moving a Circle Across the Screen

Posted 06 July 2011 - 08:18 PM

Is it possible to paint the previous position of the red circle with an identical white circle in the same position?

View PostAstrumas, on 05 July 2011 - 10:45 PM, said:

Hello, new to the community but hoping to become more involved, I am a recent college graduate looking to further expand my experience. In the future I hope to be a game developer, but for now I am experimenting and trying new things with the Java language.

My issue this evening is something that I think I understand the reason that is causing the issue, but am unable to figure out a coding solution.

The following code is supposed to draw an oval and move it across the screen. My issue is that instead of a single circle moving across the frame, I get a stream of red. My understanding is that it is redrawing the same circle on top of the previous one as it loops through, so I think I have narrowed it down to something to do with repaint()

public class Pong extends JFrame implements Runnable
{

   int x_pos = 10;
   int y_pos = 100;
   int radius = 20;

   public Pong()
   {
      //Set to exit on close
      this.addWindowListener(new WindowAdapter()
      {
         @Override
         public void windowClosing(WindowEvent we)
         {
            System.exit(0);
         }
      });
      this.setSize(300, 250);
   }
   public void start()
   {
      Thread th = new Thread(this);
      th.start();
   }
   
   public static void main(String args[])
   {
      Pong p = new Pong();
      p.setVisible(true);
      p.start();
   }

   @Override
   public void run()
   {

      //infini loop
      while (true)
      {
         x_pos++;
         repaint();

         try
         {
            //Sleep thread for 20 milliseconds
            Thread.sleep(20);
         }
         catch (InterruptedException ex)
         {
            //do nothing
         }
      }
   }

   @Override
   public void paint(Graphics g)
   {
      g.setColor(Color.RED);
      g.fillOval(x_pos - radius, y_pos - radius, 2 * radius, 2 * radius);
   }
}



Any ideas on how I could somehow "erase" the previous circle and redraw the new one to simulate a moving circle?

Was This Post Helpful? 0
  • +
  • -

#13 Astrumas  Icon User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 11
  • Joined: 05-July 11

Re: Simply Moving a Circle Across the Screen

Posted 06 July 2011 - 08:44 PM

View Postpbl, on 06 July 2011 - 08:06 PM, said:

If you really want the ultimum you will have to use a Canvas I wrote 2 tutorials about it

http://www.dreaminco...way-to-repaint/
http://www.dreaminco...to-repaint-bis/


The change you suggested still had the same result. Instead of festering over something that should be trivial, i'll take a look at your tutorials and continue my experimentation with that. Thank you again for all your help.
Was This Post Helpful? 0
  • +
  • -

#14 pbl  Icon User is offline

  • There is nothing you can't do with a JTable
  • member icon

Reputation: 8324
  • View blog
  • Posts: 31,857
  • Joined: 06-March 08

Re: Simply Moving a Circle Across the Screen

Posted 06 July 2011 - 08:50 PM

No need for that at all.
super.paint(g);
will repaint the background (erasing your previous screen.

Also when you use g.drawXXXX() you do not actually draw on the screen (if it was the case you would see all drawing appearing one after the other).

You actually draw (from scratch) on a buffered image in memory
When all drawings are DONE the whole buffered image is drawed on the screen
so erasing the previous one will do nothing, you will just draw over the background that super.paint() as just done.

Something you can try, instead of overloading paint() overload paintComponent(Graphics g) leaving the code as it is but replacing
super.paint(g);
by
super.paintComponent(g);

Happy coding
Was This Post Helpful? 1
  • +
  • -

#15 cfoley  Icon User is offline

  • Cabbage
  • member icon

Reputation: 1939
  • View blog
  • Posts: 4,027
  • Joined: 11-December 07

Re: Simply Moving a Circle Across the Screen

Posted 07 July 2011 - 03:23 AM

I get the same flickering. It's because you are painting directly on the Frame. If you follow the buffering advice in PBL's tutorials, you will get great results, but for a simple animation like this, you can get it just as good by doing the buffering yourself:


	@Override
	public void paint(Graphics g)
	{
		//super.paint(g);
		g.drawImage(getFrame(), 0, 0, null);
	}

	private Image getFrame() {
		BufferedImage img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_3BYTE_BGR);
		Graphics g = img.getGraphics();
		g.setColor(Color.white);
		g.fillRect(0, 0, getWidth(), getHeight());
		g.setColor(Color.RED);
		g.fillOval(x_pos - radius, y_pos - radius, 2 * radius, 2 * radius);
		g.dispose();
		return img;
	}


Here, I simply draw the whole frame to a BufferedImage and then dump the whole lot onto the screen at once. The original image stays unchanged until I am ready to update it, and this results in a very smooth transition.

I've removed the call to super.paint(g) because this caused a flickering on my machine. It seems that the default gray background of the component was showing momentarily. I'm not sure why Maybe the default painting forces the image to appear on the screen at the end of the process, leaving your own graphics to appear in the next monitor refresh. Seems odd to me but I know for sure I'm seeing a flickering when I leave it in!

I also used your original code to make this modification. This is one of the times that using threads with swing is absolutely fine. As soon as you start galling setters (or even getters!) you could run into trouble, but repaint() merely sets a flag that the component should redraw itself as soon as possible. In fact, you can safely do this and it will only draw itself once:

repaint();
repaint();
repaint();
repaint();
repaint();
repaint();
repaint();
repaint();


Here is the full source:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;

import javax.swing.JFrame;

public class Pong extends JFrame implements Runnable
{

	int x_pos = 10;
	int y_pos = 100;
	int radius = 20;

	public Pong()
	{
		//Set to exit on close
		this.addWindowListener(new WindowAdapter()
		{
			@Override
			public void windowClosing(WindowEvent we)
			{
				System.exit(0);
			}
		});
		this.setSize(300, 250);
	}
	public void start()
	{
		Thread th = new Thread(this);
		th.start();
	}

	public static void main(String args[])
	{
		Pong p = new Pong();
		p.setVisible(true);
		p.start();
	}

	@Override
	public void run()
	{

		//infini loop
		while (true)
		{
			x_pos++;
			repaint();

			try
			{
				//Sleep thread for 20 milliseconds
				Thread.sleep(20);
			}
			catch (InterruptedException ex)
			{
				//do nothing
			}
		}
	}

	@Override
	public void paint(Graphics g)
	{
		//super.paint(g);
		g.drawImage(getFrame(), 0, 0, null);
	}

	private Image getFrame() {
		BufferedImage img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_3BYTE_BGR);
		Graphics g = img.getGraphics();
		g.setColor(Color.white);
		g.fillRect(0, 0, getWidth(), getHeight());
		g.setColor(Color.RED);
		g.fillOval(x_pos - radius, y_pos - radius, 2 * radius, 2 * radius);
		g.dispose();
		return img;
	}
}



Was This Post Helpful? 2
  • +
  • -

  • (2 Pages)
  • +
  • 1
  • 2