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... :)
Related
I'm trying to create Circles in JFrame using JComponent. Here's what I'm trying to achieve:
And here's what's happening with my code:
I have no idea what's causing this problem. Here's my code:
CircleViewer.java
import javax.swing.JFrame;
import java.awt.event.*;
public class CircleViewer
{
public static void main(String[] args)
{
final CirclePanel panel = new CirclePanel();
class MousePressListener implements MouseListener, MouseMotionListener
{
public void mouseClicked(MouseEvent event) { }
public void mouseEntered(MouseEvent event) { }
public void mouseExited(MouseEvent event) { }
public void mouseWheelMoved(MouseWheelEvent event) { }
public void mouseMoved(MouseEvent event) { }
public void mousePressed(MouseEvent event)
{
var x = event.getX();
var y = event.getY();
panel.addCircle(x, y);
}
public void mouseDragged(MouseEvent event)
{
var x = event.getX();
var y = event.getY();
panel.moveTo(x, y);
}
public void mouseReleased(MouseEvent event)
{
panel.finalMove();
}
}
MousePressListener listener = new MousePressListener();
panel.addMouseListener(listener);
panel.addMouseMotionListener(listener);
JFrame frame = new JFrame("Circle Shapes");
frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel);
frame.setVisible(true);
}
private static final int FRAME_WIDTH = 700;
private static final int FRAME_HEIGHT = 500;
}
CirclePanel.java
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JComponent;
import java.util.ArrayList;
import java.awt.Color;
import java.lang.Math;
import java.awt.BasicStroke;
import java.awt.Stroke;
public class CirclePanel extends JComponent
{
private int lineX;
private int lineY;
private boolean isDraged;
private ArrayList<Circle> circleList;
private BasicStroke dashLine;
public CirclePanel()
{
this.circleList = new ArrayList<Circle>();
this.isDraged = false;
this.lineX = 0;
this.lineY = 0;
this.dashLine = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new float[]{6}, 0);
}
public void addCircle(int x, int y)
{
lineX = x;
lineY = y;
isDraged = true;
circleList.add(new Circle(x, y, 0, Color.RED));
repaint();
}
public void moveTo(int x, int y)
{
var circleTemp = circleList.get(circleList.size() - 1);
isDraged = true;
var tempR = (int)Math.sqrt(Math.pow(x - circleTemp.get(0), 2) + Math.pow(y - circleTemp.get(1), 2));
System.out.println(tempR);
var tempX = circleTemp.get(0) - (tempR / 2);
var tempY = circleTemp.get(1) - (tempR / 2);
circleList.get(circleList.size() - 1).setCords(tempX, tempY, tempR);
lineX = x;
lineY = y;
repaint();
}
public void finalMove()
{
isDraged = false;
circleList.get(circleList.size() - 1).setColor(Color.BLUE);
repaint();
}
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
Stroke defaultStroke;
defaultStroke = g2.getStroke();
if (!circleList.isEmpty())
{
if (isDraged)
{
g2.setColor(Color.RED);
g2.setStroke(dashLine);
g2.drawLine(circleList.get(circleList.size() - 1).get(0), circleList.get(circleList.size() - 1).get(1), lineX, lineY);
}
for (Circle circle : circleList)
{
g2.setStroke(defaultStroke);
circle.draw(g2);
isDraged = false; //this is prob reduntant
}
}
}
}
And finally, Circle.java
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
public class Circle
{
private int x;
private int y;
private int radius;
private Color color;
public Circle(int x, int y, int radius, Color color)
{
this.x = x;
this.y = y;
this.radius = radius;
this.color = color;
}
public int get(int option)
{
switch (option)
{
case 0:
return this.x;
case 1:
return this.y;
case 2:
return this.radius;
}
return 0;
}
public Color get()
{
return this.color;
}
public void setCords(int x, int y, int r)
{
this.x = x;
this.y = y;
this.radius = r;
}
public void set(int option, int value)
{
switch (option)
{
case 0: //set x
this.x = value;
break;
case 1:
this.y = value;
break;
case 2:
radius = value;
break;
}
}
public void setColor(Color color)
{
this.color = color;
}
public void draw(Graphics2D g2)
{
g2.setColor(color);
g2.draw(new Ellipse2D.Double(x, y, radius, radius));
}
}
Rather than finding the specific problem in you code, I just decided to make a cleaner implementation. Using this circle class
class Circle {
final int x;
final int y;
int radius;
public Circle(int x, int y, int radius) {
this.x = x;
this.y = y;
this.radius = radius;
}
}
You can implement CirclePanel like this:
public class CirclePanel extends JPanel {
private static final Stroke DASHED = new BasicStroke(1,
BasicStroke.CAP_BUTT,
BasicStroke.JOIN_BEVEL,
0, new float[]{6}, 0);
public Color oldCircleColor = Color.BLUE;
public Color newCircleColor = Color.RED;
public Color backgroundColor = Color.LIGHT_GRAY;
private final List<Circle> oldCircles = new ArrayList<>();
private Circle newCircle = null;
private int mouseX = 0;
private int mouseY = 0;
private class MouseHelper implements MouseListener, MouseMotionListener {
#Override public void mouseMoved(MouseEvent e) {}
#Override public void mouseClicked(MouseEvent e) {}
#Override public void mousePressed(MouseEvent e) {}
#Override public void mouseEntered(MouseEvent e) {}
#Override public void mouseExited(MouseEvent e) {}
#Override
public void mouseDragged(MouseEvent e) {
mouseX = e.getX();
mouseY = e.getY();
if (newCircle == null) {
newCircle = new Circle(mouseX, mouseY, 0);
} else {
int dX = newCircle.x - mouseX;
int dY = newCircle.y - mouseY;
newCircle.radius = (int) Math.sqrt(dX*dX + dY*dY);
}
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
if (newCircle != null) {
oldCircles.add(newCircle);
newCircle = null;
repaint();
}
}
}
public CirclePanel() {
MouseHelper helper = new MouseHelper();
addMouseListener(helper);
addMouseMotionListener(helper);
setPreferredSize(new Dimension(400, 400));
}
#Override
public void paint(Graphics g) {
g.setColor(backgroundColor);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(oldCircleColor);
for (Circle c : oldCircles) {
drawCircle(g, c);
}
Circle c = newCircle;
if (c != null) {
g.setColor(newCircleColor);
drawCircle(g, c);
Graphics2D g2 = (Graphics2D) g.create();
g2.setStroke(DASHED);
g2.drawLine(c.x, c.y, mouseX, mouseY);
g2.dispose();
}
}
private void drawCircle(Graphics g, Circle c) {
// note: drawOval takes top-left corner and diameter, NOT center and radius
g.drawOval(c.x - c.radius, c.y - c.radius, c.radius * 2, c.radius * 2);
}
}
And test to see that it works
public static void main(String[] args) {
JFrame frame = new JFrame();
CirclePanel panel = new CirclePanel();
frame.add(panel);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
Like #SirLenz0rlot mentioned in the comments there is a bug in your moveTo method. The problem is the call circleList.get(circleList.size() - 1).setCords(tempX, tempY, tempR); which is setting the position of the circle to tempX, tempY, but you probably only want to set the radius of the circle, while the coords are not changed.
Changing the moveTo method to the code below should help:
public void moveTo(int x, int y)
{
var circleTemp = circleList.get(circleList.size() - 1);
isDraged = true;
var tempR = (int)Math.sqrt(Math.pow(x - circleTemp.get(0), 2) + Math.pow(y - circleTemp.get(1), 2));
System.out.println(tempR);
var tempX = circleTemp.get(0) - (tempR / 2);
var tempY = circleTemp.get(1) - (tempR / 2);
//EDITED HERE
circleList.get(circleList.size() - 1).setCords(circleTemp.get(0), circleTemp.get(1), tempR);//using getX() and getY() on your circle would be easier to understand here, but this should also work...
lineX = x;
lineY = y;
repaint();
}
For some reason your circleTemp.get(0) and (1) doesn't give you the (x,y) coordinates
circleTemp.get(0)
What you should do is save the circle's (x,y) coordinates, and use them in the moveTo method.
Correct Change
And in the end for the draw line, you should again, use the saved (x,y) coordinates and the new coordinated of the mouse. (not the circleList.size() -1).get(0) - Line 67)
(Ex. I added the drawLineX and drawLineY)
The program is supposed to make a grid where all rectangles start black and if clicked or dragged they turn white. So far my program is able to make the rectangle you click or drag white, but it doesn't memorize or remember which one I clicked to make them stay white until you click them again. Maybe I would call the change using the mouse listener or maybe by creating an LinkedList , I just can't figure out the easiest solution.
public class Clicky extends JFrame {
private static class Board extends JPanel {
private double BRICK_WIDTH = 20;
private double BRICK_HEIGHT = 20;
public Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
public double width = screenSize.getWidth();
public double height = screenSize.getHeight();
private int COLS = (int)(width/BRICK_WIDTH);
private int ROWS =(int)(height/BRICK_HEIGHT);
private Color CO = Color.BLACK;
public Board() {
System.out.println("WIdth:" + COLS + "Height:" + ROWS);
setBackground(Color.BLACK);
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
mx = e.getX();
my = e.getY();
System.out.printf("X: %d Y: %d ", mx, my);
repaint();
}
});
addMouseMotionListener(new MouseMotionListener() {
#Override
public void mouseDragged(MouseEvent e) {
mx = e.getX();
my = e.getY();
}
#Override
public void mouseMoved(MouseEvent e) {
}
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
drawBricks(g);
}
private double x;
private double y;
private void drawBricks(Graphics g) {
Graphics2D brick = (Graphics2D) g.create();
x = 0;
y = 0;
for (int j = 0; j <= ROWS; j++)
{
for (int a = 0; a <= COLS; a++) {
brick.setColor(Color.BLACK);
Rectangle2D.Double rect = new Rectangle2D.Double(x, y, BRICK_WIDTH, BRICK_HEIGHT);
brick.fill(rect);
if (mx > x && mx < x + BRICK_WIDTH && my > y && my < y + BRICK_HEIGHT) {
if (brick.getColor() == Color.BLACK) {
CO = Color.white;
brick.setColor(CO);
brick.fill(rect);
repaint();
}
else {
CO = Color.BLACK;
brick.setColor(CO);
brick.fill(rect);
repaint();
}
}
brick.setColor(Color.gray);
brick.draw(rect);
x += BRICK_WIDTH;
}
repaint();
x = 0;
y += BRICK_HEIGHT;
}
}
public int mx = -100;
public int my = -100;
}
public Clicky() {
setDefaultCloseOperation(EXIT_ON_CLOSE); //mai bine cu exit on close
setSize(800, 820);
add(new Board());
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Clicky().setVisible(true);
}
});
}
An easy solution is to use an 2-dimensional boolean array that represents the status of rectangles. When the color changes, change the value of the array-element and set the color depending on it:
public class Clicky extends JFrame {
private static class Board extends JPanel {
private double BRICK_WIDTH = 20;
private double BRICK_HEIGHT = 20;
public Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
public double width = screenSize.getWidth();
public double height = screenSize.getHeight();
private int COLS = (int) (width / BRICK_WIDTH);
private int ROWS = (int) (height / BRICK_HEIGHT);
private Color CO = Color.BLACK;
private boolean[][] isWhite = new boolean[COLS + 1][ROWS + 1];
public Board() {
System.out.println("WIdth:" + COLS + "Height:" + ROWS);
setBackground(Color.BLACK);
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
mx = e.getX();
my = e.getY();
System.out.printf("X: %d Y: %d ", mx, my);
isWhite[(int) (mx / BRICK_WIDTH)][(int) (my
/ BRICK_HEIGHT)] = !isWhite[(int) (mx / BRICK_WIDTH)][(int) (my / BRICK_HEIGHT)];
repaint();
}
});
addMouseMotionListener(new MouseMotionListener() {
#Override
public void mouseDragged(MouseEvent e) {
mx = e.getX();
my = e.getY();
isWhite[(int) (mx / BRICK_WIDTH)][(int) (my
/ BRICK_HEIGHT)] = !isWhite[(int) (mx / BRICK_WIDTH)][(int) (my / BRICK_HEIGHT)];
}
#Override
public void mouseMoved(MouseEvent e) {
}
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
drawBricks(g);
}
private double x;
private double y;
private void drawBricks(Graphics g) {
Graphics2D brick = (Graphics2D) g.create();
x = 0;
y = 0;
for (int j = 0; j <= ROWS; j++) {
for (int a = 0; a <= COLS; a++) {
if (isWhite[a][j]) {
brick.setColor(Color.WHITE);
} else {
brick.setColor(Color.BLACK);
}
Rectangle2D.Double rect = new Rectangle2D.Double(x, y, BRICK_WIDTH, BRICK_HEIGHT);
brick.fill(rect);
brick.setColor(Color.gray);
brick.draw(rect);
x += BRICK_WIDTH;
}
repaint();
x = 0;
y += BRICK_HEIGHT;
}
}
public int mx = -100;
public int my = -100;
}
public Clicky() {
setDefaultCloseOperation(EXIT_ON_CLOSE); // mai bine cu exit on close
setSize(800, 820);
add(new Board());
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Clicky().setVisible(true);
}
});
}
}
So I created a simple simple simulation where squares are spawned randomly with random vectors and bounce of the edges of the window.
I wanted it to take into account the window being resized. So that if I change the dimensions of the window from 600x600 to 1200x600 the squares will bounce of the new border rather than 600x600.
I tried doing getWidth() getHeight() but it would return 0.
So I put it in the pain() (since it gets called on window resize) method and saved the return values as local variables. But I cannot call getjpWidth() from the Rect class.
So basically what I need is to get new window dimension into the move() method in the Rect class.
Please feel free to point out any other mistakes and things that can be done better. I'm new to 2D programming (studying Computer Science)
Application
import javax.swing.*;
public class Application {
private Application(){
//create a JFrame window
JFrame frame = new JFrame("Moving Squares");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//add a JPanel
GamePanel gamePanel = new GamePanel();
frame.add(gamePanel);
//pack the window around the content
frame.pack();
//center
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String args[]){
new Application();
}
}
GamePanel
import java.awt.*;
import java.util.ArrayList;
import javax.swing.*;
public class GamePanel extends JPanel implements Runnable{
private int jpWidth=0, jpHeight=0;
//set JPanel size
private static final Dimension DESIRED_SIZE = new Dimension(600,600);
#Override
public Dimension getPreferredSize(){
return DESIRED_SIZE;
}
//constructor
GamePanel(){
Thread t = new Thread(this);
t.start();
}
private ArrayList <Rect> rect=new ArrayList<>();
public void run(){
for(int i=0; i<15; i++){
rect.add(new Rect());
}
while(true){
for(Rect rect:rect){
rect.move();
}
//repaint still image for better frames
//should be 100fps instead it's >144fps
repaint();
try{Thread.sleep(10);}
catch(InterruptedException e){/**/};
repaint();
try{Thread.sleep(10);}
catch(InterruptedException e){/**/};
repaint();
try{Thread.sleep(10);}
catch(InterruptedException e){/**/};
}
}
public void paint(Graphics g){
Graphics2D g2d = (Graphics2D) g.create();
jpWidth=getWidth();
jpHeight=getHeight();
g2d.setColor(Color.white);
g2d.fillRect(0,0,jpWidth,jpHeight);
for(Rect rect:rect) {
g2d.setColor(Color.black);
g2d.fillRect(rect.getXcord()-1, rect.getYcord()-1, rect.getWidth()+2, rect.getHeight()+2);
g2d.setColor(Color.getHSBColor(rect.getR(), rect.getG(), rect.getB()));
g2d.fillRect(rect.getXcord(), rect.getYcord(), rect.getWidth(), rect.getHeight());
}
}
public int getJpWidth() {
return jpWidth;
}
public int getJpHeight() {
return jpHeight;
}
}
Rect
import java.util.Random;
public class Rect {
//properties
private int width=30, height=30;
private int R, G, B;
//movement
private int xCord, yCord;
private int xVector, yVector;
private int xSlope, ySlope;
public Rect(){
Random rand = new Random();
//random color
R=rand.nextInt(255);
G=rand.nextInt(255);
B=rand.nextInt(255);
//random spawn position
xCord=rand.nextInt(600-width);
yCord=rand.nextInt(600-height);
//direction
do{
xVector=rand.nextInt(3) - 1;
yVector=rand.nextInt(3) - 1;
}while(xVector==0 || yVector==0);
//slope
do{
xSlope=rand.nextInt(3);
ySlope=rand.nextInt(3);
}while(xSlope==0 || ySlope==0);
xVector*=xSlope;
yVector*=ySlope;
}
public void move(){
//if(xCord>=//how to get screen width ? ){}
if((xCord>=600-width) || (xCord<=0)){
bounceX();
}
if((yCord>=600-height) || (yCord<=0)) {
bounceY();
}
xCord+=xVector;
yCord+=yVector;
}
public void bounceX(){
xVector*=-1;
}
public void bounceY(){
yVector*=-1;
}
public int getR() {
return R;
}
public int getG() {
return G;
}
public int getB() {
return B;
}
public int getXcord() {
return xCord;
}
public int getYcord() {
return yCord;
}
public int getWidth(){
return width;
}
public int getHeight(){
return height;
}
}
So basically what I need is to get new window dimension into the move() method in the Rect class.
Don't know if it is the best design but I pass the "panel" as a parameter to the "move()" method so its width/height can be used.
Here is some old code I have lying around that shows this approach:
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.pack();
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);
ball.setSize(64, 64);
balls.add( ball );
}
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
for (Ball ball: balls)
{
ball.draw(g);
}
}
#Override
public Dimension getPreferredSize()
{
return new Dimension(800, 600);
}
public void startAnimation()
{
Timer timer = new Timer(1000/60, 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;
}
}
}
Also, note that for animation you should be using a Swing Timer to schedule the animation. Updates to Swing components should be done on the Event Dispatch Thread (EDT). While not likely to cause a problem with this simple application it is a good habit to make sure this basic rule is followed otherwise you can have random problems and it is never easy to debug a random problem.
To be clear, I want to have a blue circle bouncing left to right which stops on click. This part works. But I also need to have a red rectangle bouncing up and down in the same gui which also stops on click. Each object should stop individually. However, two circles appear where one is moving and the other one is just still. Thanks in Advance! Help is appreciated.
/*
* This program creates an animation for a circle.
*/
package circleanimation;
import java.awt.*;
/*
* #author talhaiqbal18
*/
public class CircleAnimation
{
private int centerX, centerY, radius;
private Color color;
private int direction, speed;
private boolean filled;
public CircleAnimation(int x, int y, int r, Color c) {
centerX = x;
centerY = y;
radius = r;
color = c;
direction = 0;
speed = 0;
filled = false;
}
public void draw(Graphics g) {
Color oldColor = g.getColor();
g.setColor(color);
if (filled) {
g.fillOval(centerX - radius, centerY - radius, radius * 2, radius * 2);
g.fillRect(200, 200, 200, 200); }
else {
g.fillRect(200, 200, 200, 200);
g.fillOval(centerX - radius, centerY - radius, radius * 2, radius * 2);
g.setColor(oldColor); }
}
public void fill(Graphics g) {
Color oldColor = g.getColor();
g.setColor(color);
g.fillOval(centerX - radius, centerY - radius, radius * 2, radius * 2);
g.fillRect(200, 200, 200, 200);
g.setColor(oldColor);
}
public boolean containsPoint(int x, int y) {
int xSquared = (x - centerX) * (x - centerX);
int ySquared = (y - centerY) * (y - centerY);
int radiusSquared = radius * radius;
return xSquared + ySquared - radiusSquared <= 0;
}
public void move(int xAmount, int yAmount) {
centerX = centerX + xAmount;
centerY = centerY + yAmount;
}
public int getRadius() {
return radius;
}
public int getX() {
return centerX;
}
public int getY() {
return centerY;
}
public void setSpeed(int s) {
speed = s;
}
public void setDirection(int d) {
direction = d % 360;
}
public void turn(int degrees) {
direction = (direction + degrees) % 360;
}
public void move() {
move((int)(speed * Math.cos(Math.toRadians(direction))),
(int)(speed * Math.sin(Math.toRadians(direction))));
}
public void setFilled(boolean b) {
filled = b;
}
}
/*
* This is the color panel class for the circle animation project.
*/
package circleanimation;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
/*
* #author talhaiqbal18
*/
public class ColorPanel extends JPanel
{
private CircleAnimation circle, rectangle;
private javax.swing.Timer timer;
private CircleAnimation selectedCircle, selectedRectangle;
private int x, y;
public ColorPanel(Color backColor, int width, int height) {
setBackground(backColor);
setPreferredSize(new Dimension(width, height));
circle = new CircleAnimation(350, 300, 350, Color.blue);
circle.setDirection(180);
circle.setSpeed(6);
rectangle = new CircleAnimation(350, 300, 400, Color.red);
rectangle.setDirection(90);
rectangle.setSpeed(6);
timer = new javax.swing.Timer(1, new MoveListener());
timer.start();
addMouseListener(new PanelListener());
addMouseMotionListener(new PanelMotionListener());
addMouseMotionListener(new PanelMotionListener1());
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
circle.fill(g);
rectangle.fill(g);
}
private class MoveListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
int x = circle.getX();
int radius = circle.getRadius();
int width = getWidth();
if (x - radius <= 0 || x + radius >= width) {
circle.turn(180);
}
circle.move();
repaint();
}
}
private class MoveListener1 implements ActionListener {
public void actionPerformed(ActionEvent e) {
int x = rectangle.getX();
int radius = rectangle.getRadius();
int width = getWidth();
if (x - radius <= 0 || x + radius >= width) {
rectangle.turn(270);
}
rectangle.move();
repaint();
}
}
private class PanelListener extends MouseAdapter {
public void mousePressed(MouseEvent e)
{
x = e.getX();
y = e.getY();
if (circle.containsPoint(x, y))
selectedCircle = circle;
if (rectangle.containsPoint(x, y))
selectedRectangle = rectangle;
}
public void mouseReleased(MouseEvent e) {
//nothing
}
public void mouseClicked(MouseEvent e) {
if(timer.isRunning())
timer.stop();
else
timer.start();
}
}
private class PanelMotionListener extends MouseMotionAdapter
{
public void mouseDragged(MouseEvent e)
{
int newX = e.getX();
int newY = e.getY();
int dx = newX - x;
int dy = newY - y;
if (selectedCircle != null) {
selectedCircle.move(dx,dy);
x = newX;
y = newY;
repaint(); }
}
}
private class PanelMotionListener1 extends MouseMotionAdapter
{
public void mouseDragged(MouseEvent e)
{
int newX = e.getX();
int newY = e.getY();
int dx = newX - x;
int dy = newY - y;
if (selectedRectangle != null) {
selectedRectangle.move(dx,dy);
x = newX;
y = newY;
repaint(); }
}
}
}
/*
* This is the main method which will implement the actions of the previous classes.
*/
package circleanimation;
import java.awt.*;
import javax.swing.JFrame;
/*
* #author talhaiqbal18
*/
public class MainClass
{
public static void main(String[] args) throws Exception {
JFrame theGUI = new JFrame();
theGUI.setTitle("Circle Animation");
theGUI.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
ColorPanel panel = new ColorPanel(Color.white, 100, 100);
Container pane = theGUI.getContentPane();
pane.add(panel);
theGUI.setVisible(true);
}
}
You only register a timer for your circle, but not for your rectangle in the ColorPanel constructor
i have looked all over the internet and in my school books but I can't seem to slove my problem.
In my program "bouncing ball" (got the code from our teacher) i need to change the size of the ball from small to bigger and reverse. I understand that i need a boolean to do that and maybe alsow an if statment. This is what I have in the Ball class rigth now regarding the size change:
private boolean changeSize = true;
int maxSize = 10;
int minSize = 1;
public void changeSize(boolean size ){
if(size == maxSize ){
return minSize;
}
else return maxSize;
}
public void changeBallSize(int d, int f){
diameter = d*f;
This is the whole code for the class Ball:
class Ball {
static int defaultDiameter = 10;
static Color defaultColor = Color.yellow;
static Rectangle defaultBox = new Rectangle(0,0,100,100);
// Position
private int x, y;
// Speen and angel
private int dx, dy;
// Size
private int diameter;
// Color
private Color color;
// Bouncing area
private Rectangle box;
// New Ball
public Ball( int x0, int y0, int dx0, int dy0 ) {
x = x0;
y = y0;
dx = dx0;
dy = dy0;
color = defaultColor;
diameter = defaultDiameter;
}
// New color
public void setColor( Color c ) {
color = c;
}
public void setBoundingBox( Rectangle r ) {
box = r;
}
// ball
public void paint( Graphics g ) {
// Byt till bollens färg
g.setColor( color );
g.fillOval( x, y, diameter, diameter );
}
void constrain() {
// Ge absoluta koordinater för det rektangulära området
int x0 = box.x;
int y0 = box.y;
int x1 = x0 + box.width - diameter;
int y1 = y0 + box.height - diameter;
// Setting speed and angels
if (x < x0)
dx = Math.abs(dx);
if (x > x1)
dx = -Math.abs(dx);
if (y < y0)
dy = Math.abs(dy);
if (y > y1)
dy = -Math.abs(dy);
}
// movingt the ball
x = x + dx;
y = y + dy;
constrain();
}
}
I am a total rookie of java! Thanks for the help!
Add the following into your Ball class:
private int changeFlag=-1;
In your constrain() function, just before the last line, after moving the ball:
if(diameter==maxSize) {
changeFlag=-1;
}
else if (diameter==minSize) {
changeFlag=1;
}
diameter=diameter+changeFlag;
Add this code to your Ball Class
private minSize = 1;
private maxSize = 10;
public void setDiameter(int newDiameter) {
this.diameter = newDiameter;
}
public int getMinSize() {
return minSize;
}
public int getMinSize() {
return maxSize;
}
Use this when you use the ball
Ball ball = new Ball(1,1,1,1);
int newDiameter = 10;
if(newDiameter == ball.getMinSize()) {
ball.setDiameter(ball.getMaxSize());
}
id(newDiameter == ball.getMaxSize()) {
ball.setDiameter (ball.getMinSize());
}
I've edited it, is this what you mean?
Main Class:
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
public class Main extends JFrame {
public static void main(String[] args) {
new Main();
}
public Main() {
// configure JFrame
setSize(640, 360);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// create ball
Ball ball = new Ball(this.getWidth()/2, this.getHeight()/2, 50, 100);
add(ball);
Thread t = new Thread(ball);
t.start();
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
}
}
Ball Class:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JComponent;
public class Ball extends JComponent implements Runnable {
private int x, y, minDiameter, maxDiameter, currentDiameter, growRate = -1;
private Color color = Color.BLUE;
public Ball(int x, int y, int minDiameter, int maxDiameter) {
this.x = x;
this.y = y;
this.minDiameter = minDiameter;
this.maxDiameter = maxDiameter;
this.currentDiameter = minDiameter;
setVisible(true);
}
#Override
public void run() {
while (true) {
// coerce max and min size
if (this.currentDiameter + growRate > maxDiameter) {
this.currentDiameter = maxDiameter;
this.growRate = -1;
}
if (this.currentDiameter + growRate < minDiameter) {
this.currentDiameter = minDiameter;
this.growRate = 1;
}
this.currentDiameter += this.growRate;
repaint();
try {
Thread.sleep(10);
}
catch(Exception e) {
System.out.println(e.toString());
}
}
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
g2.setColor(this.color);
g2.fillOval(this.x, this.y, this.currentDiameter, this.currentDiameter);
}
}