Okay I posted a question a while back regarding how to get a method to pause without pausing the whole program. Here's the link for context- How do I make method pause without pausing the whole program?. The answer I got was to create a new thread and pause it from there.
The new thread worked, but now I have a different problem. Since I'm running flip 5-6 times simultaneously the three setcolor methods, which all use the same Graphics, are getting mixed up. I think it's because some threads are setting the color to green, some to black and some to white. The result is that the pieces rapidly change color during their animation. How do I fix this?
public void flip(int row, int col, Graphics window)
{
Color a;
Color b = new Color(0, 100, 0);
if (pieces[row][col]==2)
a = Color.black;
else
a = Color.white;
for ( int size = 90; size>=0; size-=2)
{
try { Thread.sleep(5,5555); } catch (InterruptedException exc){}
window.setColor(b);
window.fillRect(row*100+3, col*100+3, 94, 94);
window.setColor(a);
window.fillOval(row*100 + 5, col*100+5+(90-size)/2, 90, size);
}
if (a==Color.black)
a=Color.white;
else
a=Color.black;
for ( int size = 0; size<=90; size+=2)
{
try { Thread.sleep(5,5555); } catch (InterruptedException exc){}
window.setColor(b);
window.fillRect(row*100+3, col*100+3, 94, 94);
window.setColor(a);
window.fillOval(row*100 + 5, col*100+5+(90-size)/2, 90, size);
}
}
Since you are now using Threads which all change the same value, you need to learn about synchronization.
Related
I'm currently working on a program of mine for personal development as a programmer and I have hit a miniature brick wall of frustration on this mouseclicked event I'm working on. When I thought up the procedure in my mind and on the white board I saw it utilizing a switch statement to get the job done. I have met with no success with this plan. I have since experimented with a few other control structures, but nothing seems to work the problem is controlling the control structure inside the mouseClicked event. I'll provide some relevant example code to try to communicate my objective. Note that I know the provided code is bad. I'm just trying to communicate an idea.
The goal is to be able to have the box start out green, then the user can click the box turn it red, click again it goes white, and one last time it goes back to green. For some reason this is beyond me at the moment. Any and all help will be greatly appreciated. Thanks in advance!
//This component allows the user to store information on current unit identifier. Maybe necessary to pass this in as an arguement to the PssGui since there is no accounting for callsigns.
//This component also needs to be updated with a mouse click event that can turn the color of the box to reflect the tooltip text.
unitId = new JTextField();
unitId.setEditable(false);
unitId.setBackground(Color.GREEN);
unitId.setHorizontalAlignment(SwingConstants.CENTER);
unitId.setToolTipText("<html>SHADE CELLS TO REFLECT CURRENT UNIT STATUS:" + "<br/>GREEN-MC" + "<br/>RED-NMC" + "<br/>WHITE-UNIT IN TRANSISTION</html>");
unitId.setText("A 4/5");
unitId.setBounds(0, 116, 79, 172);
getContentPane().add(unitId);
unitId.setColumns(10);
unitId.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent arg0) {
//Fill with sweet code to change the color of this box accordingly.
//use an Int and a while loop or something so that every click increments the Int
//then each int value corresponds to a color
// have a statement at the end that resets the int back to zero to keep the colors in the loop
//I.E int color = 0, mouse click happens int color = 1, now color =1 which turns the box red,
//color can never be greater than 3, when it is, we set color back to zero.
for(int i = 0; i<=3; i++){
switch (i){
case 1: unitId.setBackground(Color.GREEN);
break;
case 2: unitId.setBackground(Color.red);
break;
case 3: unitId.setBackground(Color.white);
break;
}
}//end while
//System.out.println(i);
}
});
Addendum:
I fixed my own problem. I knew it was going to be something trivial and I apologize if posting here was a waste of server resources. I was massively frustrated last night when I was trying to solve this problem. Here is the functional code. The lesson to remember here is to take a break and don't always try to force the solution.
unitId = new JTextField();
unitId.setEditable(false);
unitId.setBackground(Color.GREEN);
unitId.setHorizontalAlignment(SwingConstants.CENTER);
unitId.setToolTipText("<html>SHADE CELLS TO REFLECT CURRENT UNIT STATUS:" + "<br/>GREEN-MC" + "<br/>RED-NMC" + "<br/>WHITE-UNIT IN TRANSISTION</html>");
unitId.setText("A 4/5");
unitId.setBounds(0, 116, 79, 172);
getContentPane().add(unitId);
unitId.setColumns(10);
unitId.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent arg0) {
//Fill with sweet code to change the color of this box accordingly.
//use an Int and a while loop or something so that every click increments the Int
//then each int value corresponds to a color
// have a statement at the end that resets the int back to zero to keep the colors in the loop
//I.E int color = 0, mouse click happens int color = 1, now color =1 which turns the box red,
//color can never be greater than 3, when it is, we set color back to zero.
if(starter==0){
unitId.setBackground(Color.GREEN);
starter++;
}
else if(starter==1){
unitId.setBackground(Color.WHITE);
starter++;
}
else if(starter==2){
unitId.setBackground(Color.RED);
starter++;
}
else{
starter=0;
unitId.setBackground(Color.GREEN);
}
//System.out.println(i);
}
});
I need a certain image to be redrawn at different locations constantly as the program runs. So I set up a while loop that should move an image across the screen, but it just redraws the image on top of itself over and over again. What am I doing wrong? Is there a way to delete the old image before drawing it in a new location?
JFrame frame = buildFrame();
final BufferedImage image = ImageIO.read(new File("BeachRoad_double_size.png"));
JPanel pane = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int num = 0;
boolean fluff = true;
while (fluff == true) {
num = num + 1;
g.drawImage(image, num, 0, null);
if (num == 105) {
fluff = false;
}
}
}
};
frame.add(pane);
You can't code a loop in the paintComponent() method. The code will execute so fast that the image will only be painted in the final position, which in your case should be with an x position of 105.
Instead you need to use a Swing Timer to schedule the animation every 100 milliseconds or so. Then when the timer fires you update the x position and invoke repaint() on the panel. Read the Swing tutorial on Using Swing Timers for more information.
Putting a while loop inside a paintComponent method is not the way to do it. Instead, there should be some setup like the following:
...
final int num = 0;
final JPanel pane;
Timer timer = new Timer(10, new ActionListener() {
public void actionPerformed(ActionEvent e) {
num++;
pane.repaint();
}
});
pane = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, num, 0, null);
}
});
timer.start();
This will move the image ever 10 milliseconds, as specified in the Timer constructor.
This is a common issue people starting out in animation have, as I did. You can't 'remove an image' from the screen. However, you can repaint the entire screen, then redraw your image at a new location.
In psuedocode:
while (condition)
background(white); //or whatever color your background is
drawImage(x,y);
The code above clears the screen so it's safe for you to redraw your image. This effectively 'deletes' your image.
Edit: I didn't read your code, I just addressed your question. So other answers that fix your code are probably better than mine.
I am trying to implement a Fish Eye Image Menu in a JavaBean. As a start, I created a JLabel and put this code on the mouseEntered event. But when I run this, the output is shaky and doesn't re-size the JLabel.
This is my code.
new Thread() {
public void run() {
for (int i = 0; i < 30; i++) {
int x = imgLabel.getWidth()+1;
int y = imgLabel.getHeight()+1;
imgLabel.setSize(x , y );
// sets the icon to the label
imgLabel.setIcon(new ImageIcon(new ImageIcon(getClass().getResource("/pics/icon.png")).getImage().getScaledInstance(x , y, Image.SCALE_DEFAULT)));
repaint();
try {
Thread.sleep(10);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
}.start();
If I comment that line where I set the image to the JLabel, the label gets re-sized perfectly.
Where has this gone wrong?
The problem is that getScaledInstance() together with resource loading is slow. Do these calculations once and cache them (in an array of 30 items). Not every time in the for loop.
Another thing: make sure you use a Swingworker for your animation, that helps in the timing and avoids setting icons outside the Event Dispatch Thread (EDT).
So I'm writing a program that plays Reversi/Othello against a player. I wrote a method to make a short animation of the pieces flipping-
public void flip(int row, int col, Graphics window)
{
Color a;
if (pieces[row][col]==1)
a = Color.black;
else
a = Color.white;
for ( int size = 90; size>0; size-=2)
{
try { Thread.sleep(11,1111); } catch (InterruptedException exc){}
window.setColor(new Color( 0, 100, 0 ));
window.fillRect(row*100+3, col*100+3, 94, 94);
window.setColor(a);
window.fillOval(row*100 + 5, col*100+5+(90-size)/2, 90, size);
}
if (a==Color.black)
a=Color.white;
else
a=Color.black;
for ( int size = 0; size<90; size+=2)
{
try { Thread.sleep(11,1111); } catch (InterruptedException exc){}
window.setColor(new Color( 0, 100, 0 ));
window.fillRect(row*100+3, col*100+3, 94, 94);
window.setColor(a);
window.fillOval(row*100 + 5, col*100+5+(90-size)/2, 90, size);
}
}
It works well and looks great, but the problem is that since thread.sleep pauses the entire program, it can only flip one piece at a time. Is there something I can do to pause just that method without interrupting the rest of the program?
Thanks everyone. The new thread worked, but now I have a different problem. The three setcolor methods in the flip method are getting mixed up. I think it's because some threads are setting the color to green, some to black and some to white. How do I fix this?
You should make run this method inside a dedicated thread created from your main program.
You should run flip in separate Thread in that case. The simplest example:
Thread t = new Thread(new Runnable() {
public void run() {
flip();
}
});
t.start();
What you are targetting is asynchronous programming: schedule a javax.swing.Timer that, each time it fires, does one animation step. This would be the idiomatic way for a GUI program; the approach with a separate thread that sleeps in a loop and uses invokeLater in each step will also work, but is less elegant because it uses more system resources (another thread that mostly just sleeps).
You need to create a separate thread that will handle the animation. That thread can call Thread#sleep, since Thread#sleep does not pause "the entire program", but only the current thread. At each step in the animation, it should change some state indicating the current stage of the piece flip animation and then request a repaint.
Iv'e looked all over for answers to this book. And I know anyone else who has tried to read this book feels the same way. It's called "Programming Video Games for The Evil Genius" Is there anyone who has read this book? I'm on project 10:Radical Racing-The Cars. Everything compiles correctly but for some reason my cars are not showing up in the correct spot on the JFrame. They should be showing up under the two white lines. I'm positive the code is exactly the same as in the book, but the book is wrong. I have already tried changing the HEIGHT part of the point of origin but no matter what I do it does not budge. I can't attach an image because I don't have a rep of at least 10 .This is the code that the deals with the placement of the cars.
public class TheCars extends JFrame
{
final int WIDTH = 900; int HEIGHT = 650;
double p1Speed = .5, p2Speed = .5;
Rectangle p1 = new Rectangle(WIDTH/9,HEIGHT/2, WIDTH/30,WIDTH/30);
Rectangle p2 = new Rectangle(((WIDTH/9)+((int)((WIDTH/9)*1.5)/2)),(HEIGHT/2)+
(HEIGHT/10),WIDTH/30,WIDTH/30);
//the constructor
public TheCars()
{
//the following code creates the JFrame
super("Radical Racing");
setSize(WIDTH,HEIGHT);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
//start the inner class (which works on it's own because it is a thread)
Move1 m1 = new Move1();
Move2 m2 = new Move2();
m1.start();
m2.start();
}
//this will draw the cars and the racetrack
public void paint(Graphics g)
{
super.paint(g);
//set the color to blue for p1
g.setColor(Color.BLUE);
//now draw the actual player
g.fill3DRect(p1.x,p1.width,p1.width,p1.height,true);
//set the color to red for p2
g.setColor(Color.red);
//now draw the actual player
g.fill3DRect(p2.x,p2.width,p2.width,p2.height,true);
}
private class Move1 extends Thread
{
public void run()
//This should all be in an infinite loop so that the process repeats.
{
while(true)
{
//now put in the try block. This will let
//the program exit if there is an error
try
{
//first refresh the screen
repaint();
//increase speed a bit
if(p1Speed<=5)
p1Speed+=.2;
p1.y-=p1Speed;
//this delays the refresh rate
Thread.sleep(75);
}
catch(Exception e)
{
//if there is an exception (an error), exit the loop
break;
}
}
}
}
private class Move2 extends Thread
{
public void run()
{
//this should all be in an infinite loop so the process repeats
while(true)
{
//now put the code in a "try" block.
//this will let the program exit if there is an error
try
{
//first refresh the screen
repaint();
//increase the speed a bit
if(p2Speed<=5)
p2Speed+=.2;
p2.y-=p2Speed;
//this delays the refresh rate
Thread.sleep(75);
}
catch(Exception e)
{
//if there is an exception (an error), exitthe loop
break;
}
}
}
}
public static void main(String[]args)
{
new TheCars();
}
}
Assuming you're painting those Rectangle objects directly onto the screen, we have to assume that the expressions "HEIGHT/2" and "HEIGHT/2 + HEIGHT/10" are coming out equal, and nonzero but small. That would be the case if HEIGHT is an int, and the value is more than 2 and less than 10. Presumably the value would need to be a couple hundred, at least, for those boxes to show up in the middle of the screen. Check the value of HEIGHT (just using a System.out.println() would be fine) and make sure it's the actual height of the window.
EDIT
Now that we see the rest of the code: the second argument to each call to fill3DRect() is wrong. It should be the y member of the Rectangle objects, which is a large, varying number, but you're passing the width member, which is a small fixed number. Change both the calls to look like this and you'll be back on track:
g.fill3DRect(p1.x, p1.y, p1.width, p1.height, true);