I declared and built an array of two Balls instances ' m_ball[0]=new Ball(400,400,400,180);' & ' m_ball[1]=new Ball(400,400,400,0);' with different parameter values (180 and 0) but for some reason it seems that this different values get reset to 0 when the Timer start running and than both balls place on the same XY location. Can someone see why? I'm new in programming, Many Thanks... .
//This is main
public class BBDemo extends JApplet{
...........
}
}//endclass BBDemo
public class BBPanel extends JPanel {
BallInBox m_bb; // The moving ball panel ///in order to take the Timer->setAnimation from m_bb
//========================================================== constructor
/** Creates a panel with the controls and moving ball display. */
BBPanel() {
//... Create components
m_bb = new BallInBox(); // The moving ball panel
.........
startButton.addActionListener(new StartAction());
.........
}
}//endclass BBPanel
public class BallInBox extends JPanel {
private Ball m_ball[]= new Ball[2];
//... Instance variables for the animation
private int m_interval = 40; // Milliseconds between updates.
static Timer m_timer; // Timer fires to animate one step.
static int j;
private Color Pigment;
//=================================================== constructor
/** Set panel size and creates timer. */
public BallInBox() {
.........
m_timer = new Timer(m_interval, new TimerAction());
addMouseListener(new PressBall());
m_ball[0]=new Ball(400,400,400,180);
m_ball[1]=new Ball(400,400,400,0);
}
public void setAnimation(boolean turnOnOff) {
if (turnOnOff) {
m_timer.start(); j=1; // start animation by starting the timer.
} else {
m_timer.stop(); j=0; // stop timer
}
}
//======================================================= paintComponent
public void paintComponent(Graphics g) {
super.paintComponent(g); // Paint background, border
........
g.setColor(Pigment);
m_ball[0].draw(g); // Draw the ball.
m_ball[1].draw(g);
}
class TimerAction implements ActionListener {
public void actionPerformed(ActionEvent e) {
m_ball[0].setBounds(getWidth(), getHeight());
m_ball[0].move(); // Move the ball.
m_ball[1].setBounds(getWidth(), getHeight());
m_ball[1].move(); // Move the ball.
repaint(); // Repaint indirectly calls paintComponent.
}
}
}
}//endclass
public class Ball {
//... Constants
final static int DIAMETER = 50;
//... Instance variables
static int m_x; // x and y coordinates upper left
static int m_y;
......
//public int deg=90; // Pixels to move each time move() is called.
public int deg;
public int offset=400;
//======================================================== constructor
public Ball(int x, int y, int offset, int deg) {
m_x = x;
m_y = y;
}
.........
}
//============================================================== move
public void move() {
double degrees=(double) deg;
double radians=Math.toRadians(degrees);
double Sinu=Math.sin(radians);
double Sinu200=Math.sin(radians)*300;
int SinuInt=(int) Sinu200;
m_y=offset+SinuInt;
double Cos=Math.cos(radians);
double Cos200=Math.cos(radians)*300;
int CosInt=(int) Cos200;
m_x=offset+CosInt;
deg++; // j--;
if (deg==360) deg=0;
}
//============================================================== draw
public void draw(Graphics g) {
g.fillOval(m_x, m_y, DIAMETER, DIAMETER);
}
//============================================= getDiameter, getX, getY
public int getDiameter() { return DIAMETER;}
public int getX() { return m_x;}
public int getY() { return m_y;}
//======================================================== setPosition
public void setPosition(int x, int y) {
m_x = x;
m_y = y;
}
}
In your Ball constructor, both offset and deg are not set. Try:
public Ball(int x, int y, int offset, int deg) {
m_x = x;
m_y = y;
this.offset = offset;
this.deg = deg;
}
In the Ball class, m_x and m_y are static, so they are shared by all instances of Ball. Removing the word static from their declarations should help.
Related
I'm making a simple Java program to bounce a ball up and down. The problem is that the ball bounces up higher than its starting point with each bounce. I expect the ball to bounce back up exactly to the height that it started from.
The ball physics can be found in the circle class in the doPhysics() method where I suspect the problem can be found
import java.awt.*;
import java.util.*;
public class Main{
public static Frame frame = new Frame();
public static Physics physics = new Physics();
public static ArrayList<Circle> circles = new ArrayList<Circle>(); //array for the points
public static void main(String args[]) {
Circle circle = new Circle(100, 300, 50, Color.BLACK);
circles.add(circle);
run();
}
public static void run() {
physics.timer.start();
}
}
import java.awt.*;
public class Circle {
private int x;
private int y;
private double xAccel= 0;
private double yAccel = 0;
private double xVel= 0;
private double yVel = 0;
private Color colour;
private int radius;
public Circle(int x, int y, int radius, Color colour) {
setX(x);
setY(y);
setRadius(radius);
setColour(colour);
}
public void draw(Graphics2D g2d) {
g2d.setColor(colour);
g2d.fillOval(x, y, radius*2, radius*2);
}
public void doPhysics() {
hitGround();
System.out.println(yVel);
yVel += Physics.getGravity();
y -= yVel;
}
public void hitGround() {
if(y + radius*2 > Frame.panel.h ) {
yVel = -yVel;
}
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void setColour(Color colour) {
this.colour = colour;
}
public void setRadius(int radius) {
this.radius = radius;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public Color getColour() {
return colour;
}
public int getRadius() {
return radius;
}
}
import java.awt.*;
import javax.swing.*;
class Frame extends JFrame {
public static Panel panel;
public Frame() {
panel = new Panel();
this.setTitle("Fun");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.add(panel);
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
}
}
class Panel extends JPanel {
public int w = 500;
public int h = 500;
public Panel() {
this.setPreferredSize(new Dimension(w, h));
this.setBackground(Color.red);
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
for(Circle circle : Main.circles) {
circle.draw(g2d);
}
}
}
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
public class Physics implements ActionListener {
private static double gravity = -.1;
public Timer timer;
public Physics() {
timer = new Timer(1, this);
}
public static double getGravity() {
return gravity;
}
#Override
public void actionPerformed(ActionEvent e) {
for(Circle circle : Main.circles) {
circle.doPhysics();
}
Main.frame.repaint();
}
}
The problem is mainly caused by using integer values for position (x and y). On each iteration the values are rounded and the errors get accumulated.
Solution: declare double x and double y and only use the rounded integer values for drawing.
Above should reduce the problem, but not completely solve it. The code is doing a rough integration over timeĀ¹ by using the velocity calculated after the time interval (see Numerical Integration). This can be improved by doing an average of the velocities before and after it was changed. Roughly:
double preVel = yVel;
yVel += Physics.getGravity();
y -= (preVel + yVel)/2;
which can be simplified (pure math) to:
yVel += Physics.getGravity();
y -= yVel - Physics.getGravity()/2;
This should work fine since the acceleration is constant. Not the case if the acceleration is also changing. And it is also susceptible to precision errors being accumulated over time.
1 - see Numerical integration and Temporal discretization
I am trying to rotate a circle around a separate point in a program. right now I can get the circle to rotate but it slowly starts getting closer and closer to the point it's rotating from. I am trying to do this using JPanel and implementing it as a rectangle.
package WoffindenZone;
import java.awt.*;
import javax.swing.*;
import java.util.*;
import java.awt.event.*;
import java.lang.Math;
public class Protector extends Rectangle{
double Velocity;
int speed = 3;
Protector(int x, int y, int PROTECTOR_DIAMETER){
super(x,y,PROTECTOR_DIAMETER,PROTECTOR_DIAMETER);
}
public void keyPressed(KeyEvent e){
if(e.getKeyCode()==KeyEvent.VK_A) {
setDirection(speed);
move();
}
if(e.getKeyCode()==KeyEvent.VK_D) {
setDirection(speed);
move();
}
}
public void keyReleased(KeyEvent e){
if(e.getKeyCode()==KeyEvent.VK_A) {
setDirection(0);
move();
}
if(e.getKeyCode()==KeyEvent.VK_D) {
setDirection(0);
move();
}
}
public void setDirection(int Direction){
Velocity = Direction*Math.PI/180;
}
public void move(){
x = (int)Math.round(500 + Math.cos(Velocity) * (x-500) - Math.sin(Velocity) * (y-((1000*0.5555)/2)));
y = (int)Math.round(((1000*0.5555)/2) + Math.sin(Velocity) * (x-500) + Math.cos(Velocity) * (y-((1000*0.5555)/2)));
System.out.println(x);
System.out.println(y);
}
public void draw(Graphics g){
g.setColor(Color.blue);
g.fillOval(x,y,width,height);
}
Use a rotation instance of an AffineTransform. See getRotateInstance(theta,anchorx,anchory) for details.
Returns a transform that rotates coordinates around an anchor point. This operation is equivalent to translating the coordinates so that the anchor point is at the origin (S1), then rotating them about the new origin (S2), and finally translating so that the intermediate origin is restored to the coordinates of the original anchor point (S3).
How do you rotate a circle about a point in JPanel?
Here's how I rotate a circle about a point in a JPanel.
I don't know how to make an animated GIF. Just imagine the blue circle rotating clockwise around the center of the drawing JPanel.
So, let's start at the beginning. Basically, I have a circle rotating on the circumference of another circle. So, I create a Circle model class from plain Java.
public class Circle {
private final int radius;
private final Color color;
private Point center;
public Circle(int radius, Color color) {
this.radius = radius;
this.color = color;
}
public Point calculateCircumferencePoint(int theta) {
double radians = Math.toRadians(theta);
int x = center.x + (int) Math.round(Math.cos(radians) * radius);
int y = center.y + (int) Math.round(Math.sin(radians) * radius);
return new Point(x, y);
}
public void setCenter(int x, int y) {
this.center = new Point(x, y);
}
public void setCenter(Point center) {
this.center = center;
}
public int getRadius() {
return radius;
}
public Color getColor() {
return color;
}
public Point getCenter() {
return center;
}
}
The class consists of basic getters and setters. I make the radius and color final because they don't change value in this Java application.
The calculateCircumferencePoint method is the only interesting method. It takes an int angle in degrees, and calculates the point on the circumference represented by that angle, rounded to the nearest X and Y integer points.
Next, we create two Circle instances, an inner circle and an outer circle. Here's the class constructor setting the preferred size of the drawing area, the inner circle, and the outer circle. We start the outer circle at zero degrees (to the right);
private Circle innerCircle;
private Circle outerCircle;
private Dimension drawingPanelSize;
public RotateCircle() {
this.drawingPanelSize = new Dimension(400, 400);
int innerCircleRadius = drawingPanelSize.width / 4;
int centerX = drawingPanelSize.width / 2;
int centerY = drawingPanelSize.height / 2;
int outerCircleRadius = drawingPanelSize.width / 10;
this.innerCircle = new Circle(innerCircleRadius, null);
this.innerCircle.setCenter(centerX, centerY);
this.outerCircle = new Circle(outerCircleRadius, Color.BLUE);
Point point = innerCircle.calculateCircumferencePoint(0);
this.outerCircle.setCenter(point);
}
Now, we can start coding the GUI. First, we start the Java application by calling the SwingUtilities invokeLater method. This method ensures that we create and execute the Swing components on the Event Dispatch Thread.
Next, we define the JFrame. Here's the code we have so far.
public static void main(String[] args) {
SwingUtilities.invokeLater(new RotateCircle());
}
private Animation animation;
private Circle innerCircle;
private Circle outerCircle;
private DrawingPanel drawingPanel;
private Dimension drawingPanelSize;
public RotateCircle() {
this.drawingPanelSize = new Dimension(400, 400);
int innerCircleRadius = drawingPanelSize.width / 4;
int centerX = drawingPanelSize.width / 2;
int centerY = drawingPanelSize.height / 2;
int outerCircleRadius = drawingPanelSize.width / 10;
this.innerCircle = new Circle(innerCircleRadius, null);
this.innerCircle.setCenter(centerX, centerY);
this.outerCircle = new Circle(outerCircleRadius, Color.BLUE);
Point point = innerCircle.calculateCircumferencePoint(0);
this.outerCircle.setCenter(point);
}
#Override
public void run() {
JFrame frame = new JFrame("Rotate Circle");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
drawingPanel = new DrawingPanel(drawingPanelSize,
outerCircle);
frame.add(drawingPanel, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
animation = new Animation(0);
new Thread(animation).start();
}
The JFrame methods must be called in a certain order. This is the order I use for most of my SWwing applications.
I pack the JFrame. I don't set a JFrame size. I let the Swing layout managers set the size of my JFrame. The default layout of a JFrame content pane is a BorderLayout. I put my drawing JPanel in the center of the BorderLayout.
Next, I create the drawing JPanel.
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
private Circle circle;
public DrawingPanel(Dimension size, Circle circle) {
this.circle = circle;
this.setBackground(Color.WHITE);
this.setPreferredSize(size);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Point center = circle.getCenter();
int radius = circle.getRadius();
int diameter = radius + radius;
g2d.setColor(circle.getColor());
g2d.fillOval(center.x - radius, center.y - radius,
diameter, diameter);
}
}
All the drawing JPanel does is paint a Circle object. Pretty straightforward.
The fillOval method paints an oval from the upper left hand corner. We calculate the upper left hand point from the center point.
The responsibility for calculating and updating the outer circle center point falls to my controller class, the Animation class. I use a simple loop to update the theta angle, calculate the new outer circle center point, paint the outer circle, and wait a period of time.
Here's that code.
public class Animation implements Runnable {
private int theta;
public Animation(int theta) {
this.theta = theta;
}
#Override
public void run() {
while (true) {
theta++;
theta = (theta >= 360) ? 0 : theta;
Point center = innerCircle.calculateCircumferencePoint(theta);
outerCircle.setCenter(center);
repaint();
sleep(30L);
}
}
private void repaint() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
drawingPanel.repaint();
}
});
}
private void sleep(long duration) {
try {
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
The Animation repaint method makes the call to the drawing JPanel repaint method inside another SwingUtilities invokeLater method. This method ensures that the drawing happens on the Event Dispatch Thread.
Finally, here's the complete, runnable, example. I used inner classes so I could post the code as one block, and you can copy and run this code as one block. Generally, classes should be in separate files and for a more complex GUI, separate packages.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class RotateCircle implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new RotateCircle());
}
private Animation animation;
private Circle innerCircle;
private Circle outerCircle;
private DrawingPanel drawingPanel;
private Dimension drawingPanelSize;
public RotateCircle() {
this.drawingPanelSize = new Dimension(400, 400);
int innerCircleRadius = drawingPanelSize.width / 4;
int centerX = drawingPanelSize.width / 2;
int centerY = drawingPanelSize.height / 2;
int outerCircleRadius = drawingPanelSize.width / 10;
this.innerCircle = new Circle(innerCircleRadius, null);
this.innerCircle.setCenter(centerX, centerY);
this.outerCircle = new Circle(outerCircleRadius, Color.BLUE);
Point point = innerCircle.calculateCircumferencePoint(0);
this.outerCircle.setCenter(point);
}
#Override
public void run() {
JFrame frame = new JFrame("Rotate Circle");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
drawingPanel = new DrawingPanel(drawingPanelSize,
outerCircle);
frame.add(drawingPanel, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
animation = new Animation(0);
new Thread(animation).start();
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
private Circle circle;
public DrawingPanel(Dimension size, Circle circle) {
this.circle = circle;
this.setBackground(Color.WHITE);
this.setPreferredSize(size);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Point center = circle.getCenter();
int radius = circle.getRadius();
int diameter = radius + radius;
g2d.setColor(circle.getColor());
g2d.fillOval(center.x - radius, center.y - radius,
diameter, diameter);
}
}
public class Animation implements Runnable {
private int theta;
public Animation(int theta) {
this.theta = theta;
}
#Override
public void run() {
while (true) {
theta++;
theta = (theta >= 360) ? 0 : theta;
Point center = innerCircle.calculateCircumferencePoint(theta);
outerCircle.setCenter(center);
repaint();
sleep(30L);
}
}
private void repaint() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
drawingPanel.repaint();
}
});
}
private void sleep(long duration) {
try {
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Circle {
private final int radius;
private final Color color;
private Point center;
public Circle(int radius, Color color) {
this.radius = radius;
this.color = color;
}
public Point calculateCircumferencePoint(int theta) {
double radians = Math.toRadians(theta);
int x = center.x + (int) Math.round(Math.cos(radians) * radius);
int y = center.y + (int) Math.round(Math.sin(radians) * radius);
return new Point(x, y);
}
public void setCenter(int x, int y) {
this.center = new Point(x, y);
}
public void setCenter(Point center) {
this.center = center;
}
public int getRadius() {
return radius;
}
public Color getColor() {
return color;
}
public Point getCenter() {
return center;
}
}
}
I'm writing a game in which i need to paint bricks of a random color. This is my progress so far. I have a class of Brick, AllBricks, and Overridden paint method for JPanel:
private static class Brick {
static int x;
static int y;
static Color color;
Brick(int _x, int _y, Color _color){
x = _x;
y = _y;
color = _color;
}
void paint(Graphics g) {
g.setColor(Color.WHITE);
g.drawRoundRect(x, y, BRICK_SIZE, BRICK_SIZE, BRICK_ARC_SIZE, BRICK_ARC_SIZE);
g.setColor(color);
g.fillRoundRect(x + 1, y + 1, BRICK_SIZE-2, BRICK_SIZE-2, BRICK_ARC_SIZE-1, BRICK_ARC_SIZE-1);
}
private static class AllBricks {
private ArrayList<Brick> bList = new ArrayList<>();
AllBricks(){ bList.clear(); }
void add (Brick b){ bList.add(b); }
void paint(Graphics g) {
if(bList.size()>0) {
for (Brick brick : bList) brick.paint(g);
}
}
}
private static class GameField extends JPanel {
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
allBricks.paint(g);
}
}
And now, when I call my main loop, adding new blocks and trying to draw them, i only see the last added block, but not all of them...
private void loop()
{
while (true) {
delay.wait(1000);
Brick b1 = new Brick(random.nextInt(WIN_WIDTH - BRICK_SIZE), random.nextInt(WIN_HEIGHT - BRICK_SIZE), COLORS.get(random.nextInt(COLORS.size() - 1)));
allBricks.add(b1);
mainField.repaint();
}
}
Can you, please, help me save previously painted blocks on the screen?
Your brick x and y coordinate shoudn't be static. Since it's static all Bricks have one shared x and y value (so all Bricks are drawn at the same position)
This is my first Graphical Java program and what I'm trying to do is re-create a simple classic program where I have multiple balls bouncing in a JFrame window.
So far I have successfully been able to get one ball to bounce around using code inside the run() method. That works for one ball object that I create, but now I want to have many balls so I'm trying to create a method in my Ball class that will make each ball object that I create bounce in my "ball world" independently.
Right now all I care about is them bouncing off the walls, not each other (I will figure that out later).
The problem: In my ballMove(int, int, int, int) method I have four int parameters where the first two parameters are the width and height of the window, and the last two parameters are the Xspeed, and Yspeed. When I go through my if statements it will temperately set the x and y speed parameters to negative when the ball hits the right or bottom wall, but when the run() method executes the ballMove(int, int, int, int) method again, they go back to being positive and the balls disappear from the window. I have tried using a bunch of getter and setter methods in my ball class. I have tried temporary variables within my ballMove(int, int, int, int) method. Nothing I've tried works.
Question: By using my Ball class method, how do I prevent my parameters Xspeed and Yspeed from reinitializing my instance speed variables to positive when the ball(s) collide with the walls?
Because I'm new to graphical programming any helpful suggestions would be greatly appreciated.
import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;
import javax.swing.JFrame;
public class Main extends JFrame implements Runnable {
/**
*
*/
private static final long serialVersionUID = 1L;
private int width = 800;
private int height= 600;
private int ballRadius = 50;
private Random rand = new Random();
//Create and initialize a ball object
public Ball ball = new Ball(Color.BLUE, ballRadius, ballRadius, rand.nextInt(500), rand.nextInt(500));
//public Ball ball2 = new Ball(Color.RED, ballRadius, ballRadius, rand.nextInt(500), rand.nextInt(500));
//public Ball ball3 = new Ball(Color.GREEN, ballRadius, ballRadius, rand.nextInt(500), rand.nextInt(500));
//public Ball ball4 = new Ball(Color.ORANGE, ballRadius, ballRadius, rand.nextInt(500), rand.nextInt(500));
//public Ball ball5 = new Ball(Color.YELLOW, ballRadius, ballRadius, rand.nextInt(500), rand.nextInt(500));
//constructor
public Main(){
setSize(width, height);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
//Paint the ball(s)
public void paint(Graphics g){
super.paint(g);
g.setColor(ball.getColor());
g.fillOval(ball.getBallX(), ball.getBallY(), ball.getWidth(), ball.getHeight());
//g.setColor(ball2.getColor());
//g.fillOval(ball2.getBallX(), ball2.getBallY(), ball2.getWidth(), ball2.getHeight());
//g.setColor(ball3.getColor());
//g.fillOval(ball3.getBallX(), ball3.getBallY(), ball3.getWidth(), ball3.getHeight());
//g.setColor(ball4.getColor());
//g.fillOval(ball4.getBallX(), ball4.getBallY(), ball4.getWidth(), ball4.getHeight());
//g.setColor(ball5.getColor());
//g.fillOval(ball5.getBallX(), ball5.getBallY(), ball5.getWidth(), ball5.getHeight());
}
//Run the program
public static void main(String [] args){
Main main = new Main();
main.setVisible(true);
main.run();
}
#Override
public void run() {
// TODO Auto-generated method stub
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ball.ballMove(width, height, 20, 5);
repaint();
//ball2.ballMove(width, height, 15, 3);
//repaint();
//ball3.ballMove(width, height, 3, 20);
//repaint();
//ball4.ballMove(width, height, 10, 10);
//repaint();
//ball5.ballMove(width, height, 10, 20);
//repaint();
}
}
}
Here is my Ball class
import java.awt.Color;
import javax.swing.JFrame;
public class Ball extends JFrame {
/**
*
*/
private static final long serialVersionUID = 1L;
private int width, height, ball_X, ball_Y;
private int Xspeed;
private int Yspeed;
private Color color;
public Ball(Color color, int width, int height, int ball_X, int ball_Y){
this.width = width;
this.height = height;
this.color = color;
this.ball_X = ball_X;
this.ball_Y = ball_Y;
}
public Color getColor(){
return this.color;
}
public int getWidth(){
return this.width;
}
public int getHeight(){
return this.height;
}
public int getBallX(){
return this.ball_X;
}
public int getBallY(){
return this.ball_Y;
}
public void setSpeedX(int x){
this.Xspeed = x;
}
public void setSpeedY(int x){
this.Yspeed = x;
}
public int getSpeedX(){
return this.Xspeed;
}
public int getSpeedY(){
return this.Yspeed;
}
public void setBallX(int x){
this.ball_X = x;
}
public void setBallY(int y){
this.ball_Y = y;
}
public void ballMove(int X, int Y, int xSpeed, int ySpeed){
//initialize Xspeed and Yspeed with the parameters of the function
this.setSpeedX(xSpeed);
this.setSpeedY(ySpeed);
//Moves the balls by adding the set speed to the position of the balls each time thread is executed
this.setBallX(this.getBallX() + this.getSpeedX());
this.setBallY(this.getBallY() + this.getSpeedY());
//When the balls hit the walls they are suppose to bounce back until they hit another wall.
if(this.getBallX() + 50 >= X){
this.setSpeedX(-xSpeed);
}
if(this.getBallY() + 50 >= Y){
this.setSpeedY(-ySpeed);
}
if(this.getBallX() + 25 <= 0){
this.setBallX(xSpeed);
}
if(this.getBallY() + 25 <= 0){
this.setSpeedY(ySpeed);
}
}
}
Your problem is right there:
ball.ballMove(width, height, 20, 5);
Since this is a loop, every time it's called you make it move in the same direction. You're inverting the speed at the end of ballmove if it hits a wall but it doesn't matter because next time you call it, the ball still moves towards +20, +5.
My suggestion is to add the speed parameters when you create your instance of ball and have the ballmove update its own speed.
ball.ballMove(width, height);
Other suggestion: Put your collision check before actually moving the ball. That way you can make sure you're not going in the wrong direction before actually moving it.
Now, another problem is that in your main, you're calling the run() method of your runnable. Run gets executed on the current thread. You'd need to do something like this:
Thread t = new Thread(main);
t.start();
To do drawings and calculations independently of the JFrame.
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.