There is a problem with the repaint() method in Java. I made a new thread that constantly repaints the screen. When I release the spacebar I want my player to fall smoothly by setting its position and then waiting for 50 milliseconds and looping that 20 times. Instead, it waits the whole amount of time in the loop, then repaints. I am wondering why it doesn't constantly repaint the changes in the players co-ordinates. Thank you.
(Edit) Thanks everyone for the help. This is my first time using stack overflow, and I am only 13 and still learning java, so I probably will go back to the tutorials again.
My 'a' class (main):
public class a {
public static void main(String[] args) {
JFrame frame = new JFrame("StickFigure Game");
frame.setSize(740, 580);
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
b board = new b();
frame.add(board);
frame.addKeyListener(board);
}
}
My 'b' class (JPanel/drawing):
public class b extends JPanel implements KeyListener {
c player = new c();
public class MyRunnable implements Runnable {
public void run() {
while (true)
repaint();
}
}
MyRunnable run = new MyRunnable();
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(player.getImage(), player.getX(), player.getY(), 80, 140,
null);
}
public b() {
Thread thread = new Thread(new MyRunnable());
thread.start();
}
public static void slow(int n) {
long t0, t1;
t0 = System.currentTimeMillis();
do {
t1 = System.currentTimeMillis();
} while (t1 - t0 < n);
}
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_D) {
player.setPos(player.getX() + 6, player.getY());
}
if (e.getKeyCode() == KeyEvent.VK_A) {
player.setPos(player.getX() - 6, player.getY());
}
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
player.setPos(player.getX(), player.getY() - 60);
}
}
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
for (int i = 0; i < 20; i++) {
slow(50);
player.setPos(player.getX(), player.getY() + 2);
}
}
}
public void keyTyped(KeyEvent e) {
}
}
my 'c' class (player):
public class c {
private ImageIcon i = new ImageIcon("guy.png");
private Image img = i.getImage();
private int x = 0;
private int y = 100;
public void wait(int what) {
try {
Thread.sleep(what);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public c() {
}
public Image getImage() {
return img;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void setPos(int mx, int my) {
x = mx;
y = my;
}
}
I haven't gone through all the code here but here are some pointers:
Swing has its own concurrency mechanisms which allow you to handle UI updates. You can use a Swing Timer rather than a raw Thread. Related is the use of Thread.sleep - don't do this, it only blocks the EDT and prevents UI updates.
The Swing paint chain mechanism requires you to override paintComponent rather than paint.
Always use Key Bindings rather than KeyListeners in Swing. KeyListeners require component focus to work to interact with the KeyEvents. Key Bindings do not have this limitation.
"There is a problem with the repaint() method in java." Did you consider that perhaps the problem is with your code instead? You are blocking the event thread and giving the system no time to do the intermediate repaints. In particular, this method:
public static void slow (int n){
long t0,t1;
t0=System.currentTimeMillis();
do{
t1=System.currentTimeMillis();
}
while (t1-t0<n);
}
and this loop:
for(int i = 0;i<20;i++){
slow(50);
player.setPos(player.getX(), player.getY()+2);
}
do not relinquish control to the system so that repaints can actually happen. Rewrite those using Swing timers. Look at this tutorial for an introduction on how to use these.
Also, your thread that constantly calls repaint() in a tight loop:
public void run(){
while(true) repaint();
}
is a terrible idea. You don't need to call repaint() at full CPU speed. Once every 30 milliseconds or so is fine for animation. Again, consider using Swing utilities to do this rather than writing your own looping thread.
The repaint is only a "request" to paint as soon as possible. so when you call it it causes a call to the paint method as soon as possible.
from here
So basically you just flooding the scheduled calls of paint or update with while(true) repaint();.
Oracle's stance on painting in AWT and Swing
One way you could do it, or should I say how I would do it, is to make your c class implement KeyListener, so that when a key is pressed (and only when it is pressed) you update it's location.
So move your KeyListener methods to class c, in your class b constructor you can add the call this.addKeyListener(player) or make a method void addPlayer(c player) that adds it.
Related
I'm having trouble with this code I am using to create a roulette wheel. The goal is to spin the wheel when I click the "SPIN!" button. I have done this by creating a for loop that should change the status of the wheel from true to false, which changes the orientation. This, when done fast enough, should create the illusion of movement.
THE PROBLEM I AM HAVING: is that my wheel is only repainting after the whole for loop is done, despite my placement of the repaint(). So, it only spins one tick.
Here is some sample code of my ActionListener:
public class spinListener implements ActionListener
{
RouletteWheel wheel;
int countEnd = (int)(Math.random()+25*2);
public spinListener(RouletteWheel w)
{
wheel = w;
}
public void actionPerformed(ActionEvent e)
{
for (int i = 0; i <countEnd; i++)
{
try
{
Thread.sleep(100);
if (wheel.getStatus() == true)
{
wheel.setStatus(false);
repaint();
}
if (wheel.getStatus() == false)
{
wheel.setStatus(true);
repaint();
}
}
catch (InterruptedException ex)
{
Logger.getLogger(WheelBuilder.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
UPDATE: I figured out the problem. Here are the changes I made for anyone having a similar problem.
public class spinListener implements ActionListener
{
Timer tm = new Timer(100, this);
int count = 0;
public void actionPerformed(ActionEvent e)
{
tm.start();
changeWheel();
}
public void changeWheel()
{
int countEnd = (int)(Math.random()+20*2);
if (count < countEnd)
{
wheel.setStatus(!wheel.getStatus());
repaint();
count++;
}
}
}
Swing is a single threaded environment, anything that blocks the Event Dispatching Thread, will prevent it from been able to process new events, including, paint events.
The use of Thread.sleep within the actionPerformed method is blocking the EDT, preventing it from processing new events, including paint events, until the actionPerformed method is exited.
You should use a javax.swing.Timer instead.
Take a look at Concurrency in Swing and How to Use Swing Timers for more details
I am having some difficulties using swing workers, timers, and I am actually a little confused.
As far as my understanding goes, I have to put on a timer to set-up recurring tasks that have to be called by the EDT.
I'm trying to make a program that shows graphically a sorting alghoritm (like this : https://www.youtube.com/watch?v=kPRA0W1kECg )
I just don't understand why the GUI won't refresh. I am quite sure the repaint method is being called since I put a sysout showing me the ordered values and it seems to work , but the GUI just... doesn't change.
Here's my code:
public class MainWindow {
private JFrame frame;
JPanel panel;
public final static int JFRAME_WIDTH = 800;
public final static int JFRAME_HEIGHT = 600;
public final static int NELEM = 40;
ArrayList<Double> numbers;
ArrayList<myRectangle> drawables = new ArrayList<myRectangle>();
Lock lock = new ReentrantLock();
Condition waitme = lock.newCondition();
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
MainWindow window = new MainWindow();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public MainWindow() {
initialize();
}
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, JFRAME_WIDTH + 20, JFRAME_HEIGHT + 40);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel = new myPanel();
frame.getContentPane().add(panel, BorderLayout.CENTER);
Timer timer = new Timer(500, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
lock.lock();
try{
//Updating the gui
panel.repaint();
panel.revalidate();
//Giving the OK to the sorting alghoritm to proceed.
waitme.signal();
}catch(Exception e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
});
timer.start();
SwingWorker<Integer, String> sw = new SwingWorker<Integer, String>(){
#Override
protected Integer doInBackground() throws Exception {
mapAndCreate();
bubbleSort();
return null;
}
};
sw.execute();
}
private void bubbleSort() throws InterruptedException{
for(int i=0; i < NELEM; i++){
for(int j=1; j < (NELEM-i); j++){
if(drawables.get(j-1).wid > drawables.get(j).wid){
//swap the elements!
myRectangle temp = drawables.get(j-1);
drawables.set(j-1, drawables.get(j));
drawables.set(j, temp);
lock.lock();
try{
//Wait for the GUI to update.
waitme.await();
}catch(Exception e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
}
}
}
/***
* Function that maps values from 0 to 1 into the rectangle width.
*/
private void mapAndCreate() {
double max = 0;
numbers = new ArrayList<Double>(NELEM);
//Finding maximum.
for(int i = 0; i < NELEM; i++){
Double currElem = Math.random();
if(currElem > max) max = currElem;
numbers.add(currElem);
}
//Mapping process
int offset = 0;
for(int j = 0; j < NELEM; j++){
Integer mapped = (int) (( JFRAME_WIDTH * numbers.get(j) ) / max);
myRectangle rect = new myRectangle(offset , mapped);
drawables.add(rect);
offset += JFRAME_HEIGHT / NELEM;
}
}
private class myRectangle{
int myy , wid , colorR,colorG,colorB;
public myRectangle(int y , int wid){
this.myy = y;
this.wid = wid;
Random r = new Random();
colorR = r.nextInt(255);
colorG = r.nextInt(255);
colorB = r.nextInt(255);
}
}
private class myPanel extends JPanel{
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for(myRectangle rectan : drawables){
Graphics2D graphics2D = (Graphics2D)g;
System.out.println(rectan.wid);
Rectangle2D.Double rect = new Rectangle2D.Double(0,rectan.myy,rectan.wid,JFRAME_HEIGHT / NELEM);
graphics2D.setColor(new Color(rectan.colorR,rectan.colorG,rectan.colorB));
graphics2D.fill(rect);
}
System.out.println("====================================================================================================");
}
}
}
Most OSs (or rather the UI frameworks which they use) don't support concurrent access. Simply put, you can't render two strings of text at the same time.
That's why Swing runs all rendering operations in the UI thread. Calling rendering functions (like paint()) outside of the UI thread can cause all kinds of problems. So when you do it, Swing will just remember "I should repaint" and return (instead of doing any actual work). That way, Swing protects you but most people would prefer to get an error with a useful message.
A timer always also means that there is a thread somewhere which executes when the timer runs out. This is not the UI thread of Swing. So any paing operations there must be wrapped with EventQueue.invokeLater() or similar.
Another common bug is to hog the UI thread (so no rendering happens because you do complex calculations there). That's what the SwingWorker is for. Again, in most methods of the SwingWorker, calling methods which would render something is forbidden (-> use invokeLater()).
So my guess is that the UI thread waits for the lock and the lock simply isn't unlocked early or often enough. See this demo how to do a simple animation in Swing.
public class TimerBasedAnimation extends JPanel implements ActionListener {
public void paint(Graphics g) {
// setup
// do some first-run init stuff
// calculate the next frame
// render frame
}
public void actionPerformed(ActionEvent e) {
repaint();
}
public static void main(String[] args) {
JFrame frame = new JFrame("TimerBasedAnimation");
frame.add(new TimerBasedAnimation());
...
}
}
As you can see in the code doesn't lock. Instead, you just send "render now" events from actionPerformed to Swing. Some time later, Swing will call paint(). There is no telling (and no way to make sure or force Swing) when this will happen.
So good animation code will take the current time, calculate the animation state at that time and then render it. So it doesn't blindly step through N phases in M seconds. Instead, it adjusts for every frame to create the illusion that the animation is smooth when it really isn't.
Related:
Java: Safe Animations with Swing
How to Use Swing Timers
I am trying to move a ball in applet using thread but its not moving. Can anyone help me out as m new to applet and proceeding for game development..for reference here is my code
public class ballGame extends JApplet implements Runnable
{
int x_pos=50;
int y_pos=100;
int rad=10;
Thread t;
public void start()
{
super.start();
t=new Thread("t");
t.start();
}
public void paint(Graphics g)
{
super.paint(g);
g.setColor(Color.red);
setBackground(Color.BLACK);
g.drawOval(x_pos,y_pos,2*rad,2*rad);
while(true)
{
x_pos++;
//validate();
repaint();
try
{
Thread.sleep(100);
}
catch(Exception e)
{
e.printStackTrace();
}
}//end of while
}//end of paint()
}
Swing is a single thread environment. That is, all updates and interactions are executed within a single thread. Swing is also NOT thread safe. This means that all updates to the UI MUST be executed within the context of that thread (the Event Dispatching Thread or ETD).
Any code that blocks the EDT will prevent it from (amongst other things), repainting the UI and responding to input from the user.
You're paint code will NEVER update the screen, in fact it will make your application appear to "hang", as the paint method isn't being allowed to complete and is blocking the ETD.
It is an exception that the paint method will return quickly after been called and may be called repeatedly in quick succession.
Generally speaking, a Thread is probably a little over kill, something like a javax.swing.Timer would be more then suitable under these circumstances.
public class AnimatedBoat {
public static void main(String[] args) {
new AnimatedBoat();
}
public AnimatedBoat() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new AnimationPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class AnimationPane extends JPanel {
private BufferedImage boat;
private int xPos = 0;
private int direction = 1;
public AnimationPane() {
try {
boat = ImageIO.read(new File("boat.png"));
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
xPos += direction;
if (xPos + boat.getWidth() > getWidth()) {
xPos = getWidth() - boat.getWidth();
direction *= -1;
} else if (xPos < 0) {
xPos = 0;
direction *= -1;
}
repaint();
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return boat == null ? super.getPreferredSize() : new Dimension(boat.getWidth() * 4, boat.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int y = getHeight() - boat.getHeight();
g.drawImage(boat, xPos, y, this);
}
}
}
As a side note. You should rarely need to override the paint method of a top level container like JApplet or JFrame, while there are a number of good reasons for this, the one that you're going to most interested in is the fact that they're not double buffered, meaning you are likely to see flickering as the screen is updated.
It's better to use something like JPanel and override it's paintComponent method instead.
Take a look at
Performing Custom Painting
Concurrency in Swing
Painting in AWT and Swing
For more information
nb
While I've used a JFrame for my example, it would be a simple matter to take the animation panel and put it into a JApplet, this is another reasons why you don't need/want to extend from top level containers ;)
Having an infinite loop in paint means that not a single pass of the method can complete.
Also you should never call Thread.sleep(100) in the paint method. This blocks the EDT and degrades performance.
Instead use a Swing Timer to do the update and repainting work. Also I would sub-class a JComponent and override paintComponent.
You can not invoke repaint() method inside paint(). And you can not organize infinitely loop inside paint() method - doing so, you are blocking drawing in your applet.
x_posis an int value, thus it is passed to methods by value, not by reference. That is why, when you change its value, the value inside of your circle is not updated...
You create a Thread without a run() method. This method should contain the runnable code... Furthermore, the paint() method is to paint stuff, not update stuff!
So move your while loop from the paint() method into the run() method of your thread:
t=new Thread("t") {
#Override
public void run()
{
while(true)
{
x_pos++;
//validate();
repaint();
try
{
Thread.sleep(100);
}
catch(Exception e)
{
e.printStackTrace();
}
}//end of while
}
};
Note that ballGame does not require the implement Runnable part. As the thread you created will provide it.
Have the while loop inside the run method of Runnable.
UPDATE:
Have this in the start method.
t=new Thread(this);
t.start();
What im trying to do is pretty simple, I want to show the steps of an algorithm on the screen, hence why im trying to combine repaint() with sleep(), but I am doing it wrong, Id love it if someone knows enough about it to firstly explain whats wrong with this code, and secondly, what do i do to make it work...
thanks!
in summery, what this code was meant to do is paint 10 red vertices, then balcken em one by one in intervals of 200 milliseconds.
here's the code:
public class Tester {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ShowGUIGraph();
}
});
}
private static void ShowGUIGraph() {
JFrame f = new JFrame("something");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel p=new JPanel();
p.setLayout(new BorderLayout());
p.add(BorderLayout.CENTER,new SomePanel());
f.add(p);
f.setPreferredSize(new Dimension(800,600));
f.pack();
f.setVisible(true);
}
}
public class SomePanel extends JPanel {
private static final long serialVersionUID = 1L;
LinkedList<Vertex> vertices=new LinkedList<Vertex>();
public SomePanel () {
for (int i=0;i<10;i++) {
Vertex v=new Vertex(i);
v.setLocation(20+30*i, 20+30*i);
vertices.add(v);
}
traverseVerticesRecoursive(0);
traverseVerticesNonRecoursive();
}
public void traverseVerticesRecoursive(int i) {
if (i>=vertices.size()) return;
vertices.get(i).setColor(Color.black);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
repaint();
traverseVerticesRecoursive(i+1);
}
public void traverseVerticesNonRecoursive() {
for (int i=0;i<10;i++) {
vertices.get(i).setColor(Color.red);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
repaint();
}
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (int i=0;i<vertices.size();i++) {
vertices.get(i).paintVertex(g);
}
}
}
public class Vertex {
private int x,y,tag,r=20;
private Color color=Color.red;
Vertex (int i) {
tag=i;
}
public void setLocation(int x0,int y0) {
x=x0;
y=y0;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void setColor(Color c) {
color=c;
}
public boolean colorIs(Color c) {
return (color.equals(c));
}
public void paintVertex(Graphics g) {
g.setColor(color);
g.fillOval(x,y,r,r);
g.setColor(Color.BLACK);
g.drawOval(x,y,r,r);
g.drawString(""+tag, x+r/2, y+r/2+4);
}
public int getR() {
return r;
}
}
Do not sleep in the Event Dispatch Thread; this will cause the GUI to freeze. For animation, use an EDT-friendly utility class, such as javax.swing.Timer.
Just a few ideas that might make your code cleaner:
In your SomePanel class, put the traversing code in a method out of the constructor. Constructors are intended for initializing fields.
First launch your static GUI, then spawn a worker thread to do the updates via the previous method (this would be your small "engine"). In this thread is were you can call sleep.
In your traverseVerticesRecoursive method, do only the repaint on the UI thread, and the status update on your worker thread.
Tha main modification you should do is not to block the GUI thread with sleep calls, as they have told you in the first answer.
Thread.sleep is a long running task. When you a running such a task in the EDT it blocks all repaint requests from being executed. All repaint requests which are pending and which were sent during the sleep phase are queued for future processing.
As a result when the EDT comes out of the sleep phase it coalesce all such repaint request (if coalescing is enabled which is the default property) into a single repaint which gets executed. If coalescing is not enabled then all queued request are executed serially without any time gap in between. As a result it seems that the UI did not update.
To correct the situation use a timer which triggers periodically after specific intervals of time.
Guy, you could use a new Thread differ with EDT thread to make an animation. For example,
void play() {
Thread thread = new Thread() {
#Override
public void run() {
game();
}
};
thread.start();
}
void game() {
for (; ; ) {
switch (state) {
case GameData.ANIMATING:
// call some function as repaint() to update GUI
break;
case GameData.GAME_ENDED:
return;
default:
break;
}
diffTime = System.currentTimeMillis() - beforeTime;
sleepTime = delay - diffTime;
sleepTime = (sleepTime < 0) ? 0 : sleepTime;
Thread.sleep(sleepTime);
}
}
I got a problem I couldn't get to work after about 2 Hours of trying. I want to have a loop that do 2 Methods (Draw and update) but also listen to Mouse/Keyboard events. I have a loop that Draws and Updates, but does nothing outside of the loop ( Listening to events ) I tried alot of things but nothing worked. Help Please?
I tried using the Runnable Thread, using different orders, using wait() and notify(), I've tried alot of things. But basicly I want to know how to run a loop and still check for User Input
Also when I try to quit the program clicking the red "X", it won't quit but still work
Here's the Code:
import java.applet.Applet;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class main extends Applet implements MouseListener, Runnable {
public main() {
super();
init();
}
Thread t;
Screen screen = new Screen();
String Text = "Hello";
boolean Running = true;
boolean Click = false;
int R = 0x00;
int G = 0x00;
int B = 0x00;
int xpoints[] = {25, 40, 40, 25, 25};
int ypoints[] = {40, 40, 25, 25, 25};
int npoints = 5;
public void run() {
while (Running) {
GameLoop();
}
}
public void init() {
this.addMouseListener(this);
this.setSize(400, 300); //manually set your Frame's size
t = new Thread(this);
t.start();
}
public void paint(Graphics g) {
g.setColor(new Color(R, B, G));
g.fillPolygon(xpoints, ypoints, npoints);
Running = true;
t.run();
}
public void mousePressed(MouseEvent e) { //On Mouse Click
System.exit(0);
}
public void mouseReleased(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
System.exit(0);
}
public void mouseExited(MouseEvent e) {
}
public void mouseClicked(MouseEvent e) {
}
public boolean keyDown(Event e, int key) {
return true;
}
public void GameLoop() {
if (Running) {
if (R != 0xff) {
R++;
} else {
if (G != 0xff) {
G++;
} else {
if (B != 0xff) {
B++;
} else {
System.exit(0);
}
}
}
try {
sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
paint(getGraphics());
}
}
public void sleep(int time) throws InterruptedException {
Thread.sleep(time, 0);
}
}
This tutorial should provide some insight as to how your program should be structured. And this one is helpful for the mouse listener.
Issues you should address:
1) You're doing something fishy with the paint method. Why are you calling t.run() in there? The thread t is already running and looping constantly calling the paint() method to redraw the screen. Remove this call and see what you get.
1) The destruction of your thread/applciation is poor. The first example above provides a better way for that to occur
2) You have your System.Exit(0) on mousePressed() with the comment //on mouse click but nothing in mouseClicked()... it works but its bad convention
3)Having your class named main is extremely poor convention that is both confusing and impractical. Rename your class to something like "Game" or similar.
4) Why declare Screen if you don't use it?
I see that you define a Running variable to be true upon initialization. This variable is used to determine whether or not the game should stop. I, however, don't see any place where you modify the value of this variable to false. This would explain why your game never exits.
As for the the game not working, try debugging the application in an IDE. You should then pay attention to what, if any, Exception are being thrown and the values of any variables you are questioning. Hopefully this will give you insight into the behavior of your app.
Don't forget to update us with any new info you discover so we can help you out along the way.