JLabel's Set Location inside for not moving smoothly - java

i'm using a label for a little sprite for some testing that i'm doing, but i want to move the sprite 10 pixels per keypress. Now, i can do that, but now i'm trying to make it move the 10 pixels smoothly, so i tried the next code:
for(int i = 0; i < 100; i++){
x++;
container.setLocation(x, y);
System.out.println(x);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Now the problem is that, the sprite only moves when the for cycle ends, but the console shows the X value changing for each iteration. Any thoughts/help?
Thanks!

I suggest you to take a look at how to animate a JComponent using Swing Timer class, instead of for loop. You can find various tutorials about how to use Swing Timer. Here, to briefly explain, you are blocking EDT(Event Dispatch Thread) which operates the graphical side of the Java. Whenever you want to make a constant and smooth flow in your animations, make sure that you never block the EDT.
EDIT: Here is the demonstration of the usage of Swing Timer Class:
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.Timer;
public class AnimationTrial extends JFrame {
private final int DELAY = 10;
private Timer timer;
private int x, y;
private JLabel label;
public static void main(String[] args) {
EventQueue.invokeLater( new Runnable () {
#Override
public void run() {
new AnimationTrial();
}
});
}
public AnimationTrial()
{
setSize(500, 500);
x = 50;
y = 50;
label = new JLabel("They see me movin' they hatin'!");
timer = new Timer( DELAY, new ActionListener()
{
#Override
public void actionPerformed(ActionEvent arg0) {
x++;
label.setLocation(x, y);
}
});
timer.start();
getContentPane().add(label);
pack();
setVisible (true);
}
}

If you dont create new Thread, the user interface runs on the same thread as its method.
Therefore your for-cycle is fired after some action and thread cant do anything else until it ends.
Solution : Create your own class, pass the JLabel or the whole form as parameter in constructor, implement threading and run it as new thread.

I'd suggest you give a look to the Timing Framework, if you want to do something close to an animation in Swing. It could help you, depending on your general need.

If you want other sprites to move in sync with your sprite you can create a TimerTask and use scheduleAtFixedRate(). Your TimerTask would then be responsible for moving all sprites and redrawing everything that was part of the moving like the JPanel in the background and the sprites.
To make your code snippet work you would have to add redrawing of the Background and the sprite after setting the location but I would advise against that approach as it can easily lead to badly designed code where you create one God Class that does everything.
The TimerTask approach should also be more precise if the calculations need a bit time as it tries to have the same time between 2 calls where the approach with the sleeping thread can easily lead to different delays if the calculations are finished earlier or later.

Related

How can I pause/continue scheduled task?

I'm new to JAVA and trying to learn some concurrency concepts.
I have a simple GUI class that pops-up a window with 1 button which I want to use for pause/continue.
Also, I have a class that extends TimerTask, it looks like below and start with the GUI:
public class process extends TimerTask {
public void run() {
while(true) { /*some repetitive macro commands.. */ }
}
}
Real question is, how can I pause the task onClick of the button and also continue onClick of the button if already paused?
I have taken a step to use a boolean to flag the button as it changes from pause to continue on each click.
But then I had to type a lot of while(button); for busy waiting inside the while()...
Do you think I can make like Thread.sleep() or something but from outside the task thread?
OLD ANSWER
Basically, there is no support for pause and resume on TimerTask, you can only cancel, check here
perhaps you might want to read about threading as that's the alternative I know of that has an interrupt and start features and then you can keep track of the progress of what you're doing to resume where it stopped.
So, I will suggest you go through this link, because you need to understand threading not just copy a code to use, there is a sample code there that will definitely solve your problem also.
Note that running an endless while loop will basically cause your program not to respond, unless the system crashes. At a certain point, the data becomes an overload and the program will overflow. This means it will fail.
.
NEW ANSWER
So, response to the new question, I was able to run a tiny little program to demonstrate how you can achieve something that looks like multithreading when working with SWING.
To rephrase your question: You want to run an indefinite task like let say we're playing a song, and then onclick of a button to pause the song, on click again should continue the song?, if so, I think below tiny program might work for you.
public class Test{
static JLabel label;
static int i = 0;
static JButton action;
static boolean x = false; //setting our x to false initialy
public static void main(String[] args) {
JFrame f=new JFrame();//creating instance of JFrame
label = new JLabel("0 Sec"); //initialized with a text
label.setBounds(130,200,100, 40);//x axis, y axis, width, height
action=new JButton("Play");//initialized with a text
action.setBounds(130,100,100, 40);//x axis, y axis, width, height
f.add(action);//adding button in JFrame
f.add(label);
action.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e){
if(x){
x = false; //Update x here
action.setText("Play");
action.revalidate();
}else{
x = true; //Update x here also
action.setText("Pause");
action.revalidate();
if(x){ //Using x here to determind whether we should start our child thread or not.
(new Thread(new Child())).start();
}
}
}
});
f.setSize(500, 700);//500 width and 700 height
f.setLayout(null);//using no layout managers
f.setVisible(true);//making the frame visible
}
}
class Child implements Runnable{
#Override
public void run() {
while (x) {
//You can put your long task here.
i++;
label.setText(i+" Secs");
label.revalidate();
try {
sleep(1000); //Sleeping time for our baby thread ..lol
} catch (InterruptedException ex) {
Logger.getLogger("No Foo");
}
}
}
}

How to deal with SwingWorker thread that goes slow?

Hardly, do I understand multithreading stuff. And beginning look into it , I faced one issue, that came in my mind. I have recently wrote one simple application, and as soon as I get some new knowledge about Java I wish to improve my app with that I’ve learned.
It looks like simple swing GUI that updates images every period of time. I implemented ActionListener and overrode actionPerformed method. Timer with 15ms delay, repainted JPanelclass and everything worked fine. But I thought that updating my GUI using timer directly in actionPerformed (I presume that it’s another thread, but I’m barely sure) is bad idea. So I decided to change code and use SwingWorker. I called all my methods for my animation inside process() .. and again app working fine but it became extremely slow.
Now I’m thinking what’s wrong? Why it acts slower then before ? My timer delay is actually not waiting 15ms , it’s much slower even though delay the same. Am I made mistake with multithreading ?
Help me understand this stuff. Thanks in advance
public class GameEngine() extends SwingWorker<Void, Drawable>
GamePanel gp; // ref to JPanel class
{
public GameEngine(GamePanel gp)
{
this.gp = gp;
}
}
protected void doInBackground()
{
publish();
}
protected void process(List<Drawable> chunks)
{
Timer timer = new Timer(15, e ->
{
//methods for animation
fall();
animate();
checkTouch();
});
}
Some code I left beyond. If you need it I can write...
EDITION
Just for clarity of my issue I provide some more examples and addition explanation.
**Used to be: **
public class GamePanel extends JPanel
{
public void GamePanel()
{
GameEngine engine = new GameEngine(this);
}
//some images , variables etc...
protected void paintComponent(Graphics g)
super.paintComponent(g)
g.drawImage(image1, x, y, null);
g.drawImage(image2, w, z,null);
...
}
public class GameEngine () implements ActionListener
{
GamePanel gp;
Timer timer;
public void GameEngine(GamePanel gp)
{
this.gp = gp;
timer = new Timer( 15 , this );
}
public void actionPerformed()
{
//these methods repaint my GamePanel every 15ms.
fall(); // make object (image) increment on Y Axis
animate(); // make another object (image) decrement on X Axis
checkTouch(); // check if objects collided
}
}
**Became: **
public class GamePanel extends JPanel
{
public void GamePanel()
{
GameEngine engine = new GameEngine(this);
}
//some images , variables etc...
protected void paintComponent(Graphics g)
super.paintComponent(g)
g.drawImage(image1, x, y, null);
g.drawImage(image2, w, z,null);
...
}
public class GameEngine () extends SwingWorker<Void, Drawable>
{
GamePanel gp;
Timer timer;
public void GameEngine(GamePanel gp)
{
this.gp = gp;
}
protected void doInBackground()
{
process();
}
protected void progress()
{
timer = new Timer (15, e->
{
new ActionListener(new actionPerformed)
{
//these methods repaint my GamePanel every 15ms.
fall(); // make object (image) increment on Y Axis
animate(); // make another object (image) decrement on X Axis
checkTouch(); // check if objects collided
}
});
}
protected void done()
{
};
}
When I created it first I implemented ActionListener and updated my panel through timer declared in constructor.. I presumed that it’s thread-unsafe.
That’s why I transfer everything in progress method where I declared timer which ActionListener as lambda argument.
In other words I call all methods for animation in another thread.
Finally it became slower, comparing with first example..
I don’t understand
Is Timer from first example EDT or it’s another thread ?
My first example is thread safe ?
Why my second example goes much slower then first one ?
I heard about NOT update your GUI outside EDT, is it that case?
There is not enough information in your question to answer it, but I will take your queries about multi-threading and the implied question about Swing and rendering and see if I can help you out.
I think the most likely reason for your slowdown is unnecessary screen updating. Once a pixel has been drawn onto the canvas or whatever in your application, usually your application does not need to redraw it unless it is supposed to change; either a sprite moves or some other image in your app obscures a part of the drawing temporarily and then needs to be restored.
It is common for a novice repainting to ignore this, and just repaintng the entire painted surface. Although this will work, it is slow; if you're doing it a number of times in a loop, then the loop will seem slow.
The better way to do this is to use the rectangle passed into the redrawing routine and only repaint its intersection with the entire surface redrawn by your routine -- this cuts way down on the part that needs to be redrawn, and therefore on the time it takes to redraw it.
As for multithreading, I think it's helpful to think of it the way we used to think of things in a single-processor world -- the computer does something for a while, then stops in a place you cannot predict and does something in the other thread for a while, etc. You cannot assume the order in which things are going to get done, or how long it will spend on each thing, etc.
With modern multi-core computers, it is possible that these things in fact get done at the same time, but I don't know that trying to envision that helps you any.

keyListener with Swing and game level system with Swing/keyListener and Thread

I am a novice coder, have a tiny bit of experience with C++ about 10 years ago and now learning java (it's been about 4-5 months). I have a little collaborative project going, and I've got some things to figure out.
Here's the code:
import java.awt.event.*;
import java.awt.*;
import javax.swing.JFrame;
import java.util.Scanner;
import java.util.Random;
public class Game extends JFrame {
GamePanel panel;
int[][] grid;
int size;
//...and some other variables
public Game(String title) {
super(title);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel = new GamePanel(grid,size);
this.add(panel);
Button button = new Button("WHAT");
button.setBounds(-100, -100, 70, 70);
this.add(button);
button.addKeyListener(new KeyHandler());
}
class KeyHandler extends KeyAdapter {
public void keyPressed(KeyEvent e) {
int keycode = e.getKeyCode();
switch(keycode) {
//arrow keys input and stuff
}
if(checkForWin()) //checkForWin() returns a boolean value for win/loss
//won the game, thus end the thread here
if(checkForLoss()) // similar stuff as checkForLoss()
//lost the game, thus end the thread here
//...update the panel
panel.repaint();
}
}
public static void main(String [] args) {
Game me = new Game("GAME");
me.setVisible(true);
}
}
So that's pretty much how the whole game thing looks like.
And I have questions:
I am using a button and put it at a negative position to make it invisible and using it as a mean of KeyListener. Is there any other way to add a key listener? ex) to the panel or something?
I want to change it so that each "level" is a thread and make it like
public static void main(String [] args) {
int level = 1;
do {
GameThread gt = new GameThread("Game");
// run() will have the game constructor inside
gt.setLevel(level);
gt.start();
gt.join(); //wait until the game "level" is finished
if(won)
level++;
if(lost)
level = 1;
} while(!checkIfDonePlaying())
}
Somewhat like this. I'm having trouble making the thread continue to run until the game level is actually finished. How do I do that?
I want to add a JLabel to show the score on the frame. But when I do that, the score doesn't update when I repaint() it. How do I do that?
Thanks in advance!
A few things:
Yes, there is a way to add a KeyListener to the panel, and that's by using key bindings. For example:
javax.swing.InputMap im = panel.getInputMap(panel.WHEN_IN_FOCUSED_WINDOW);
javax.swing.ActionMap am = panel.getActionMap();
im.put(javax.swing.KeyStroke.getKeyStroke("pressed UP"), "up");
am.put("up", new javax.swing.AbstractAction() {
#Override
public void actionPerformed(ActionEvent ev) {
// handle up arrow key action here
}
});
Swing is an event-driven environment, so do-while loops should not be used. Instead, use a Swing timer that periodically checks if your level has been completed.
Since you're not doing any custom painting on your JLabel, you shouldn't be using repaint. Instead, use its setText method.
Here's a solution for your first issue:
panel.setFocusable(true);
panel.addKeyListener(this);
Then you should just be able to do the usual key listener methods. Be sure to implement KeyListener!
Solutions for your other issues on their way. (Actually take #TNT 's suggestion and use keybindings, I'm more comfortable with listeners but for games I usually use lwjgl or slick2d)
2.
I feel that running your programs levels may be a bit inefficient (that may just be me) I would suggest having one thread in a run method and have the following:
Public void reset(int level)
{
//reset all variables based on local variable level...
}
Call it like this:
Reset(++currentLevel)); //use ++ before currentLevel so it adds to it and then resets
And you could easily use switch cases to do some special stuff if you want
Switch(level){
case 1: //...
}
And so forth. (I'm typing on mobile sorry for weird caps)

MultiThreading Swing Event Dispatcher Thread

I already have a post related to the multithreading issue but I have some new questions+code. I have a multiball game project and it requires me to parse an XML file to obtain information about the ball(like size, speed, initial position etc). Now, I wanted to create a different thread to parse the XML file, but I cannot figure out a way to do it. Here is my code:
main() starts here:
public class BounceBallApp extends JFrame{
public BounceBallApp()
{
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle("BounceBallApp");
setSize(300,300);
setVisible(true);
add(new BallWorld());
validate();
}
public static void main(String[] args)
{
/*Create main GUI in the Event Dispatch Thread*/
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
new BounceBallApp(); //main frame
}
});
}
}
Within the constructor for BallWorld(), I have an inner class BallContainer(), which contains a Start button:
jbtStart.addActionListener(new ActionListener()
{public void actionPerformed(ActionEvent e)
{
//Populate the ballList arraylist
if(filePathField.getText().equals(" "))
{
JOptionPane.showMessageDialog(null, "Please input the XML file","Information", JOptionPane.INFORMATION_MESSAGE);
}
else
{
XMLFilePath = filePathField.getText();
ballList = new BallList(XMLFilePath);//I want to put this in a thread
JOptionPane.showMessageDialog(null,"Game started!","Bouncing Balls",JOptionPane.INFORMATION_MESSAGE);
for(Ball ball:ballList.ballsArrayList)
{
timer.setDelay(1000/ball.getSpeed()); //change the delay of the timer to the ball's speed
timer.start(); //start the timer
bTimer = true; //timer is now on
}
}
}
});
}
Now the problem is that if I put the parsing process in another thread, then I have to wait for the ballsArrayList to fill before I can continue with the application. I was thinking of using invokeAndWait() but I read that that method cannot be called from the Event Dispatch Thread. So, How can I achieve this? Or is it even worthwhile?
Also, I wanted to move the calculation for moving the ball (calculating the x,y coords) to a thread, but again, I don't know how to implement it.
for(Ball ball:ballList.ballsArrayList)
{
ball.draw(g);
ball.move(ballContainerWidth,ballContainerHeight,buttonPanel.getHeight());
}
public void move(int ballContainerWidth,int ballContainerHeight,int buttonPanelHeight)
{
if((this.getX()+this.getsize()+this.getDx()) > ballContainerWidth)
{
this.setDx(-Math.abs(this.getDx()));
}
//the height/depth to which the balls can bounce is the (main ball container height) minus (button panel height)
if((this.getY()+this.getsize()+this.getDy()) > ballContainerHeight-buttonPanelHeight)
{
this.setDy(-Math.abs(this.getDy()));
}
if((this.getX()-this.getsize()) < 0 )
{
this.setDx(Math.abs(this.getDx()));
}
if((this.getY()-this.getsize()) < 0 )
{
this.setDy(Math.abs(this.getDy()));
}
int newX = (int)Math.round((this.getX()+this.getDx()));
int newY = (int)Math.round((this.getY()+this.getDy()));
this.setX(newX);
this.setY(newY);
}
Sorry for the long post, but multithreading is all new to me. I am a bit confused about it.
Initial loading of the files
I personally would opt for one of the following approaches
Increase the start-up time of your program by parsing all the files during start-up. For a few XML files this overhead might be very small. If it takes too long, you can consider showing a splash screen
Load the XML files when the start button is pressed, but show a progress bar until the loading is done. Start the game afterwards. A SwingWorker can help you with this. Examples can be found in the Swing documentation or here on SO.
Updating of the ball position
If the calculation is as easy as what is shown here, I would simply use a javax.swing.Timer to update the position on regular time intervals, and do the calculation on the Event Dispatch Thread.
If you want to do the calculation on a background thread just for the exercise, I would still opt for a calculation of the position on a background thread. The calculation should be using local variables which are only know to that background thread. Once the new position is calculated, update the position of the ball on the Event Dispatch Thread using SwingUtilities#invokeLater. This allows you to access the position during the paint operation without having to worry about threading issues. Probably easier then messing around with locks.

Java Swing game has poor performance when many sprites are on screen

I'm making a simple tower defense game in Swing and I've run into a performance problem when I try to put many sprites (more than 20) on screen.
The whole game takes place on a JPanel which has setIgnoreRepaint(true).
Here is the paintComponent method (con is the Controller):
public void paintComponent(Graphics g){
super.paintComponent(g);
//Draw grid
g.drawImage(background, 0, 0, null);
if (con != null){
//Draw towers
for (Tower t : con.getTowerList()){
t.paintTower(g);
}
//Draw targets
if (con.getTargets().size() != 0){
for (Target t : con.getTargets()){
t.paintTarget(g);
}
//Draw shots
for (Shot s : con.getShots()){
s.paintShot(g);
}
}
}
}
The Target class simple paints a BufferedImage at its current location. The getImage method doesn't create a new BufferedImage, it simply returns the Controller class's instance of it:
public void paintTarget(Graphics g){
g.drawImage(con.getImage("target"), getPosition().x - 20, getPosition().y - 20, null);
}
Each target runs a Swing Timer to calculate its position. This is the ActionListener it calls:
public void actionPerformed(ActionEvent e) {
if (!waypointReached()){
x += dx;
y += dy;
con.repaintArea((int)x - 25, (int)y - 25, 50, 50);
}
else{
moving = false;
mover.stop();
}
}
private boolean waypointReached(){
return Math.abs(x - currentWaypoint.x) <= speed && Math.abs(y - currentWaypoint.y) <= speed;
}
Other than that, repaint() is only called when placing a new tower.
How can I improve the performance?
Each target runs a Swing Timer to calculate its position. This is the ActionListener it calls:
This may be your problem - having each target/bullet (I assume?) responsible for keeping track of when to update itself and draw itself sounds like quite a bit of work. The more common approach is to have a loop along the lines of
while (gameIsRunning) {
int timeElapsed = timeSinceLastUpdate();
for (GameEntity e : entities) {
e.update(timeElapsed);
}
render(); // or simply repaint in your case, I guess
Thread.sleep(???); // You don't want to do this on the main Swing (EDT) thread though
}
Essentially, an object further up the chain has the responsibility to keep track of all entities in your game, tell them to update themselves, and render them.
I think what might be at fault here is your whole logic of the games setup (no offense intended), As stated in another answer you have different timers taking care of each entities movement, this is not good. I'd suggest taking a look at some gaming loop examples, and adjusting yours to this, you'll notice a great readability and performance improvement a few nice links:
http://www.java-gaming.org/index.php/topic,24220.0
http://www.cokeandcode.com/info/tut2d.html
http://entropyinteractive.com/2011/02/game-engine-design-the-game-loop/
I was initially wary of the too-many-timer theory. Instances of javax.swing.Timer use "a single, shared thread (created by the first Timer object that executes)." Dozens or even scores are perfectly fine, but hundreds typically start to become sluggish. Depending on period and duty cycle, the EventQueue eventually saturates. I agree with the others that you need to critically examine your design, but you may want to experiment with setCoalesce(). For reference, here's an sscce that you may like to profile.
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.Timer;
/**
* #see http://stackoverflow.com/a/11436660/230513
*/
public class TimerTest extends JPanel {
private static final int N = 25;
public TimerTest() {
super(new GridLayout(N, N));
for (int i = 0; i < N * N; i++) {
this.add(new TimedLabel());
}
}
private static class TimedLabel extends JLabel {
private static final Random r = new Random();
public TimedLabel() {
super("000", JLabel.CENTER);
// period 100 to 1000 ms; frequency 1 to 10 Hz.
Timer timer = new Timer(r.nextInt(900) + 100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
TimedLabel.this.setText(next());
}
});
timer.setCoalesce(true);
timer.start();
}
private String next() {
return String.valueOf(r.nextInt(900) + 100);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(640, 480);
}
private void display() {
JFrame f = new JFrame("TimerTet");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new JScrollPane(this));
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new TimerTest().display();
}
});
}
}
for painting in the Swing is better (in all cases >= Java5) use Swing Timer exclusivelly
this painting proccess required only one Swing Timer
example about bunch of Stars and one Swing Timer
Try to use one timer for all the targets.
If you have 20 targets then you will also have 20 timers running simultaneously (think about 1000 targets?). There is some expense and the most important thing is each of them is doing the similar job -- to calculate the position -- You don't need to split them. I guess it is a simple task, which will not take you a blink, even running 20 times.
If I got the point, What you want to do is trying to change the positions of all the targets at the same time. You can achieve this by changing all of them in one single method running in one thread.

Categories

Resources