Moving a rectangle incredibly choppy - java

I've created a simple program that draws a rectangle which falls down the screen at a constant rate. I first run Main.java:
package highst;
public class Main {
public static void main(String args[]){
new GameFrame();
}
}
which creates a new instance of GameFrame.java:
package highst;
import javax.swing.JFrame;
public class GameFrame extends JFrame {
public GameFrame() {
super("Falling rectangle");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(800, 600);
GameLogic game = new GameLogic();
this.getContentPane().add(game);
this.setVisible(true);
game.run();
}
}
Which in turn creates a new instance of GameLogic.java:
package highst;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JPanel;
public class GameLogic extends JPanel implements Runnable, KeyListener {
Marvin marvin;
private enum GameState{
Running, Dead
}
GameState state = GameState.Running;
public GameLogic(){
marvin = new Marvin(50, 50);
Thread thread = new Thread(this);
thread.start();
addKeyListener(this);
setFocusable(true);
this.setBackground(Color.black);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.white);
g.fillRect(marvin.getX(), marvin.getY(), 50, 50);
}
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_SPACE){
marvin.jump();
}
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void run() {
if(state == GameState.Running){
while(true){
marvin.update();
repaint();
try {
Thread.sleep(17);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
Which finally makes use of what will be my playable character, Marvin.java who is now a white rectangle:
package highst;
public class Marvin {
private int x, y;
public Marvin(int x, int y){
this.x = y;
this.y = y;
}
public void update(){
y -= -1;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void jump() {
x += 1;
}
}
It runs fine but the rectangle is not painted smoothly as it falls down the page. It seems to jump a few pixels at a time. I thought sleeping the thread for 17 milliseconds would cause everything to render smoothly. What am I doing wrong?

To have smooth animation, you need to make updates to the screen at a constant rate.
Here, you're doing a graphic repaint, which could take any amount of time, then waiting 17ms no matter what. This leads to each frame taking a different amount of time. The first frame might be done in 2ms, the next might take 5ms, then 3ms and so on... your frames will be displayed for 19ms then 22ms then 20ms...
What you need is a dedicated thread whose only job is to wait the right amount of time and then signal the main thread to repaint. Then your frames (provided they don't take more than 17ms to paint) come out every 17ms, exactly on cue.
Here's an tutorial on animation in Java applets, you should find it relevant.

Try these:
1> Reducing the sleeping time of the thread to see the effects and get the optimum speed of falling.
2> Make use of Double Buffering (a concept in which the screen is painted first in memory and then painted onto the display monitor):
Double Buffering - Docs
Double Buffering - Google Search Results
3> Simple suggestion: Avoid using sleep(). Instead use Timers. It is a very interesting and powerful substitute for threads. Also it will not create problems in the later stages of your game development. Check these links out:
Concurrency in java
Timer in Java
4> Check out these interesting tutorials about animation in Java:
Java World
Clear Rice

There are several things wrong...
You're ignoring the Initial Threads of Swing and not starting your UI within the context of the EDT, which leads to...
You are calling run on the instance of GameLogic AND creating a Thread which will call run again, setting up to loops. It's actually dumb luck that this worked at all. What this does is calls marvin.update twice...and random intervals, meaning that the object is moved at inconsistent rates
Basically, you should remove the line game.run() and wrap new GameFrame(); within the context of a EventQueue.invokeLater call...
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
GameFrame frame = new GameFrame();
}
});
}
Personally, I'd recommend against extending from JFrame directly, you're not adding any functionality to the class, and simply create an instance within the main which you add your GameLogic panel to. This makes the game infinitely more flexible in terms of deployment as you're not locking yourself into a single container.
I'd also encourage you not use KeyListener, but instead, make use the Key Bindings API instead, as it solves the focus issues associated with KeyListener

Related

I am not sure what is incorrect

I am creating a small Java Jpanel game in which I am supposed to have a rocket that moves up and down via arrows and fires via space.
The firing method should work like this: Space bar pressed, thing fires and moves across screen , and then when it hits a certain x, it disappears. Also, you can only fire once until the other bullet disappears.
I do not know what I am doing wrong. For one, as soon as my code starts you can see a bullet flying across the screen.
2nd, the bullet is not disappearing.
3rd, even though the other bullet is still visible, it allows me to fire again.
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.File;
import java.io.IOException;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.swing.*;
#SuppressWarnings("serial")
public class SpaceGame extends JPanel implements ActionListener{
Timer t = new Timer(2, this);
private ImageIcon rocket,asteroid,bullet;
private JLabel rocketlabel,ast1,ast2,ast3,bulletLabel;
public static int y=90,dy=0,bulletX=110,bulletY,i=0,canFire;
//public sound sound;
static boolean bulletFired=false;;
static JFrame f = new JFrame();
SpaceGame(){
this.setBackground(Color.black);
rocket = new ImageIcon(getClass().getResource("rocketFinal.png"));
rocketlabel= new JLabel(rocket);
this.add(rocketlabel);
asteroid = new ImageIcon(getClass().getResource("asteroid.png"));
ast1=new JLabel(asteroid);
ast2=new JLabel(asteroid);
ast3=new JLabel(asteroid);
bullet = new ImageIcon(getClass().getResource("bulletReal.png"));
bulletLabel = new JLabel(bullet);
canFire=1;
bulletLabel.setVisible(false);
this.add(ast1);this.add(ast2);this.add(ast3);this.add(bulletLabel);
f.addKeyListener(new controller());
this.setLayout(null);
this.setVisible(true);
}
public class controller implements KeyListener{
#Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if(keyCode== KeyEvent.VK_UP) {
dy=-1;
}
if(keyCode== KeyEvent.VK_DOWN) {
dy=1;
}
if(keyCode== KeyEvent.VK_SPACE) {
if(canFire==0) {
System.out.println(String.valueOf(canFire));
bulletFired = true;
bulletY = y;
bulletX=110;
}canFire=1;
}
}
#Override
public void keyReleased(KeyEvent e) {
int key = e.getKeyCode();
switch(key) {
case KeyEvent.VK_UP: dy=0; break;
case KeyEvent.VK_DOWN: dy=0; break;
}
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
rocketlabel.setBounds(45,y,rocket.getIconWidth(),80);
fireBullet();
paintStars(g);
t.start();
}
public void paintStars(Graphics g) {
g.setColor(Color.yellow);
for(int i=0; i<5;i++) {
Random rand = new Random();
int o = rand.nextInt(500);
int p = rand.nextInt(300);
g.fillOval(o, p, 3, 3);
}
}
public void actionPerformed(ActionEvent e) {
if(y==-20) y=249;
if(y==250)y=-20;
y+=dy;
if(bulletFired=true) {
bulletX++;
if(bulletX==455)bulletFired=false;bulletLabel.setVisible(false);System.out.println(String.valueOf(bulletX)); canFire=0;
}
repaint();
}
public void fireBullet(){
if(bulletFired=true) {
bulletLabel.setVisible(true);
bulletLabel.setBounds(bulletX,bulletY+25,bullet.getIconHeight(),bullet.getIconWidth());
}
}
public static void main(String[] args) {
String filepath = "SpaceGameMusic.wav";
musicStuff musicPlayer = new musicStuff();
musicPlayer.playMusic(filepath);
SpaceGame t = new SpaceGame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(t);
f.setSize(500,335);
f.setVisible(true);
f.setResizable(false);
}
}
For one, as soon as my code starts you can see a bullet flying across the screen.
The paintComponent() method is for painting only. You can't control when Swing will determine a component needs to be repainted.
So, for example:
t.start();
should NOT be in the painting method. As soon as the frame is made visible the panel will be painted and the Timer will be started.
You application code should control when the Timer is started.
Other issues:
you should not be using static variables. The variable should simply be instances of your class.
the paintStars() method should not generate random locations. Again. a painting method should only paint the current state of the class. So if you want to change the location of the stars you should have a method like randomizeStars(). In this method you would update an ArrayList of Point objects. Each Point instance would represent the location of a star. Then the paintStars() method would simply iterate through the ArrayList and paint each star.
you should not be using a KeyListener. A KeyListener only works if a component has focus. You can't guarantee that your component will lose focus. Instead you should be using Key Bindings. Key bindings allow you to handle a KeyEvent even if the component doesn't have focus. See Motion Using the Keyboard for more information and a working example.
you can only fire once until the other bullet disappears
Your canFire variable should be a boolean variable so it only has true/false values. Again you have a method that sets the state. Your game logic will then check the state before firing the bullet again.
if(y==-20) y=249;
if(y==250)y=-20;
Don't hardcode values. The number should be based on the size of your panel. So you use methods like getWidth() and getHeight() to determine the current size of the panel.
The problem was quite simply that I had forgotten to use == in my if(boolean) statements.

Java Drawing Multiple Squares in Same JFrame

I am trying to make an animation with multiple thread. I want to paint n squares where this n comes from commend-line argument. Every square has their x-y coordinates, colors and speed. They are moving to the right of the frame with different speed, color and coordinates. Since I am using multi thread I assume I have to control each squares. So I have to store each square object in the ArrayList. However, I am having trouble with painting those squares. I can paint one square but when I try to paint multiple squares, it does not show. Here what I have done so far:
DrawSquare.java
import java.awt.Graphics;
import javax.swing.JPanel;
public class DrawSquare extends JPanel {
public Square square;
public DrawSquare() {
square = new Square();
}
#Override
public void paintComponents(Graphics g) {
// TODO Auto-generated method stub
super.paintComponents(g);
}
#Override
public void paint(Graphics g) {
// TODO Auto-generated method stub
super.paint(g);
g.setColor(square.getC());
g.fillRect(square.getX(), square.getY(), square.getR(), square.getR());
}
}
Square.java
import java.awt.Color;
import java.util.Random;
public class Square {
private int x,y,r,s;
private Color c;
private Random random;
public Square() {
random = new Random();
x = random.nextInt(100) + 30;
y = random.nextInt(100) + 30;
r = random.nextInt(50) + 20;
s = random.nextInt(20) + 5;
c = new Color(random.nextInt(255),random.nextInt(255),random.nextInt(255));
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getR() {
return r;
}
public int getS() {
return s;
}
public Color getC() {
return c;
}
}
Animation.java
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Animation extends JFrame implements Runnable {
private JPanel panel;
private DrawSquare square;
public Animation() {
}
public static void main(String[] args) {
Animation w = new Animation();
DrawSquare square = new DrawSquare();
JFrame f = new JFrame("Week 9");
int n = Integer.parseInt(args[0]);
f.setVisible(true);
f.setSize(700,700);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setResizable(false);
for(int i=0; i<n; i++) {
f.getContentPane().add(square);
}
}
#Override
public void run() {
// TODO Auto-generated method stub
}
}
So, starting with...
public class DrawSquare extends JPanel {
public Square square;
public DrawSquare() {
square = new Square();
}
#Override
public void paintComponents(Graphics g) {
// TODO Auto-generated method stub
super.paintComponents(g);
}
#Override
public void paint(Graphics g) {
// TODO Auto-generated method stub
super.paint(g);
g.setColor(square.getC());
g.fillRect(square.getX(), square.getY(), square.getR(), square.getR());
}
}
As general recommendation, it's preferred to put custom painting in the paintComponent method (note, there's no s at the end)
When paint is called, the Graphics context has already been translated to the component coordinate position. This means that 0x0 is the top/left corner of the component, this also means that...
g.fillRect(square.getX(), square.getY(), square.getR(), square.getR());
is painting the rect at x + x x y + y, which will, at the very least, paint the rect in the wrong position, at worst paint it beyond the visible scope of the component.
You're also not providing any sizing hints for the component, so it's default size will be 0x0, which prevent it from been painted.
Since I am using multi thread I assume I have to control each squares.
Well, since I can't really see what's driving the animation, I imagine that when you say "multi thread" you're suggesting that each square has it's own Thread. In this case, that's a bad idea. Let's put aside the thread synchronisation issues for a moment, more threads doesn't equate to more work you can do, at some point, it will begin to degrade the system performance.
In most cases, a single, well managed thread, is all you really need. You also have to understand that Swing is NOT thread safe. This means that you shouldn't update the UI (or states that the UI relies on) from outside the context of the Event Dispatching Thread.
So, while you're thread can update the position of the rects, you need to take care to ensure that they are not been painted while they are been update. Once you've updated the state, you then need to trigger a paint pass (which is trivial in of itself)
So I have to store each square object in the ArrayList.
Yep, good start
However, I am having trouble with painting those squares. I can paint one square but when I try to paint multiple squares, it does not show.
Okay, so instead of using multiple components, use one. Run through your ArrayList within the paintComponent method of this component and paint all the rects to it. This provides a much simpler way to manage things like bounds detection, as you have only one container to worry about.
I'd highly recommend you have a look at:
Java Bouncing Ball which demonstrates many of the concepts discussed here
Concurrency in Swing
How to use Swing Timers
Performing Custom Painting
Painting in AWT and Swing

How to create new instances of a timer Task instead of canceling and restarting it all over?

Well the thing is that I have a project where I have to make a game on java. In my game there's a spaceship that shoots lasers. I have the mechanics for shooting the laser more or less figured out but I am currently using a timer task to make the laser fly through the JFrame and give the impression a laser was shot.
Problem is that TimerTask seems to bug out as soon as I start shooting many times.
The main goal is to move an object across the screen at a given speed.
Is there something else I could do to achieve this? Is there a better way to implement this?
I appreciate all the help I could get, Thanks.
Here is some of the code:
public Space() {
this.setBackground(Color.BLACK);
this.setCursor(Cursor.getDefaultCursor());
this.addMouseMotionListener(new MouseAdapter() {
public void mouseMoved(MouseEvent e) {
repaint();
x = e.getX()-spaceFighterIcon.getIconHeight()/2;
y = e.getY()-spaceFighterIcon.getIconWidth()/2;
}
public void mouseDragged(MouseEvent e) {
repaint();
x = e.getX()-spaceFighterIcon.getIconHeight()/2; //Positions the cursor on the middle of the spaceShip and viceVersa
y = e.getY()-spaceFighterIcon.getIconWidth()/2;
}
}
);
this.addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent e) {
if(timerRunning = true){
laserTimer.cancel();
laserTimer.purge();
laserFired = false;
}
if(SwingUtilities.isLeftMouseButton(e)){ // Gets where the laser is going to be shot from
repaint();
laserX = e.getX()-spaceFighterIcon.getIconWidth()/6;
laserY = e.getY();
laserFired = true;
}
if(SwingUtilities.isRightMouseButton(e)){
}
if(SwingUtilities.isMiddleMouseButton(e)){
}
}
});
}
public void paintComponent(Graphics g) {
this.graphics = g;
super.paintComponent(g);
spaceFighterIcon.paintIcon(this, g, x, y);
if(laserFired == true){
shootLaser();
}
}
public void shootLaser(){
laserIcon.paintIcon(this, graphics, laserX, laserY-50); // paints the laser
laserTimer = new Timer();
laserTimer.schedule(new AnimateLasers(), 0, 200); // Timer to move the laser across the frame
timerRunning = true;
repaint();
}
public void lasers(){
laserY = laserY-1; // function to move the laser
if(laserY <= 0){
laserTimer.cancel();
laserTimer.purge();
}
}
public class AnimateLasers extends TimerTask {
public void run() {
lasers();
repaint();
}
}
Start by taking a look at Concurrency in Swing and How to use Swing Timers instead of java.util.Timer.
Swing Timer is safer to use with Swing, as it executes it ticks within the context of the Event Dispatching Thread
Also take a look at Painting in AWT and Swing and Performing Custom Painting for more details about how painting works
Don't maintain a reference to the Graphics context outside of the paint method. Your component will be told when it should repaint it self by the system (via the call to the paintComponent method), essentially, you use the time to update the location of the "laser" and call repaint, then paint the laser within the paintComponent when it's called by the paint system

Calling repaint from another class JFrame

I'm trying to call repaint from another class. But it does not work. I have to draw on a frame.
public class Tester extends JFrame{
public static dtest d ;
public static void main(String[] args) {
Tester t = new Tester();
d = new dtest();
test tnew = new test();
}
public static class dtest extends JFrame implements MouseMotionListener{
public static int x,y;
dtest()
{
super("title");
setSize(500,500);
setVisible(true);
addMouseMotionListener(this);
}
#Override
public void mouseDragged(MouseEvent e) {
x = e.getX();
y = e.getY();
repaint();
}
#Override
public void mouseMoved(MouseEvent e) {
// TODO Auto-generated method stub
}
public void paint(Graphics g)
{
System.out.println("I am called");
}
}
public static class test {
public test()
{
for(int i = 0 ; i < 5 ; i++)
{
System.out.println("I am called from run");
d.repaint();
}
}
}
}
this prints
I am called from run
I am called from run
I am called from run
I am called from run
I am called from run
so it does not executing the paint() portion. d.repaint() is not working. why?
Take a look at this page and look at the first answer. It's a similar if not exact question to yours.
JFrame's paint() method has been deprecated. The compiler, or your IDE, should be complaining a bit, especially if you place the #Override tag directly above the method (use this to test if this method can be rewritten... aka what you're trying to do).
This means that its use has been discouraged and some functionality may have been removed. When using javax.swing, you'll want to learn the system completely about JPanels and JComponents. To paint something on a screen, you'll want to add a custom class that extends JPanel with the add(Component c) method. Then, override the paintComponent(Graphics g) method in that class. Make sure to have the first line in that method be super.paintComponent(g); so that the window can refresh itself.
For completeness:
public class MyWindow extends JFrame {
MyPanel thePanel;
public MyWindow(int x, int y) {
setSize(x, y);
thePanel = new MyPanel(x, y);
this.add(thePanel);
}
}
public class MyPanel extends JPanel {
public MyPanel(int x, int y)
setSize(x, y);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(ImageManager.getImage("Cute Puppy"), 40, 40, null); // Or whatever
}
}
So, when the repaint() or revalidate() method is called on the MyWindow, the Panel will recieve a paintComponent call.
Please let me know in the comments if you need any additional help.
Edited:
Since you need to use MouseMotionListener, and I'm still not quite understanding the context and trouble of "I need to call repaint from another class"... I will try my best.
Firstly, check out this tutorial on the Oracle pages. Also, check out the others on GUI's. You'll learn a lot about organization and displaying that will make you realize how their system can work with yours.
Now, for your questions:
i have to use MouseMotionListener.
Not quite... it is a good way for set up but you can run a Thread (something that constantly runs methods over and over) to check the Mouse coordinates. You'll want to start doing this when you get into games and other miscellaneous applications.
new Thread() {
public void run() {
Point mouse;
int mousex;
int mousey;
while (true) {
mouse = MouseInfo.getPointerInfo().getLocation();
mousex = mouse.x - theWindow.getLocationOnScreen().x - 3; // You'll need to get the
// x coordinate, subtract the window's x coordinate, and subtract 3 because of
// the blue border around a standard pc window.
mousey = mouse.y - theWindow.getLocationOnScreen().y - 29; // 29 is top bar height
SomeOtherClass.processMove(mousex, mousey);
}
}
}.start();
Next: I tried that with JPanel but i could not do that. If you read the tutorial at the top of my edit, you see they implement MouseMotionListener with ease.
Next: I prefer to do it with JFrame. If you wish to process the mouse in the JFrame, do the following: Have your JFrame the listener, but the JPanel be where the mouse data comes from. As follows:
public class MyWindow extends JFrame implements MouseMotionListener {
public MyPanel thePanel;
public int x;
public int y;
public MyWindow() {
thePanel = new MyPanel();
thePanel.addMouseMotionListener(this);
// Make this JFrame get called when the mouse
// moves across the panel.
}
#Override
public void mouseDragged(MouseEvent e) {
x = e.getX();
y = e.getY();
thePanel.repaint();
}
#Override
public void mouseMoved(MouseEvent e) {
// TODO Auto-generated method stub
}
}
public class MyPanel extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
// Other painting stuff
}
}
Next: Now i have to update the frame from another class. I could not find a way to update the GUI(the frame) from another class.
Simple. Since the JPanel is what needs to be updated, add the following method to the MyWindow class:
public void repaintWindow() {
thePanel.repaint();
}
And add this to whenever you need to update it:
MyWindow theWindow = new MyWindow();
theWindow.repaintWindow();
Next: all the answers here extended JPanel. So i could not find my answer.
I apologize, but you NEED a panel. It is possible to do with JFrames, but if you want to start doing things raw and low-level, you need to learn how these things work by learning to read the oracle tutorials and the oracle documentation. For now, use JPanels in any ways I've shown you.
Next: from another class I have to draw something on JFrame.Is that possible?
Yes, indeed! Whenever you want to draw something:
MyWindow theWindow = new MyWindow();
Graphics g = theWindow.thePanel.getGraphics();
BufferedImage someRandomImage = SomeRandomClass.getRandomImage();
g.drawImage(someRandomImage, 200, 481, null);
theWindow.repaintWindow();
I really hope I've helped but to program in java you need to use the tools they give you, especially when it comes to high level things like Swing. There are tutorials everywhere for this stuff. Please read them before asking for specific help in the future.

multithreading with java swing for a simple 2d animation

my final goal for this application is to animate several items in the same JPanel at a different speed using a thread for each item.the first part is done however the items move at the same speed and i have no idea on how to fix this problem.
package javagamestutos;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JPanel;
public class Board extends JPanel implements Runnable {
private Star star;
private Thread animator;
ArrayList<Star> items=new ArrayList<Star>();
public Board() {
setBackground(Color.BLACK);
setDoubleBuffered(true);
star=new Star(25,0,0);
Star star2=new Star(50,20,25);
items.add(star2);
items.add(star);
}
public void addNotify() {
super.addNotify();
animator = new Thread(this);
animator.start();
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D)g;
for (Star s : this.items) {
g2d.drawImage(s.starImage, s.x, s.y, this);
}
Toolkit.getDefaultToolkit().sync();
g.dispose();
}
public void run() {
while(true){
try {
for (Star s : this.items) {
s.move();
}
repaint();
Thread.sleep(star.delay);
} catch (InterruptedException ex) {
Logger.getLogger(Board.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
here is the star class wich is the moving item.
package javagamestutos;
import java.awt.Image;
import javax.swing.ImageIcon;
/**
*
* #author fenec
*/
public class Star {
Image starImage;
int x,y;
int destinationX=200,destinationY=226;
boolean lockY=true;
int delay;
public Star(int delay,int initialX,int initialY){
ImageIcon ii = new ImageIcon(this.getClass().getResource("star.png"));
starImage = ii.getImage();
x=initialX;
y=initialY;
this.delay=delay;
}
void moveToX(int destX){
this.x += 1;
}
boolean validDestinatonX(){
if(this.x==this.destinationX){
this.lockY=false;
return true;
}
else
return false;
}
void moveToY(int destY){
this.y += 1;
}
boolean validDestinatonY(){
if(this.y==this.destinationY)
return true;
else
return false;
}
void move(){
if(!this.validDestinatonX() )
x+=1;
if(!this.validDestinatonY() && !this.lockY)
y+=1;
/*if(!this.validDestinatonY())
y+=1;
*/
}
}
and here is the skeleton of the animation that extends a JFrame :
package javagamestutos;
import javax.swing.JFrame;
public class Skeleton extends JFrame {
public Skeleton() {
add(new Board());
setTitle("Stars");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(300, 280);
setLocationRelativeTo(null);
setVisible(true);
setResizable(false);
}
public static void main(String[] args) {
new Skeleton();
}
}
do you have any idea how to achieve my goals?am i using threads proprely?
thank you in advance.
That's because you're invoking the "move" method at a fixed rate specified by the delay of the first "start"
Thread.sleep(star.delay);
So if you move them a little every "n" milliseconds, they will seems to move at the same peace.
If you want them to move at different speed, you have to move them in different thread ( you are using only one now ) Bear in mind the comment by omry,
EDIT
I did something similar just recently
I have two different things so animate so I have two timers ( timers use threads underneath, but they can repeat the execution code every fixed rate ).
The first apply the text to a JLabel every second ( 1000 ms )
final Timer timer = new Timer();
timer.scheduleAtFixedRate( new TimerTask() {
public void run(){
setText();
}
}, 0, 1000 );
And other change the displaying image every 10 seconds ( 10,000 ms )
final Timer imageTimer = new Timer();
imageTimer.scheduleAtFixedRate( new TimerTask() {
public void run() {
setImage();
}
}, 0, 10000 );
I have a video of the result here:
For more advanced ( and nice ) time management you MUST take a look at the "Timing Framework" project which adds additional capabilities to timers.
You should be painting in the AWTDispatchThread. To do that you will want to use something like SwingUtilities.invokeLater(Runnable); This applies not only to your animation, but to the creation and setting visible of your JFrame as well. Failing to do this could result in deadlocks with the painting thread. Also, when moving your painting operations into the SwingUtilites methods, you will not want to include any while(true) loops, as that will commandeer your painting thread.
Generally Swing components should be used from the AWT Event Dispatch Thread (EDT). repaint is one of the methods that is supposedly okay to use off EDT. However, your Star is not and should not be thread-safe.
The easiest approach is to go for EDT-only (at least to start with). Instead of using Thread use javax.swing.Timer which fires on the EDT.
Misc comments: There should be no need for your paint method to dispose of the graphics object sent to it, or for it to sync using Toolkit. The component need not be set to double-buffered, but should be set opaque (JPanel is not guaranteed to be opaque). You should just extend JComponent instead of JPanel, as this is not a panel. It's generally not a great idea for outer classes to implement Runnable. Prefer private variables.
I would suggest you take a look at the open source library trident which does just that, its author, Kirill Grouchnikov is well-known in the Swing world (he is the author of the famous Substance look & feel).
Trident should help you solve the problem of having different objects move at different speeds, without having to create one thread per object (which is a problem in the end).
if you are sure you want to paint in the threads, you can use :
update(getGraphics());
instead of repaint.
this is generally considered bad practice, as you normally paint stuff in the AWT thread.

Categories