java simple animation with threads - java

I'm looking to create a simple 2D animation using a thread. Once the thread is launched, i'm having trouble figuring out exactly what to put in the run method. Right now, the objects of the Particle class are painted on the frame but there's no animation. Also i could use your help with how to close the thread when the user closes the frame
public class ParticleFieldWithThread extends JPanel implements Runnable{
private ArrayList<Particle> particle = new ArrayList<Particle>();
boolean runnable;
public ParticleFieldWithThread (){
this.setPreferredSize(new Dimension(500,500));
for(int i = 0; i < 100; i++) {
particle.add(new Particle());
}
Thread t1 = new Thread();
t1.start();
}
public void run () {
while (true ) {
try {
Thread.sleep(40);
for (Particle p : particle) {
p.move();
}
repaint();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setColor(Color.RED);
for (Particle p : particle) {
g2.fill(new Rectangle2D.Double(p.getX(), p.getY(), 3, 3));
}
}
public static void main(String[] args) {
final JFrame f = new JFrame("ParticleField");
final ParticleFieldWithThread bb = new ParticleFieldWithThread();
f.setLayout(new FlowLayout());
f.add(bb);
f.pack();
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Here's the particle class
public class Particle {
private double x , y ;
Random r = new Random();
public Particle () {
x = r.nextDouble()*500;
y = r.nextDouble()*500;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public void move() {
x += r.nextBoolean() ? 1 : - 1;
y += r.nextBoolean() ? 1 : - 1;
//System.out.println("x : " + x+" y: " + y);
}
}

This does nothing of use:
Thread t1 = new Thread();
t1.start();
You need to pass a Runnable (in your code, it would be the current object of the class, the this) into the Thread's constructor for it to have any meaning or function. i.e.,
Thread t1 = new Thread(this);
t1.start();
For my money, I'd do something completely different and would use a Swing Timer for simple Swing animation.

Related

Trouble repainting game with swing timer

This is a follow up to my previous question. I am making Frogger in Java I'm trying to implement a swing timer but it doesn't seem to be working. I'm having some trouble setting it up.
I've tried implementing it in different areas of my code and have come to no conclusion as to what's wrong with it. I followed multiple tutorials and nothing has worked.
private int delay = 7;
public CPT() {
setLayout(new BorderLayout());
label = new JLabel("Frogger");
frame1 = new JFrame("Main");
label.setFont(new Font("Serif", Font.BOLD,50));
label.setBounds(275,10,250,250);
button1 = new JButton("PLAY");
button1.setBounds(300,350,100,50);
button1.setOpaque(false);
button1.setVisible(true);
this.setOpaque(false);
this.setLayout(null);
this.add(label);
button1.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame1.setVisible(true);
frame1.setSize(700,500);
frame1.setResizable(false);
button1.setVisible(false);
frame1.add(new TrainCanvas());
frame1.add(p1);
frame1.addKeyListener(new frog());
}
});
this.add(button1);
}
This is the main constructor of my class
class TrainCanvas extends JComponent {
private int lastX = 0;
private int lastX_1 = 0;
private int lastX_2 = 0;
public TrainCanvas() {
// Thread animationThread = new Thread(new Runnable() {
// public void run() {
// while (true) {
// repaint();
// try {Thread.sleep(10);} catch (Exception ex) {}
// }
// }
// });
//
// animationThread.start();
// }
Timer time = new Timer(delay, this {
TrainCanvas.repaint();
});
time.start();
}
public void paintComponent(Graphics g) {
Graphics2D gg = (Graphics2D) g;
int w = getWidth();
int h = getHeight();
int trainW_1 = 100;
int trainH_1 = 5;
int trainSpeed_1 = 3;
int x = lastX + trainSpeed_1;
if (x > w + trainW_1) {
x = -trainW_1;
}
gg.setColor(Color.BLACK);
gg.fillRect(x, h/2 + trainH_1, trainW_1, trainH_1);
lastX = x;
//Draw Frog
frog = new Rectangle(f_x,f_y,25,25);
g3.fill(frog);
g3.setColor(Color.GREEN);
}
}
This is the code that draws the main game I previously used a thread but was told that a swing timer is more useful.
The timer is supposed to repaint my game but it seems as if i can't even implement it properly even though I was told this was right. Any help is appreciated!

Java Moving an Object Across the Screen

I am trying to move a train across my java window and am having serious problems. I have a Train class in which I made the train, and a Driver class which is supposed to move the train. I need to make the whole train move from right to left until it 'passes' the left edge of the screen. Then add an if statement to change the dx so the train restarts on the right side . The below is what I have tried but it is not working. Can anyone help me please??
public class Driver extends GraphicsProgram
{
//~ Instance/static variables .............................................
private static final int N_STEPS = 1000;
private static final int PAUSE_TIME = 20;
private static final double TRAIN_LENGTH = 320;
//~ Constructor ...........................................................
// ----------------------------------------------------------
/**
* The run() method of the Driver Class.
* Creates an instance of the Train Class.
* Responsible for animating the train across the screen.
*/
public void run()
{
Train train = new Train(getGCanvas());
for (int i = 0; i < N_STEPS; i++) {
train.move(-100, 0);
pause(PAUSE_TIME);
}
Here is a little demo made with swing. Just replace the black rectangle with an image of your train and you're done.
The trick is to use a separate thread (or timer) to do the animation loop (often called game loop). The loop only tells your window to redraw itself, and on each redraw, you first compute the new positions of the animated objects, then you draw them.
import javax.swing.*;
import java.awt.*;
public class TrainDemo {
public static void main(String[] args) {
JFrame frame = new JFrame("Train Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(800, 400);
frame.setLocationRelativeTo(null);
frame.add(new TrainCanvas());
frame.setVisible(true);
}
}
class TrainCanvas extends JComponent {
private int lastX = 0;
public TrainCanvas() {
Thread animationThread = new Thread(new Runnable() {
public void run() {
while (true) {
repaint();
try {Thread.sleep(10);} catch (Exception ex) {}
}
}
});
animationThread.start();
}
public void paintComponent(Graphics g) {
Graphics2D gg = (Graphics2D) g;
int w = getWidth();
int h = getHeight();
int trainW = 100;
int trainH = 10;
int trainSpeed = 3;
int x = lastX + trainSpeed;
if (x > w + trainW) {
x = -trainW;
}
gg.setColor(Color.BLACK);
gg.fillRect(x, h/2 + trainH, trainW, trainH);
lastX = x;
}
}
color c = color(0); float x = 0; float y = 100; float speed = 1;
void setup() { size(200,200); }
void draw() { background(255); move(); display(); }
void move() { x = x + speed; if (x > width) { x = 0; } }
void display() { fill(c); rect(x,y,30,10); }

repaint() not working in a Thread

i just started learning java 2 weeks ago so i don't really understand to much about java yet.I'm trying to make a ball bounce around or move around inside the frame. but it doesn't repaint/update when i run it in a Thread, but it works just fine if i use a while loop or a Timer instead, I don't understand what I'm doing wrong. This is the Thread version:
public class Game {
public static void main(String args[]){
Thread t1 = new Thread(new Ball());
Ball ball1 = new Ball();
JFrame frame = new JFrame("Breakout");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBackground(Color.WHITE);
frame.setSize(1000, 1000);
frame.setVisible(true);
frame.add(ball1);
t1.start();
}
}
public class Ball extends JPanel implements Runnable{
public int x = 5, y = 5;
public int speedx = 5, speedy = 5;
public int width = 30, height = 30;
public void run() {
while (true){
try {
Physics();
repaint();
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.RED);
g.fillOval(x, y, width, height);
}
public void Physics(){
x += speedx;
y += speedy;
if(0 > x || x > 1000){
speedx = -speedx;
}
if(0 > y || y > 1000){
speedy = -speedy;
}
}
}
You are using two different Ball objects:
Thread t1 = new Thread(new Ball());
Ball ball1 = new Ball();
Change the order:
Ball ball1 = new Ball();
Thread t1 = new Thread(ball1);
Swing is not thread safe. You have to call repaint from the main thread.

Reference of graphics 2d object doesn't work in orphan Thread

I am trying to design a simple game using Graphics2D in a JPanel. I am able to draw normal objects by overriding the paintComponent() method. But when I reference the Graphics2D object inside a orphan Thread, it does not work. Where am I going wrong?
public void paintComponent(Graphics g) {
super.paintComponent(g);
g2d = (Graphics2D) g;
g2d.drawString("sample",60,100); //Works fine
if(<Certain Condition>){
new Thread(new Runnable(){
//Some Code Here
public void run() {
try{
g2d.drawString("sample2",60,100); //Does not work.. :(
System.out.println("Test Print"); //Shows Output
}
catch (Exception e)
{
}
}
}).start();
}
}
Here is the complete code for reference. This is essentially a 'ping pong ball' game. Its working well but I am not able to highlight an increase in score when the ball hits the striker. The important part of code is highlighted. It's SSCCE.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.Random;
public class MovingBall extends JPanel {
int XPos, YPos;
int speedX, speedY;
int diameter;
private JButton jButton1 = new JButton();
private JButton jButton2 = new JButton();
private JLabel jLabel1 = new JLabel();
private static Timer timer;
private static MovingBall movingball;
private int w,h;
private int strikerHeight;
private int strikerWidth;
private int score;
private boolean isBallMoving;
int strikerYPos;
Graphics2D g2d;
public MovingBall() {
//Striker Properties
strikerHeight = 100;
strikerWidth = 20;
strikerYPos = strikerHeight/2;
//Ball Properties
isBallMoving = false;
XPos = strikerWidth + 5;
YPos = 0;
Random r = new Random();
speedX = 2+ Math.abs(r.nextInt()) % 5;
speedY = 2+ Math.abs(r.nextInt()) % 5;
diameter = 50;
//UI Objects
try {
jbInit();
} catch (Exception e) {
e.printStackTrace();
}
movingball = this; //Helps to access the current class object in inner classes
//Create a timer for animation
timer = new Timer(1, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
movingball.repaint();
}
});
timer.start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g2d = (Graphics2D) g;
Dimension size = getSize();
Insets insets = getInsets();
w = size.width - insets.left - insets.right;
h = size.height - insets.top - insets.bottom;
//Paint the striker
g2d.setColor(Color.DARK_GRAY);
if(strikerYPos < strikerHeight/2) //Top End
g2d.fillRect(0,0, strikerWidth, strikerHeight);
else if(strikerYPos > (h-strikerHeight/2)) //Bottom End
g2d.fillRect(0,h-strikerHeight, strikerWidth, strikerHeight);
else //Anywhere in the middle
g2d.fillRect(0,strikerYPos - (strikerHeight/2), strikerWidth, strikerHeight);
//Paint the ball
if (isBallMoving) {
XPos += speedX;
YPos += speedY;
g2d.drawOval(XPos, YPos, diameter,diameter);
if((XPos+diameter) >= w)
{
//speedX *= -1;
speedX = ((int)Math.signum((double)speedX))*(-1) * (2+ Math.abs(new Random().nextInt()) % 5);
XPos = w-diameter-1;
}
if(XPos <= strikerWidth)
{
if((YPos+diameter/2) >= (strikerYPos-strikerHeight/2) && (YPos+diameter/2) <= (strikerYPos+strikerHeight/2))
{
score++;
//////////////////////////////////////////////////////////////////////
/////THIS IS THE PART TO FOCUS ON///////////////////////////////////////
/////WHEN THE BALL HITS THE STRIKER, I SHOW A '+1' TEXT FADING UPWARDS FROM THE POINT OF HIT
/////(THIS IS TO HIGHLIGHT A +1 INCREASE IN SCORE)///////////////////
//////NOW SINCE THE BALL MAY HIT THE STRIKER AGAIN BEFORE THE PREVIOUS +1 HAS COMPLETELY FADED,
//////I HAVE MADE THIS SIMPLE THREAD TO CREATE A +1 EVERY TIME THERE IS A HIT. SO THERE CAN BE MULTIPLE
//////+1 ON THE SCREEN.
//-------------------------------SADLY, SOMETHING IS WRONG-------------------
//Print a '+1' to show score increase
new Thread(new Runnable(){
int yStart = strikerYPos;
int fadeLength = 0;
Timer pointTimer;
int MAX_FADE_LEN = 50;
public void run() {
try
{
pointTimer = new Timer(1, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
if(fadeLength >= MAX_FADE_LEN)
pointTimer.stop();
g2d.setColor(new Color(0,0,0,255));
g2d.setFont(new Font("Times",Font.BOLD,20));
g2d.drawString("+1",60,yStart - fadeLength);
g2d.drawOval(100,100,50,50);
System.out.println("Drawn +1 at x = " + 60 + " y = " + (yStart - fadeLength));
fadeLength++;
}
});
pointTimer.start();
}
catch (Exception e)
{
}
}
}).start();
////////////////THREAD ENDS HERE//////////////////////
}
else
{
score--;
}
//SHOW THE SCORE ON THE LABEL
jLabel1.setText("Score: " + score);
speedX = ((int)Math.signum((double)speedX))*(-1) * (2+ Math.abs(new Random().nextInt()) % 5);
XPos = strikerWidth+1;
}
if(YPos <= 0)
{
speedY = ((int)Math.signum((double)speedY))*(-1) * (2+ Math.abs(new Random().nextInt()) % 5);
YPos = 0;
}
if((YPos+diameter) >= h)
{
speedY = ((int)Math.signum((double)speedY))*(-1) * (2+ Math.abs(new Random().nextInt()) % 5);
YPos = h-diameter;
}
} else {
g2d.drawOval(XPos,YPos,diameter,diameter);
return;
}
}
public static void main(String[] args) {
JFrame frame = new JFrame("Magic Ball");
movingball = new MovingBall();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(movingball);
frame.setSize(450, 700);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private void jbInit() throws Exception {
jButton1.setText("Start");
jButton1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
jButton1_actionPerformed(e);
}
});
jButton2.setText("Stop");
jButton2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
jButton2_actionPerformed(e);
}
});
jLabel1.setText("Score:0");
this.add(jButton1, null);
this.add(jButton2, null);
this.add(jLabel1, null);
this.setBackground(Color.white);
this.addMouseMotionListener(new MouseMotionListener() {
public void mouseMoved(MouseEvent e) {
int coordX = e.getX();
if(coordX < 200)
strikerYPos = e.getY();
}
public void mouseDragged(MouseEvent e) {
}
});
}
private void jButton1_actionPerformed(ActionEvent e) {
if(!isBallMoving)
isBallMoving = true;
}
private void jButton2_actionPerformed(ActionEvent e) {
isBallMoving = false;
}
}
everything inside paintComponent is repainted (automatically) on every mouse, key and internall methods implemented in API, then you thread probably never ended, there can be bunch of concurently Threads, nothing is repainted, displayed
output to the Swing GUI must be done on EDT
use Swing Timer instead of new Thread(new Runnable(){
call repaint()
I don't think many people would consider almost 250 LOC to be 'short' (though I must admit I was deliberately vague when writing the SSCCE document). OTOH I adapted my shorter source seen here to an animated example that shows a 'fade effect' on mouse clicks. Adapting it to your needs is left as an exercise for ..you.
This source shows how to change the drawn string over a period of 5 seconds. It uses the same Thread (the EDT) for both the main (bouncing ball) and fade animation.
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import javax.swing.*;
class ShapeCollision {
private BufferedImage img;
private Area walls;
int x;
int y;
int xDelta = 3;
int yDelta = 2;
ArrayList<Strike> strikes;
/**
* A method to determine if two instances of Area intersect
*/
public boolean doAreasCollide(Area area1, Area area2) {
boolean collide = false;
Area collide1 = new Area(area1);
collide1.subtract(area2);
if (!collide1.equals(area1)) {
collide = true;
}
Area collide2 = new Area(area2);
collide2.subtract(area1);
if (!collide2.equals(area2)) {
collide = true;
}
return collide;
}
ShapeCollision() {
int w = 400;
int h = 200;
img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
final JLabel imageLabel = new JLabel(new ImageIcon(img));
x = w / 2;
y = h / 2;
strikes = new ArrayList<Strike>();
MouseListener strikeListener = new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
Strike s = new Strike(e.getPoint(),System.currentTimeMillis());
strikes.add(s);
}
};
imageLabel.addMouseListener(strikeListener);
walls = new Area(new Rectangle(0, 0, w, h));
ActionListener animate = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
animate();
imageLabel.repaint();
}
};
Timer timer = new Timer(50, animate);
timer.start();
JOptionPane.showMessageDialog(null, imageLabel);
timer.stop();
}
public void animate() {
Graphics2D g = img.createGraphics();
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.BLACK);
g.fillRect(0, 0, img.getWidth(), img.getHeight());
x += xDelta;
y += yDelta;
int s = 15;
Area player = new Area(new Ellipse2D.Double(x, y, s, s));
// Acid test of edge collision;
if (doAreasCollide(player, walls)) {
if (x + s > img.getWidth() || x < 0) {
xDelta *= -1;
}
if (y + s > img.getHeight() || y < 0) {
yDelta *= -1;
}
}
g.setColor(Color.ORANGE);
g.setColor(Color.YELLOW);
g.fill(player);
for (Strike strike : strikes) {
strike.draw(g);
}
g.dispose();
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
new ShapeCollision();
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
SwingUtilities.invokeLater(r);
}
}
class Strike {
private Point point;
private long started;
private final long DURATION = 5000;
private boolean expired = false;
Strike(Point point, long time) {
this.point = point;
started = time;
}
public void draw(Graphics g) {
long now = System.currentTimeMillis();
long age = now - started;
if (age>DURATION) {
expired = true;
return;
}
double fraction = 1d-((double)age/(double)DURATION);
int alpha = (int)(fraction*255d);
Color c = new Color(255,255,255,alpha);
g.setColor(c);
String s = point.x + "," + point.y;
g.drawString( s, point.x, point.y );
}
public boolean isExpired() {
return expired;
}
}
As i understand - you save the Graphics2D object into g2d variable and trying to paint something onto it from a separate thread later? If so - don't do it. It is a really bad thing to do. Really.
If you want to modify (animate/change) whatever is painted on your component - simply change the data/model which affects the painting and than repaint the whole component or its modified part (any rectangle within the component bounds).
For example in your example case - keep painted string coordinates outside the paint method and modify them in a separate thread and then just call repaint on the component each time you change them. With each repaint string will be painted at the updated coordinates.
Also note that repaint might be called outside of the EDT (Event Dispatch Thread) as it will perform the actual repaint in EDT anyway.
Here is some random example of animation:
public class AnimationTest
{
private static List<Point> locationData = new ArrayList<Point> ();
private static List<Boolean> directionData = new ArrayList<Boolean> ();
public static void main ( String[] args )
{
locationData.add ( new Point ( 5, 25 ) );
directionData.add ( true );
final JComponent canvas = new JComponent ()
{
protected void paintComponent ( Graphics g )
{
super.paintComponent ( g );
Graphics2D g2d = ( Graphics2D ) g;
for ( int i = 0; i < locationData.size (); i++ )
{
Point p = locationData.get ( i );
g2d.drawString ( "Some string #" + i, p.x, p.y );
}
}
};
canvas.addMouseListener ( new MouseAdapter ()
{
public void mousePressed ( MouseEvent e )
{
locationData.add ( e.getPoint () );
directionData.add ( true );
canvas.repaint ();
}
} );
JFrame frame = new JFrame ();
frame.getContentPane ().setLayout ( new BorderLayout () );
frame.getContentPane ().add ( canvas );
frame.setSize ( 500, 500 );
frame.setLocationRelativeTo ( null );
frame.setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
frame.setVisible ( true );
Timer timer = new Timer ( 1000 / 48, new ActionListener ()
{
public void actionPerformed ( ActionEvent e )
{
for ( int i = 0; i < locationData.size (); i++ )
{
Point p = locationData.get ( i );
if ( directionData.get ( i ) )
{
if ( p.y < canvas.getHeight () - 1 )
{
p.y += 1;
}
else
{
directionData.set ( i, false );
}
}
else
{
if ( p.y > 20 )
{
p.y -= 1;
}
else
{
directionData.set ( i, true );
}
}
}
canvas.repaint ();
}
} );
timer.start ();
}
}
You can find here:
Data lists on which painting and animation are based with single initial element
Data modification through mouse interaction
Proper canvas update on any data changes
Example is not too optimized, but should be enough to understand the concept.
Print the graphicsobject, if it changes between calls in paintComponent it means the one you are passing to the orphan thread is a dead object which swing doesn't use anymore for painting on the current component.
Swing is a platform-independent, Model-View-Controller GUI framework for Java, which follows a single-threaded programming model.
You should use dispatcher. http://en.wikipedia.org/wiki/Event_Dispatch_Thread

Java Swing: JPanels painting over each other

Alright, so I have two JFrames each with a different implementation of JPanel sitting inside of them. When I call repaint() on the JPanels, what is painted on one JPanel also becomes painted on the other JPanel.
I know I could take care of this by calling something like g.clearRect(), but there are too many components to repaint every time.
Any idea why this is happening?
//This makes the two JFrames and JPanels, sets everything up
public void makeSpaceSimulation() {
int dimen = 10000;
int scale = 20;
int numOfClusters = 30;
int planPerCluster = 2000;
SpaceShip s = new SpaceShip(dimen, scale);
QuadTree t = new QuadTree(dimen, 20);
new PlanetCreationTest(t, dimen, scale, numOfClusters, planPerCluster);
makeMap(dimen, scale, s, t);
makePOV(s, t, scale, dimen);
}
public void makeMap(int dimen, int scale, SpaceShip s, QuadTree t) {
final JFrame f = new JFrame();
f.setSize(dimen / scale, dimen / scale);
f.setLocation(0, 0);
f.setTitle("Map Panel");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mapP = new MapPanel(scale, s, dimen);
mapP.setLayout(new BorderLayout());
mapP.addTree(t);
f.add(mapP);
f.setVisible(true);
Insets i = f.getInsets();
f.setSize(dimen / scale + (i.left + i.right) + 2, dimen / scale
+ (i.top + i.bottom) + 2);
Thread th = new Thread() {
public void run() {
while (true) {
mapP.repaint();
}
}
};
th.start();
}
public void makePOV(final SpaceShip s, QuadTree t, int scale, int dimen) {
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
JFrame f = new JFrame();
f.setSize(500, 500);
f.setLocation(screenSize.width - 500, 0);
f.setTitle("POV Panel");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
povP = new POVPanel(s, scale, dimen);
povP.setLayout(new BorderLayout());
povP.addTree(t);
povP.setFocusable(true);
povP.addKeyListener(new KeyListener() {
#Override
public void keyPressed(KeyEvent arg0) {
final int i = arg0.getKeyCode();
Thread th = new Thread() {
public void run() {
if (i == 39) {
s.moveRight();
} else if (i == 37) {
s.moveLeft();
} else if (i == 40) {
s.moveDown();
} else if (i == 38) {
s.moveUp();
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
th.start();
}
#Override
public void keyReleased(KeyEvent arg0) {
}
#Override
public void keyTyped(KeyEvent arg0) {
}
});
f.add(povP);
f.setVisible(true);
Insets i = f.getInsets();
f.setSize(dimen / 20 + (i.left + i.right) + 2, dimen / 20
+ (i.top + i.bottom) + 2);
Thread th = new Thread() {
public void run() {
while (true) {
povP.repaint();
}
}
};
th.start();
}
//here's the MapPanel
public class MapPanel extends JPanel {
private QuadTree q;
private int scale;
private int dimen;
private SpaceShip s;
private boolean firstDraw;
public MapPanel(int scale, SpaceShip s, int dimen) {
this.dimen = dimen;
q = new QuadTree(0, 0);
this.scale = scale;
this.s = s;
firstDraw = true;
}
public void addTree(QuadTree q) {
this.q = q;
}
public void paintComponent(Graphics g) {
if (firstDraw) {
q.draw(g, scale, new Point(0, 0));
s.drawScaledGeometry(g);
System.out.println("Totally drew that");
firstDraw = false;
} else {
g.clearRect(s.viewDistance.x/scale, s.viewDistance.y/scale,
s.viewDistance.width/scale, s.viewDistance.height/scale);
q.quadDraw(g, scale, s.viewDistance, new Point(0, 0));
s.drawScaledGeometry(g);
}
}
}
//and this is the POVPanel
public POVPanel(SpaceShip s, int scale, int dimen) {
super();
this.s = s;
// this.scale = scale;
this.dimen = dimen;
}
public void addTree(QuadTree q) {
this.q = q;
}
public void paintComponent(Graphics g) {
g.clearRect(0, 0, dimen / 20, dimen / 20);
q.quadDraw(g, 1, s.viewDistance, s.getMoved());
s.drawGeometry(g);
}
This is (one) of your problems...
public void paintComponent(Graphics g) {
if (firstDraw) {
q.draw(g, scale, new Point(0, 0));
s.drawScaledGeometry(g);
System.out.println("Totally drew that");
firstDraw = false;
} else {
g.clearRect(s.viewDistance.x/scale, s.viewDistance.y/scale,
s.viewDistance.width/scale, s.viewDistance.height/scale);
q.quadDraw(g, scale, s.viewDistance, new Point(0, 0));
s.drawScaledGeometry(g);
}
}
The Graphics context is shared during the paint cycle, meaning when you get it, you will be getting the same context used to paint all the other components on the screen.
You MUST call super.paintComponent(g), which will take care of preparing the graphics context for this component to start painting.
Update #1
Again...
public void paintComponent(Graphics g) {
super.paintComponent(g); // <-- Call me instead...
//g.clearRect(0, 0, dimen / 20, dimen / 20);
q.quadDraw(g, 1, s.viewDistance, s.getMoved());
s.drawGeometry(g);
}
Update #2
I'm also seriously concerned by this
while (true) {
mapP.repaint();
}
This could seriously impact the performance of you application. You DO NOT control the paint cycle, that's the responsibility of the repaint manager. The repaint manager will decide when a repaint is required. Calling repaint repetitively like this could actually cause the repaint manager to delay actually scheduling a paint cycle.
Much better to use a javax.swing.Timer, it's simpler and safer...
Timer timer = new Timer(25, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
mapP.repaint();
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
You might find reading through
Performing Custom Painting
Painting in AWT and Swing
Helpful
Update #3
Avoid KeyListener, it will only make you cry. Instead, use the key bindings API

Categories

Resources