I want to move a image across the screen by 16 to the right when the arrow key is pressed. I want to move it with a speed(1px/10ms) until reaches the point. The image is created inside a class that is child of JPanel.
I wrote the next code but the image changes the position instatly instead making a movement:
public class Test extends JFrame implements KeyListener {
private int x=0;
private int y=0;
BufferedImage img;
...
...
public void paint(Graphics g){
g.drawImage(img,x,y,null);
}
// Move to a point 16 pixels to right
public void moveRight(){
for(int i=0;i<16;i++){
x++;
repaint();
try {
Thread.sleep(10); // Sleep 10 milliseconds until next position change
}catch (InterruptedException e) {}
}
}
public void keyPressed(KeyEvent e) {
if(e.getKeyCode()==KeyEvent.VK_RIGHT){
moveRight();
}
}
}
The problem is your sleep inside the EDT (Event-Dispatching-Thread). repaint() triggers an event that will be dispatched by the EDT and will in turn perform the actual repainting of your component. Since you are blocking the EDT, the repaint does not perform directly (but after the end of all your code then a single repaint event occurs (because repaint events are grouped whenever possible). You will probably need to use SwingWorker to fix this issue.
What if you call moveRight() in another thread?
try this:
public void keyPressed(KeyEvent e) {
if(e.getKeyCode()==KeyEvent.VK_RIGHT){
new Thread(new Runnable(){
public void run(){
moveRight();
}
}).start();
}
}
I've not tested and I even don't know if this is a good aproach
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
The JFrame will not shut down when the default "X" button is clicked. I think this problem has something to do with the main thread not being read, but I don't understand the intricacies of swing or honestly, threads in general. "Window" is an extension of JFrame, "Boxy" drives the program. Program is only in initial stages. Also, I'd like to know how to get the main thread run on every loop-over. Couldn't find anything about this in other questions.
public class Window extends JFrame implements KeyListener{
private static final long serialVersionUID = 1L;
JPanel panel;
public Window(){
super("FileTyper");
super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
super.setSize(200,100);
super.setResizable(false);
panel = new JPanel();
super.getContentPane().add(panel);
super.setFocusable(true);
addKeyListener(this);
super.setVisible(true);
}
public void update(){
}
public void render(Graphics2D g){
}
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
switch(e.getKeyCode()) {
case KeyEvent.VK_F9:
break;
case KeyEvent.VK_F10:
break;
}
}
#Override
public void keyTyped(KeyEvent arg0) {
}
}
public class Boxy {
public Window window;
public static void main (String args[]){
start();
}
public Boxy(){
init();
boolean forever = true;
while(forever){
update();
render();
delay();
}
}
private void init(){
window = new Window();
}
private void update(){
window.update();
}
private void render(){
Graphics2D g2 = (Graphics2D) window.getContentPane().getGraphics();
window.render(g2);
g2.fillRect(0, 0, 100, 100);
}
private void delay(){
try {Thread.sleep(20);} catch (InterruptedException ex) {System.out.println("ERROR: Delay compromised");}
}
public static void start(){
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Boxy box = new Boxy();
}
});
}
}
I would suggest that you are blocking the Event Dispatching Thread with
while(forever){
update();
render();
delay();
}
This is preventing the Event Queue from processing the event that would close the window.
Start by taking a look at Concurrency in Swing. I would suggest you might like to take a look at something like javax.swing.Timer to start with, but if you want more control of the frame rate, you're going to need to use some kind of Thread. Remember though, Swing expects all updates to be executed from within the context of the Event Dispatching Thread.
Custom painting in Swing is not done by using something like...
Graphics2D g2 = (Graphics2D) window.getContentPane().getGraphics();
The Graphics context is short lived, anything your paint to it (using this method) will be destroyed on the next paint cycle.
Instead, you should use something like a JPanel as the bases for your painting and override it's paintComponent method and render the state from within it, when ever it is called.
You would then simply need to call repaint when you want to update the component.
Take a look at Performing Custom Painting for more details.
I would also recommend that you take a look at How to use Key Bindings as an aletrnative to KeyListener
Your program's "game" loop is incorrect:
while(forever){
update();
render();
delay();
}
Rather than looping the program, it freezes it by tying up the Swing event thread or EDT (for Event Dispatch Thread). You should use a Swing Timer instead for this functionality.
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.
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();
This is simple version of my problem.
I have 3 classes:
public class TopographyFrame extends JFrame - simple JFrame with JPAnel and button
public class TopograpyPanel extends JPanel - JPanel to fill Rectangles
public class Siec - class to perform calculations and call repaint on JPAnale
in JPanel i overided paintComponent() method
public void paintComponent (Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
System.out.println(rectangles.length);
for(int i = 0 ; i < rectangles.length ; i++){
g2.setPaint(neurony[i].winner);
g2.fillRect((int)rectangles[i].x,(int)rectangles[i].y,(int)rectangles[i].width, (int)rectangles[i].height);
}
}
neurony - array of objects with field public Color winner
in class Siec i have reference to JPanel to repaint it
in class JFrame i have a button with private action listener:
class MyListener implements ActionListener{
Siec s;
public MyListener(Siec s){
this.s = s;
}
public void actionPerformed(ActionEvent arg0) {
try {
s.forPaint();
} catch (Exception e) {
e.printStackTrace();
}
}
method forPaint() in Siec looks like:
public void forPaint(){
setTopography();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
setTopography();
}
public void setTopography() {
for (int i = 0; i < vector.colors.length; i++) {
neurony[i].winner = vector.colors[(int)(random() * 900 % vector.colors.length)];
}
panel.repaint();
}
vector.color is array of Colors
So my problem is: when i click a button i would like to JPanel repaint immediately and then after 3 second repaint one more time. Insted JPanel repaints only one time after 3s delay.
}
You can't sleep, wait, or otherwise pause on the event handling thread, ever. Doing so blocks all events from being processed, including painting events. Your first painting can't occur because you're sleeping on the event thread, preventing it from happening.
The right way to do any kind of animation -- even simple stuff like this -- is to create your own thread. That second thread can call repaint(), sleep for 3 seconds, then call repaint() again. The SwingWorker class is nominally a simpler way to do this, but in all honesty, beginners always find creating their own thread to be easier.
You are scheduling a repaint on the UI thread and then sleeping (blocking) the UI thread for 3seconds and then requesting another repaint again. Those two will either happen really close to each other after this method has finished (after 3 seconds) or be merged into one update (afterwards as well).
Instead of sleep(3000) and then calling your setTopography again you could schedule a setTopography call on the UI thread to happen after 3 seconds.
Have a look at the Swing Timer for example:
http://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html
So something along the lines of:
javax.swing.Timer timer = new javax.swing.Timer(3000, new ActionListener() {
public void actionPerformed(ActionEvent e) {
setTopography();
}
});
timer.setRepeats(false);
timer.start();
Since your sleep is being performed on the Event Dispatch Thread, the repaint() event cannot be performed until the end of the wait. Do this instead:
private Timer timer = new Timer(); // use java.util.Timer
public void forPaint() {
setTopography();
timer.schedule(new TimerTask() {
#Override
public void run() {
setTopography();
}
}, 3000);
}
public void setTopography() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
for (int i = 0; i < vector.colors.length; i++) {
neurony[i].winner = vector.colors[(int)(random() * 900 % vector.colors.length)];
}
panel.repaint();
}
});
}
Keep in mind that all modifications to a Swing component (e.g. your JPanel) must happen on the EDT.