Related
I am working on homework for class, and its late because I can't seem to understand the material despite all the research that I am doing. I am a beginner and do not know much in the way of java. Also, this is my first post so please be forgiving when you are reading this.
I am building on source code from my textbook, which I updated recently for past homework, but now I am trying to generate a class that draws multiple squares and moves those objects independently and at different speeds. They will all need to rebound off the walls as well. I followed the instructions and created two arrays that will hold the random x and y values between 1 and 10. However, I struggle with arrays and I am sure that I am not doing it correctly. So, I would love some feedback to see if I have it set up correctly.
I have a the jpanel pulling up and drawing, and as long as there is 1 square it is working fine bouncing off the walls, but things change when I draw more than one. The do not move independently and they also share the same speed. Some even disappear from time to time. This has really thrown me off. I appreciate any help!
In short, I am trying to paint new squares that all travel in different directions and at different speeds. Per the instructions we are suppose create and use a two arrays that handle the x and y values.
Here is what I have so far:
public class DotsPanel extends JPanel
{
private int delay = 15;
private final int SIZE = 7, IMAGE_SIZE = 3; // radius of each dot
private Timer timer;
private int x, y, i;
private ArrayList<Point> pointList;
static int [] xarray = new int [1000];
static int [] yarray = new int [1000];
Random rand = new Random();
//-----------------------------------------------------------------
// Constructor: Sets up this panel to listen for mouse events.
//-----------------------------------------------------------------
public DotsPanel()
{
pointList = new ArrayList<Point>();
int [] xarray = new int [1000];
int [] yarray = new int [1000];
timer = new Timer(delay, new ReboundListener());
addMouseListener (new DotsListener());
addMouseMotionListener (new DotsListener());
setBackground(Color.gray);
setPreferredSize(new Dimension(700, 500));
for(int i = 0; i < xarray.length; i++)
{
xarray[i] = rand.nextInt(7);
yarray[i] = rand.nextInt(7);
}
timer.start();
}
//-----------------------------------------------------------------
// Draws all of the dots stored in the list.
//-----------------------------------------------------------------
public void paintComponent(Graphics page)
{
super.paintComponent(page);
page.setColor(Color.BLUE);
for (Point spot : pointList)
{
page.fillRect(spot.x-SIZE, spot.y-SIZE, 25, 25);
page.drawString("Count: " + pointList.size(), 5, 15);
}
}
//*****************************************************************
// Represents the listener for mouse events.
//*****************************************************************
private class DotsListener implements MouseListener, MouseMotionListener
{
//--------------------------------------------------------------
// Adds the current point to the list of points and redraws
// the panel whenever the mouse button is pressed.
//--------------------------------------------------------------
public void mousePressed(MouseEvent event)
{
pointList.add(event.getPoint());
repaint();
}
public void mouseDragged(MouseEvent event)
{
// initially I had two xarray and yarray in here just like in
// mouseClicked
// but it did not change anything when removed
}
//--------------------------------------------------------------
// Provide empty definitions for unused event methods.
//--------------------------------------------------------------
public void mouseClicked(MouseEvent event)
{
xarray[i] = rand.nextInt(7);
yarray[i] = rand.nextInt(7);
}
public void mouseReleased(MouseEvent event) {}
public void mouseEntered(MouseEvent event) {}
public void mouseExited(MouseEvent event) {}
public void mouseMoved(MouseEvent e) {}
}
private class ReboundListener implements ActionListener
{
//--------------------------------------------------------------
// Updates the position of the image and possibly the direction
// of movement whenever the timer fires an action event.
//--------------------------------------------------------------
public void actionPerformed(ActionEvent event)
{
for (Point spot : pointList)
{
spot.x += xarray[i];
spot.y += yarray[i];
if (spot.x <= 0 || spot.x >= 700)
xarray[i] = xarray[i] * -1;
if (spot.y <= 0 || spot.y >= 500)
yarray[i] = yarray[i] * -1;
repaint();
}
}
}
}
However, I struggle with arrays and I am sure that I am not doing it correctly.
I wouldn't use Arrays.
Instead, have a Ball object manage its own state. Then you can have different color, speed, size etc for each Ball. Then when the Timer fires you just calculate the new position and repaint the Ball.
Here is an example to get you started:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;
public class BallAnimation4
{
private static void createAndShowUI()
{
BallPanel panel = new BallPanel();
JFrame frame = new JFrame("BallAnimation4");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( panel );
frame.setSize(800, 600);
frame.setLocationRelativeTo( null );
//frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.setVisible( true );
panel.addBalls(5);
panel.startAnimation();
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
class BallPanel extends JPanel implements ActionListener
{
private ArrayList<Ball> balls = new ArrayList<Ball>();
public BallPanel()
{
setLayout( null );
setBackground( Color.BLACK );
}
public void addBalls(int ballCount)
{
Random random = new Random();
for (int i = 0; i < ballCount; i++)
{
Ball ball = new Ball();
ball.setRandomColor(true);
ball.setLocation(random.nextInt(getWidth()), random.nextInt(getHeight()));
ball.setMoveRate(32, 32, 1, 1, true);
// ball.setMoveRate(16, 16, 1, 1, true);
ball.setSize(32, 32);
balls.add( ball );
}
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
for (Ball ball: balls)
{
ball.draw(g);
}
}
public void startAnimation()
{
Timer timer = new Timer(75, this);
timer.start();
}
public void actionPerformed(ActionEvent e)
{
move();
repaint();
}
private void move()
{
for (Ball ball : balls)
{
ball.move(this);
}
}
class Ball
{
public Color color = Color.BLACK;
public int x = 0;
public int y = 0;
public int width = 1;
public int height = 1;
private int moveX = 1;
private int moveY = 1;
private int directionX = 1;
private int directionY = 1;
private int xScale = moveX;
private int yScale = moveY;
private boolean randomMove = false;
private boolean randomColor = false;
private Random myRand = null;
public Ball()
{
myRand = new Random();
setRandomColor(randomColor);
}
public void move(JPanel parent)
{
int iRight = parent.getSize().width;
int iBottom = parent.getSize().height;
x += 5 + (xScale * directionX);
y += 5 + (yScale * directionY);
if (x <= 0)
{
x = 0;
directionX *= (-1);
xScale = randomMove ? myRand.nextInt(moveX) : moveX;
if (randomColor) setRandomColor(randomColor);
}
if (x >= iRight - width)
{
x = iRight - width;
directionX *= (-1);
xScale = randomMove ? myRand.nextInt(moveX) : moveX;
if (randomColor) setRandomColor(randomColor);
}
if (y <= 0)
{
y = 0;
directionY *= (-1);
yScale = randomMove ? myRand.nextInt(moveY) : moveY;
if (randomColor) setRandomColor(randomColor);
}
if (y >= iBottom - height)
{
y = iBottom - height;
directionY *= (-1);
yScale = randomMove ? myRand.nextInt(moveY) : moveY;
if (randomColor) setRandomColor(randomColor);
}
}
public void draw(Graphics g)
{
g.setColor(color);
g.fillOval(x, y, width, height);
}
public void setColor(Color c)
{
color = c;
}
public void setLocation(int x, int y)
{
this.x = x;
this.y = y;
}
public void setMoveRate(int xMove, int yMove, int xDir, int yDir, boolean randMove)
{
this.moveX = xMove;
this.moveY = yMove;
directionX = xDir;
directionY = yDir;
randomMove = randMove;
}
public void setRandomColor(boolean randomColor)
{
this.randomColor = randomColor;
switch (myRand.nextInt(3))
{
case 0: color = Color.BLUE;
break;
case 1: color = Color.GREEN;
break;
case 2: color = Color.RED;
break;
default: color = Color.BLACK;
break;
}
}
public void setSize(int width, int height)
{
this.width = width;
this.height = height;
}
}
}
Since your Arrays only contain the Point you want to paint you don't have any information about the speed each point should be moved at. The best you could do is create a random amount each point should be moved each time its location is changed. This would give erratic movement as each time you move a point the distance would be random.
If you want more constant speed then you would need to create a second Array to contain the distance each point should move every time.
This starts to get messy creating a new Array every time you want a new property to be unique for the object you want to paint. That is why the approach to create a custom Object with multiple properties is easier to manage.
I created a program that makes multiple bouncing balls with random color, speed and radius. When user clicks on the screen a new random ball should appear and move around screen. But i have a multi-thread issue. When i click on the screen a ball appears and doesn't moving at all. When another click comes nothing happens.
BouncingBalls Class
public class BouncingBalls extends JPanel implements MouseListener{
private Ball ball;
protected List<Ball> balls = new ArrayList<Ball>(20);
private Container container;
private DrawCanvas canvas;
private int canvasWidth;
private int canvasHeight;
public static final int UPDATE_RATE = 30;
int x = random(480);
int y = random(480);
int speedX = random(30);
int speedY = random(30);
int radius = random(20);
int red = random(255);
int green = random(255);
int blue = random(255);
int count = 0;
public static int random(int maxRange) {
return (int) Math.round((Math.random() * maxRange));
}
public BouncingBalls(int width, int height){
canvasWidth = width;
canvasHeight = height;
ball = new Ball(x, y, speedX, speedY, radius, red, green, blue);
container = new Container();
canvas = new DrawCanvas();
this.setLayout(new BorderLayout());
this.add(canvas, BorderLayout.CENTER);
this.addMouseListener(this);
}
public void start(){
Thread t = new Thread(){
public void run(){
while(true){
update();
repaint();
try {
Thread.sleep(1000 / UPDATE_RATE);
} catch (InterruptedException e) {}
}
}
};
t.start();
}
public void update(){
ball.move(container);
}
class DrawCanvas extends JPanel{
public void paintComponent(Graphics g){
super.paintComponent(g);
container.draw(g);
ball.draw(g);
}
public Dimension getPreferredSize(){
return(new Dimension(canvasWidth, canvasHeight));
}
}
public static void main(String[] args){
javax.swing.SwingUtilities.invokeLater(new Runnable(){
public void run(){
JFrame f = new JFrame("Bouncing Balls");
f.setDefaultCloseOperation(f.EXIT_ON_CLOSE);
f.setContentPane(new BouncingBalls(500, 500));
f.pack();
f.setVisible(true);
}
});
}
#Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent e) {
count++;
balls.add(new Ball(x, y, speedX, speedY, radius, red, green, blue));
balls.get(count-1).start();
start();
}
#Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
}
Ball Class
import java.awt.Color;
import java.awt.Graphics;
public class Ball{
public static int random(int maxRange) {
return (int) Math.round((Math.random() * maxRange));
}
private BouncingBalls balls;
int x = random(480);
int y = random(480);
int speedX = random(30);
int speedY = random(30);
int radius = random(20);
int red = random(255);
int green = random(255);
int blue = random(255);
int i = 0;
public Ball(int x, int y, int speedX, int speedY, int radius, int red, int green, int blue){
this.x = x;
this.y = y;
this.speedX = speedX;
this.speedY = speedY;
this.radius = radius;
this.red = red;
this.green = green;
this.blue = blue;
}
public void draw(Graphics g){
for(Ball ball : balls){
g.setColor(new Color(red, green, blue));
g.fillOval((int)(x - radius), (int)(y - radius), (int)(2 * radius), (int)(2 * radius));
}
}
public void move(Container container){
x += speedX;
y += speedY;
if(x - radius < 0){
speedX = -speedX;
x = radius;
}
else if(x + radius > 500){
speedX = -speedX;
x = 500 - radius;
}
if(y - radius < 0){
speedY = -speedY;
y = radius;
}
else if(y + radius > 500){
speedY = -speedY;
y = 500 - radius;
}
}
}
Container Class
import java.awt.Color;
import java.awt.Graphics;
public class Container {
private static final int HEIGHT = 500;
private static final int WIDTH = 500;
private static final Color COLOR = Color.WHITE;
public void draw(Graphics g){
g.setColor(COLOR);
g.fillRect(0, 0, WIDTH, HEIGHT);
}
}
You're maintaing two different references to your ball.
You have a reference to a single Ball called ball and a List of balls. Your update and paint methods only reference the single ball
Ball doesn't seem to have a start method (that I can see) so this balls.get(count-1).start(); doesn't make sense...
Updated
You don't need the reference to ball
While not a bad idea, while testing, you should probably call start in the constructor
Your update method in BouncingBalls should looping through the balls list, calling move on each ball in the list...
The paintComponent method of DrawCanvas needs access to and should make use of the balls list. This might be better achievable through a model interface
Do not construct a new Ball with parameters, as it's giving each ball the same properties, especially when you assign random values to it when you construct it any way...
Ball doesn't have (or need) a start method
public class BouncingBalls extends JPanel implements MouseListener {
// private Ball ball;
protected List<Ball> balls = new ArrayList<Ball>(20);
private Container container;
private DrawCanvas canvas;
private int canvasWidth;
private int canvasHeight;
public static final int UPDATE_RATE = 30;
int x = random(480);
int y = random(480);
int speedX = random(30);
int speedY = random(30);
int radius = random(20);
int red = random(255);
int green = random(255);
int blue = random(255);
int count = 0;
public static int random(int maxRange) {
return (int) Math.round((Math.random() * maxRange));
}
public BouncingBalls(int width, int height) {
canvasWidth = width;
canvasHeight = height;
// ball = new Ball(x, y, speedX, speedY, radius, red, green, blue);
container = new Container();
canvas = new DrawCanvas();
this.setLayout(new BorderLayout());
this.add(canvas, BorderLayout.CENTER);
this.addMouseListener(this);
start();
}
public void start() {
Thread t = new Thread() {
public void run() {
while (true) {
update();
repaint();
try {
Thread.sleep(1000 / UPDATE_RATE);
} catch (InterruptedException e) {
}
}
}
};
t.start();
}
public void update() {
for (Ball ball : balls) {
ball.move(container);
}
}
class DrawCanvas extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
container.draw(g);
for (Ball ball : balls) {
ball.draw(g);
}
// ball.draw(g);
}
public Dimension getPreferredSize() {
return (new Dimension(canvasWidth, canvasHeight));
}
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame f = new JFrame("Bouncing Balls");
f.setDefaultCloseOperation(f.EXIT_ON_CLOSE);
f.setContentPane(new BouncingBalls(500, 500));
f.pack();
f.setVisible(true);
}
});
}
#Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent e) {
count++;
balls.add(new Ball());
// balls.add(new Ball(x, y, speedX, speedY, radius, red, green, blue));
// balls.get(count - 1).start();
// start();
}
#Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
public static class Ball {
public int random(int maxRange) {
return (int) Math.round(Math.random() * maxRange);
}
int x = random(480);
int y = random(480);
int speedX = random(30);
int speedY = random(30);
int radius = random(20);
int red = random(255);
int green = random(255);
int blue = random(255);
int i = 0;
public Ball() { //int x, int y, int speedX, int speedY, int radius, int red, int green, int blue) {
// this.x = x;
// this.y = y;
// this.speedX = speedX;
// this.speedY = speedY;
// this.radius = radius;
// this.red = red;
// this.green = green;
// this.blue = blue;
}
public void draw(Graphics g) {
g.setColor(new Color(red, green, blue));
g.fillOval((int) (x - radius), (int) (y - radius), (int) (2 * radius), (int) (2 * radius));
}
public void move(Container container) {
x += speedX;
y += speedY;
if (x - radius < 0) {
speedX = -speedX;
x = radius;
} else if (x + radius > 500) {
speedX = -speedX;
x = 500 - radius;
}
if (y - radius < 0) {
speedY = -speedY;
y = radius;
} else if (y + radius > 500) {
speedY = -speedY;
y = 500 - radius;
}
}
}
public static class Container {
private static final int HEIGHT = 500;
private static final int WIDTH = 500;
private static final Color COLOR = Color.WHITE;
public void draw(Graphics g) {
g.setColor(COLOR);
g.fillRect(0, 0, WIDTH, HEIGHT);
}
}
}
Updated
As pointed out by the commentators, ArrayList is not thread safe, it's not a good idea to have multiple threads trying to access it simultaneously. While adding is slightly safer then removing, it is still bad practice.
You can either replace ArrayList with Vector, which would be the simpler solution, or synchronize the access to the list around a common monitor lock. Given your example, I'd use a java.util.Vector
You Can try this alternate Java Programm for bouncing 10 multi-colored balls on a single "START" button.....
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;
import javaimage.io.*;
class Thr extends Thread
{
boolean up=false;
Ballbounce parent;
int top,left;
Color c;
Thr(int t,int l,Color cr,ex5 p)
{
top=l;
if(top > 170)
top=170-t/8;
left=t;
c=cr;
parent=p;
}
public void run()
{
try
{
while(true)
{
Thread.sleep(37);
if(top >= 188)
up=true;
if(top <= 0)
up=false;
if(!up)
top=top+2;
else
top=top-2;
parent.p.repaint();
}
}catch(Exception e){}
}
}
class Ballbounce extends JFrame implements ActionListener
{
int top=0,left=0,n=0,radius=50;
Color C[]={Color.black,Color.cyan,Color.orange,Color.red,Color.yellow,Color.pink,Color.gray,Color.blue,Color.green,Color.magenta};
Thr t[]=new Thr[10];
GPanel p;
JButton b;
Panel p1;
Ballbounce()
{
setSize(700,300);
setVisible(true);
setLayout( new BorderLayout());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(p=new GPanel(this),BorderLayout.CENTER);
b= new JButton("Start");
b.addActionListener(this);
add(p1=new Panel(),BorderLayout.SOUTH);
p1.setBackground(Color.lightGray);
p1.add(b);
}
public static void main(String args[])
{
new Ballbounce();
}
public void actionPerformed(ActionEvent e)
{
t[n]=new Thr(left+(radius+13)*n+29,top+n*25,C[n],this);
t[n].start();
n++;
p.repaint();
if(n >9)
b.setEnabled(false);
}
}
class GPanel extends JPanel
{
Ballbounce parent;
GPanel(Ballbounce p)
{
parent=p;
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
setBackground(Color.white);
for(int i=0;i< parent.n;i++)
{
g.setColor(parent.t[i].c);
g.fillOval(parent.t[i].left,parent.t[i].top,parent.radius,parent.radius);
}
}
}
I hope you will like it....
If u are unable to understand the code... You can question it anytime...... :)
Enjoy the code... :)
I want to do a bouncing balls application in java. Each ball should take place by mouse clicking and each of them should have random speed, color, radius and starting position. I managed to do everything except the part where mouse listener takes place. Whatever i do in the mousePressed method didn't work. What should i do to make user create a random ball when he presses the mouse?
EDIT: This is the last version of my code. Now the problem is that i can't create more than one ball. When i click on the screen same ball is just keeps speeding.
BouncingBalls Class
public class BouncingBalls extends JPanel implements MouseListener{
private Ball ball;
protected List<Ball> balls = new ArrayList<Ball>(20);
private Container container;
private DrawCanvas canvas;
private int canvasWidth;
private int canvasHeight;
public static final int UPDATE_RATE = 30;
int x = random(480);
int y = random(480);
int speedX = random(30);
int speedY = random(30);
int radius = random(20);
int red = random(255);
int green = random(255);
int blue = random(255);
int count = 0;
public static int random(int maxRange) {
return (int) Math.round((Math.random() * maxRange));
}
public BouncingBalls(int width, int height){
canvasWidth = width;
canvasHeight = height;
ball = new Ball(x, y, speedX, speedY, radius, red, green, blue);
container = new Container();
canvas = new DrawCanvas();
this.setLayout(new BorderLayout());
this.add(canvas, BorderLayout.CENTER);
this.addMouseListener(this);
}
public void start(){
Thread t = new Thread(){
public void run(){
while(true){
update();
repaint();
try {
Thread.sleep(1000 / UPDATE_RATE);
} catch (InterruptedException e) {}
}
}
};
t.start();
}
public void update(){
ball.move(container);
}
class DrawCanvas extends JPanel{
public void paintComponent(Graphics g){
super.paintComponent(g);
container.draw(g);
ball.draw(g);
}
public Dimension getPreferredSize(){
return(new Dimension(canvasWidth, canvasHeight));
}
}
public static void main(String[] args){
javax.swing.SwingUtilities.invokeLater(new Runnable(){
public void run(){
JFrame f = new JFrame("Bouncing Balls");
f.setDefaultCloseOperation(f.EXIT_ON_CLOSE);
f.setContentPane(new BouncingBalls(500, 500));
f.pack();
f.setVisible(true);
}
});
}
#Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent e) {
balls.add(new Ball(x, y, speedX, speedY, radius, red, green, blue));
start();
}
#Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
}
Ball Class
import java.awt.Color;
import java.awt.Graphics;
public class Ball{
public static int random(int maxRange) {
return (int) Math.round((Math.random() * maxRange));
}
private BouncingBalls balls;
int x = random(480);
int y = random(480);
int speedX = random(30);
int speedY = random(30);
int radius = random(20);
int red = random(255);
int green = random(255);
int blue = random(255);
int i = 0;
public Ball(int x, int y, int speedX, int speedY, int radius, int red, int green, int blue){
this.x = x;
this.y = y;
this.speedX = speedX;
this.speedY = speedY;
this.radius = radius;
this.red = red;
this.green = green;
this.blue = blue;
}
public void draw(Graphics g){
for(Ball ball : balls){
g.setColor(new Color(red, green, blue));
g.fillOval((int)(x - radius), (int)(y - radius), (int)(2 * radius), (int)(2 * radius));
}
}
public void move(Container container){
x += speedX;
y += speedY;
if(x - radius < 0){
speedX = -speedX;
x = radius;
}
else if(x + radius > 500){
speedX = -speedX;
x = 500 - radius;
}
if(y - radius < 0){
speedY = -speedY;
y = radius;
}
else if(y + radius > 500){
speedY = -speedY;
y = 500 - radius;
}
}
}
Container Class
import java.awt.Color;
import java.awt.Graphics;
public class Container {
private static final int HEIGHT = 500;
private static final int WIDTH = 500;
private static final Color COLOR = Color.WHITE;
public void draw(Graphics g){
g.setColor(COLOR);
g.fillRect(0, 0, WIDTH, HEIGHT);
}
}
ERROR: I get "Can only iterate over an array or an instance of java.lang.Iterable" error in this part of code:
public void draw(Graphics g){
for(Ball ball : balls){
g.setColor(new Color(red, green, blue));
g.fillOval((int)(x - radius), (int)(y - radius), (int)(2 * radius), (int)(2 * radius));
}
}
First, if you want to render more than one ball, you should create another class to contain all the properties needed to draw a ball (maybe even a draw (Graphics g) method to delegate the drawing). Then you would have a list of balls on your BouncingBalls class which should be iterated over and painted on the paintComponent method.
Said that, your mouseClicked handler would just create a new Ball instance and add it to the list.
EDIT:
Example of how the drawing process would be on your DrawCanvas class:
class DrawCanvas {
public void paintComponent(Graphics g){
super.paintComponent(g);
container.draw(g);
for (Ball ball : balls)
//the draw method should only care of the specific ball instance
//you are calling it from
ball.draw(g);
}
...
I think you are having problems separating your problem into classes and making their instances cooperate to do what you want. If you are indeed having doubts about this, I recommend you read some articles/books about the topic to get a better idea of the concepts of a class and an object and how they work; it'll definitely help you do your programming with ease.
You need to add the MouseListener to the component:
public BouncingBalls() {
this.addMouseListener(this); // <-- Add this object as a MouseListener.
this.setPreferredSize(new Dimension(BOX_WIDTH, BOX_HEIGHT));
Try using this code in your main method:
frame.addMouseListener(this);
You need to add the mouse listener to the frame/panel.
(response to this comment by you) Alternatively, if you want to add the listener to the panel, first you must call
setFocusable(true);
requestFocusInWindow();
In your BouncingBalls constructor. Then you can add the mouse listener to the panel with:
addMouseListener(this);
This is because the panel does not initially have focus.
The easiest way to do it, though, is to just add the mouse listener to the frame.
I am trying to create a simple animation which draws random rectangles when a button is pressed. So far I managed to create rectangle on the press of a button. I want to further develop the code so that when I press the button, more than multiple random rectangles are created. I tried to create a for loop which asks the inner class to repaint itself but it still didn't work. can anyone help me please.
public class TwoButtonsRandomRec {
JFrame frame;
private int width = 500;
private int height = 500;
private DrawPanel dp;
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public static void main (String[] args)
{
TwoButtonsRandomRec test = new TwoButtonsRandomRec();
test.go();
}
public void go()
{
dp = new DrawPanel();
JButton start = new JButton("Start");
start.addActionListener(new startListener());
JButton stop = new JButton("Stop");
stop.addActionListener(new stopListener());
frame = new JFrame();
frame.setSize(getWidth(), getHeight());
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(BorderLayout.NORTH, start);
frame.getContentPane().add(BorderLayout.SOUTH, stop);
}
class startListener implements ActionListener{
public void actionPerformed(ActionEvent event){
frame.getContentPane().add(BorderLayout.CENTER, dp);
frame.repaint();
frame.getRootPane().revalidate();
for(int i=0; i<10; i++){
dp.repaint();
}
}
}
class stopListener implements ActionListener{
public void actionPerformed(ActionEvent event){
System.out.println("stop");
}
}
class DrawPanel extends JPanel{
public void paintComponent(Graphics g){
int w = 5+(int)(Math.random() * width-5);
int h = 5+(int)(Math.random() * height-5);
int maxX = width-w; // diffX & diffY are used to ensure that rectangle is
int maxY = width-h; // draw completely inside the window
int x = (int)(Math.random() * maxX);
int y = (int)(Math.random() * maxY);
Color color = new Color((int) (Math.random()*256), // random red
(int) (Math.random()*256), // random green
(int) (Math.random()*256));// random blue
g.setColor(color);
g.fillRect(x,y,w,h);
}
}
}
repaint() simply tells Swing "when you'll have time, please repaint this area". So if you add rectangles in a loop and call repaint at each iteration, all the rectangles will only appear after the loop has finished, and the action event has been handled.
To have an animation, you need to loop in a separate thread. The easiest way to do that is to use a Swing Timer. When the Start button is started, start a timer which adds a random rectangle and calls repaint() every X milliseconds. When the Stop button is pressed, stop the timer.
What you should do is to put the loop inside paintComponent method and not call repaint in the loop.
So your paintComponent method should look like this:
public void paintComponent(Graphics g){
for (int i = 0; i < 10; i++) {
int w = 5+(int)(Math.random() * width-5);
int h = 5+(int)(Math.random() * height-5);
int maxX = width-w; // diffX & diffY are used to ensure that rectangle is
int maxY = width-h; // draw completely inside the window
int x = (int)(Math.random() * maxX);
int y = (int)(Math.random() * maxY);
Color color = new Color((int) (Math.random()*256), // random red
(int) (Math.random()*256), // random green
(int) (Math.random()*256));// random blue
g.setColor(color);
g.fillRect(x,y,w,h);
}
}
And your action performed should look like this:
public void actionPerformed(ActionEvent event){
frame.getContentPane().add(BorderLayout.CENTER, dp);
frame.repaint();
frame.getRootPane().revalidate();
dp.repaint();
}
Well,here I have done a short EG for you.It displays random rectangles, random times on random screen location.
(You can set your own value of randomization, and the screen location max bound,as per your requirements.)
And also note
int i=(int)(Math.random()*10);
int j=(int)(Math.random()*10);
for(;i<j;i++)
Where at times i may be > than j.So,loop may not work on one or two cliks.Change as per your need.
Here is the working code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class SimpleStamper extends JApplet {
public void init() {
Display display = new Display();
setContentPane(display);
}
class Display extends JPanel implements MouseListener {
Display() {
setBackground(Color.black);
addMouseListener(this);
}
public void mousePressed(MouseEvent evt) {
if ( evt.isShiftDown() ) {
repaint();
return;
}
int x = evt.getX();
int y = evt.getY();
Graphics g = getGraphics();
//***MODIFY THE FOLLOWING LINES****//
int i=(int)(Math.random()*10);
int j=(int)(Math.random()*10);
for(;i<j;i++)
{ g.setColor(Color.red);
x=(int)(Math.random()*100);
y=(int)(Math.random()*100);
g.fillRect( x , y , 60, 30 );
g.setColor(Color.black);
g.drawRect(x , y , 60, 30 );}
g.dispose();
}
public void mouseEntered(MouseEvent evt) { }
public void mouseExited(MouseEvent evt) { }
public void mouseClicked(MouseEvent evt) { }
public void mouseReleased(MouseEvent evt) { }
}
}
I am having a problem with a game that I am trying to make in Java. I am trying to attach a MouseListener to my canvas, however, when I click on the canvas, nothing happens. I think I may be attaching the MouseListener to the wrong thing, but I don't know what to attach it to. I have tried attaching it to the JFrame and the canvas. Here is my code:
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.io.*;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.util.Random;
public class Gravity extends Canvas {
public static final int screenw = 1024;
public static final int screenh = 768;
public static Random gen = new Random();
public static Boolean started = false;
public static int[] starx = new int[100];
public static int[] stary = new int[100];
public static Color[] starc = new Color[100];
public static JFrame frame;
public static Gravity canvas;
public static Image buffer;
public static Graphics bg;
public static int[] xh = new int[1000];
public static int[] yh = new int[1000];
public static int i = 0;
public static Image title;
public static ArrayList<Integer> ptx = new ArrayList<Integer>();
public static ArrayList<Integer> pty = new ArrayList<Integer>();
double x = 100;
double y = 100;
public Gravity(){
}
public void paint (Graphics g) {
frame.addMouseListener(new MouseListener(){
public void mouseClicked(MouseEvent e){
started = true;
System.out.println("Mouse was clicked");
}
public void mouseEntered(MouseEvent arg0) {}
public void mouseExited(MouseEvent arg0) {}
public void mousePressed(MouseEvent arg0) {}
public void mouseReleased(MouseEvent arg0) {}
});
buffer = createImage(screenw, screenh);
bg = buffer.getGraphics();
int w = getWidth();
int h = getHeight();
double px = getWidth()/2;
double py = getHeight()/2;
bg.setColor(Color.BLACK);
bg.fillRect(0, 0, w, h); //black background
for (int j=0; j < 100; j++){ //make stars
starx[j] = gen.nextInt(w);
stary[j] = gen.nextInt(h);
starc[j] = new Color(gen.nextInt(100)+156, gen.nextInt(100)+156, gen.nextInt(100)+156);
bg.setColor(starc[j]);
bg.drawLine(starx[j], stary[j], starx[j]+2, stary[j]+2);
bg.drawLine(starx[j], stary[j]+2, starx[j]+2, stary[j]);
}
try {
title = ImageIO.read(new ByteArrayInputStream(Base64.decode(""))); //I have omitted the Base64 code for the image for my title screen
} catch (IOException e) {
e.printStackTrace();
}
bg.drawImage(title, 100, 100, null);
g.drawImage(buffer, 0, 0, null);
while (!started){
try {
Thread.sleep(50);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
double xvel = -15;
double yvel = 10;
for (int j=0; j < 100; j++){ //store stars
starx[j] = gen.nextInt(w);
stary[j] = gen.nextInt(h);
starc[j] = new Color(gen.nextInt(100)+156, gen.nextInt(100)+156, gen.nextInt(100)+156);
}
Image test = createImage(200,200);
Graphics testg = test.getGraphics();
testg.drawLine(50,50,150,150);
while(true){
g.drawImage(buffer, 0,0, null);
try {
Thread.sleep(33);
} catch (Exception e) {
e.printStackTrace();
}
bg.setColor(Color.BLACK);
bg.fillRect(0, 0, w, h); //black background
for (int j=0; j < 100; j++){ //draw stars
bg.setColor(starc[j]);
bg.drawLine(starx[j], stary[j], starx[j]+2, stary[j]+2);
bg.drawLine(starx[j], stary[j]+2, starx[j]+2, stary[j]);
}
bg.setColor(Color.BLUE);
if (i > 0){
for (int z=0; z < i-1; z++){
bg.drawLine(ptx.get(z), pty.get(z), ptx.get(z+1), pty.get(z+1));
}
}
bg.setColor(Color.CYAN);
bg.fillOval((int)px, (int)py, 25, 25); //planet
bg.setColor(Color.RED);
bg.fillRect((int)(x-5),(int)(y-5),10,10); //ship
double fg = (5*50000)/(Math.pow(dist(x,y,px,py),2));
double m = (y-py)/(x-px);
double ms = Math.sqrt(Math.abs(m));
if (m < 0) ms = -ms;
double xchg = fg;
double ychg = fg*ms;
if (x > px){
xchg = -xchg;
ychg = -ychg;
}
xvel += xchg;
yvel += ychg;
x += xvel;
y += yvel;
ptx.add((int)x);
pty.add((int)y);
i++;
}
}
public static void main(String[] args){
canvas = new Gravity();
frame = new JFrame();
frame.setSize(screenw, screenh);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(canvas);
frame.setVisible(true);
}
public static double dist(double x1, double y1, double x2, double y2){
double x = x2-x1;
double y = y2-y1;
return Math.sqrt((x*x)+(y*y));
}
}
General tips, in point form:
Don't mix AWT (e.g. Canvas) with Swing (e.g. JFrame) components. Instead of the Canvas, use a JPanel and override paintComponent(Graphics) rather than paint(Graphics).
Don't call Thread.sleep(n) on the EDT. Instead use a Swing based Timer to call repaint()
Don't perform long running operations on the paint method, especially starting an infinite loop! Even the creation of the buffer image should only be done in the case the screen size changes. (left as 'TODO' - BNI)
Add the MouseListener once in the constructor or an init() method rather than every time paint is called.
Highly recommend all of Nate's numbered list of notes.
Try this code, and compare it carefully to the original to see the changes.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Dimension;
import java.awt.event.*;
import java.util.ArrayList;
import java.io.*;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.util.Random;
public class Gravity extends JPanel {
public static final int screenw = 800;
public static final int screenh = 600;
public static Random gen = new Random();
public static int[] starx = new int[100];
public static int[] stary = new int[100];
public static Color[] starc = new Color[100];
public static Image buffer;
public static Graphics bg;
public static int[] xh = new int[1000];
public static int[] yh = new int[1000];
public static int i = 0;
public static ArrayList<Integer> ptx = new ArrayList<Integer>();
public static ArrayList<Integer> pty = new ArrayList<Integer>();
double x = 100;
double y = 100;
Timer timer;
public Gravity(){
// set thre PREFERRED size!
setPreferredSize(new Dimension(screenw, screenh));
addMouseListener(new MouseListener(){
public void mouseClicked(MouseEvent e){
System.out.println("Mouse was clicked");
timer.start();
}
public void mouseEntered(MouseEvent arg0) {}
public void mouseExited(MouseEvent arg0) {}
public void mousePressed(MouseEvent arg0) {}
public void mouseReleased(MouseEvent arg0) {}
});
ActionListener animation = new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
repaint();
}
};
timer = new Timer(50, animation);
}
#Override
public void paintComponent(Graphics g) {
buffer = createImage(screenw, screenh);
bg = buffer.getGraphics();
int w = getWidth();
int h = getHeight();
double px = getWidth()/2;
double py = getHeight()/2;
bg.setColor(Color.BLACK);
bg.fillRect(0, 0, w, h); //black background
for (int j=0; j < 100; j++){ //make stars
starx[j] = gen.nextInt(w);
stary[j] = gen.nextInt(h);
starc[j] = new Color(gen.nextInt(100)+156, gen.nextInt(100)+156, gen.nextInt(100)+156);
bg.setColor(starc[j]);
bg.drawLine(starx[j], stary[j], starx[j]+2, stary[j]+2);
bg.drawLine(starx[j], stary[j]+2, starx[j]+2, stary[j]);
}
g.drawImage(buffer, 0, 0, null);
double xvel = -15;
double yvel = 10;
for (int j=0; j < 100; j++){ //store stars
starx[j] = gen.nextInt(w);
stary[j] = gen.nextInt(h);
starc[j] = new Color(gen.nextInt(100)+156, gen.nextInt(100)+156, gen.nextInt(100)+156);
}
Image test = createImage(200,200);
Graphics testg = test.getGraphics();
testg.drawLine(50,50,150,150);
g.drawImage(buffer, 0,0, null);
try {
Thread.sleep(33);
} catch (Exception e) {
e.printStackTrace();
}
bg.setColor(Color.BLACK);
bg.fillRect(0, 0, w, h); //black background
for (int j=0; j < 100; j++){ //draw stars
bg.setColor(starc[j]);
bg.drawLine(starx[j], stary[j], starx[j]+2, stary[j]+2);
bg.drawLine(starx[j], stary[j]+2, starx[j]+2, stary[j]);
}
bg.setColor(Color.BLUE);
if (i > 0){
for (int z=0; z < i-1; z++){
bg.drawLine(ptx.get(z), pty.get(z), ptx.get(z+1), pty.get(z+1));
}
}
bg.setColor(Color.CYAN);
bg.fillOval((int)px, (int)py, 25, 25); //planet
bg.setColor(Color.RED);
bg.fillRect((int)(x-5),(int)(y-5),10,10); //ship
double fg = (5*50000)/(Math.pow(dist(x,y,px,py),2));
double m = (y-py)/(x-px);
double ms = Math.sqrt(Math.abs(m));
if (m < 0) ms = -ms;
double xchg = fg;
double ychg = fg*ms;
if (x > px){
xchg = -xchg;
ychg = -ychg;
}
xvel += xchg;
yvel += ychg;
x += xvel;
y += yvel;
ptx.add((int)x);
pty.add((int)y);
i++;
}
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new Gravity());
frame.setResizable(false);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
});
}
public static double dist(double x1, double y1, double x2, double y2){
double x = x2-x1;
double y = y2-y1;
return Math.sqrt((x*x)+(y*y));
}
}
Use a JComponent instead of a Canvas. You'll want to add the mouse listener to that object. You'll also need to set up the mouse listener in the constructor, not the paint() method.
Edit: You're doing to much in the paint() method as #AndrewThompson pointed out.
public Gravity() {
addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("Mouse was clicked");
}
#Override
public void mouseEntered(MouseEvent arg0) {
}
#Override
public void mouseExited(MouseEvent arg0) {
}
#Override
public void mousePressed(MouseEvent arg0) {
}
#Override
public void mouseReleased(MouseEvent arg0) {
}
});
}
#Override
public void paint(Graphics g) {
buffer = createImage(screenw, screenh);
bg = buffer.getGraphics();
...
bg.setColor(Color.BLACK);
bg.fillRect(0, 0, w, h); // black background
for (int j = 0; j < 100; j++) { // make stars
...
}
bg.drawImage(title, 100, 100, null);
g.drawImage(buffer, 0, 0, null);
double xvel = -15;
double yvel = 10;
for (int j = 0; j < 100; j++) { // store stars
...
}
Image test = createImage(200, 200);
Graphics testg = test.getGraphics();
testg.drawLine(50, 50, 150, 150);
g.drawImage(buffer, 0, 0, null);
bg.setColor(Color.BLACK);
bg.fillRect(0, 0, w, h); // black background
for (int j = 0; j < 100; j++) { // draw stars
...
}
bg.setColor(Color.BLUE);
if (i > 0) {
for (int z = 0; z < i - 1; z++) {
bg.drawLine(ptx.get(z), pty.get(z), ptx.get(z + 1), pty.get(z + 1));
}
}
bg.setColor(Color.CYAN);
bg.fillOval((int) px, (int) py, 25, 25); // planet
bg.setColor(Color.RED);
bg.fillRect((int) (x - 5), (int) (y - 5), 10, 10); // ship
....
ptx.add((int) x);
pty.add((int) y);
}
public static void main(String[] args) {
...
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
...
}
Notes about your code:
Avoid making fields public static. 99.9% of the time this is not necessary, and usually leads to trouble later.
The painting code seems to be unnecessarily using buffers--the test Image is not used at all currently!
I removed the while(true) loop, but noticed you're drawing an image, modifying the image, and then drawing the image again. You can probably do this in one go.
You should be able to avoid using an Image buffer altogether since you're creating a new one each time paint() is called, and clearing it at the beginning. Just draw your graphics directly to g.
Avoid doing I/O from within the paint() method. Load your images during construction or in a background thread.
Your paint() method is tying up the Thread that handles the mouse click event, due to the while (!started) loop that never exits. started is never true because the MouseListener's mouseClicked() is never called because the paint() method is waiting for started to be true! If you remove that loop, the while (true) loop will have a similar effect. Any mouse events that happen while paint() is running will be queued until paint() returns.