I have a model of planet (below) and paint the Sun in center of screen. Then I draw the planets using a new thread. Planets move is too fast and not means that it's a move on a circle. I tried to change the thread's sleep time and planet's velocity and does not matter - planets move too fast. Velocity > 3 - too fast speed.
I need result: Planets move slow and I can manage a planet's speed with her velocity (1, 3, 5, 10). Angle (position of planet) changes 1 time in second on small amount (1, 3, 5 degrees - velocity)
public class Planet
{
private String name;
private int id;
private double radius = 1.0;
private double radiusOrbit = 5.0;
private double velocity = 1;
private Color color;
private int angle = 0;
private String parent;
public Planet(String name, int id, double rad, double radOrbit, double velocity, Color color)
{
this.name = name;
this.id = id;
this.radius = rad;
this.radiusOrbit = radOrbit;
this.velocity = velocity;
this.color = color;
}
...getters and setters
}
Main Class
public class ShowCosmos2 {
public static void main(String[] args)
{
JFrame frame = new PlanetsFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
class PlanetsFrame extends JFrame
{
private int width = Toolkit.getDefaultToolkit().getScreenSize().width;
private int height = Toolkit.getDefaultToolkit().getScreenSize().height;
public PlanetsFrame()
{
setSize(width, height);
setTitle("Planets");
setContentPane(new PlanetsCanvas(width, height));
Container contentPane = getContentPane();
contentPane.setBackground(Color.BLACK);
}
}
class PlanetsCanvas extends JPanel
{
private int width, height;
private int centerX = 0;
private int centerY = 0;
private Thread runner;
private boolean running = false;
Planet[] planets = {
new Planet("Venera", 1, 5.0, 50.0, 1, Color.GREEN),
new Planet("Mercury", 1, 3.0, 75.0, 1.5, Color.ORANGE),
new Planet("Earth", 1, 6.0, 100.0, 2, Color.BLUE),
new Planet("Jupiter", 1, 12.0, 150.0, 1, Color.RED)
};
public PlanetsCanvas(int w, int h)
{
width = w;
height = h;
centerX = (int)(w/2);
centerY = (int)(h/2);
}
protected void drawFrame(Graphics g)
{
//Sun
g.setColor(Color.YELLOW);
g.fillOval(width/2 - 25, height/2 - 25, 50, 50);
for (int i = 0; i < planets.length; i++)
{
Planet p = planets[i];
g.setColor(p.getColor());
int newX = (int)(centerX + Math.cos(p.getAngle())*p.getRadiusOrbit());
int newY = (int)(centerY - Math.sin(p.getAngle())*p.getRadiusOrbit());
g.fillOval((int)(newX-p.getRadius()),
(int)(newY-p.getRadius()),
(int)p.getRadius()*2, (int)p.getRadius()*2);
//int angle = (int)(p.getAngle() + p.getVelocity());
//if (angle >= 360) angle = 0;
//p.setAngle(angle);
}
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
drawFrame(g);
startAnimation();
}
public void startAnimation() {
runner = new Thread() {
public void run() {
try {
while (!Thread.interrupted()) {
repaint();
for(int i=0; i<planets.length; i++)
{
Planet p = planets[i];
int angle = (int)(p.getAngle() + p.getVelocity());
if (angle >= 360) angle = 0;
p.setAngle(angle);
}
Thread.sleep(500);
}
} catch (Exception e) {
}
}
};
runner.start();
running = true;
}
}
Most important -- don't start your animation from within paintComponent. The paintcomponent method will keep being called over and over again, meaning you're going to be creating more and more animation threads unnecessarily, when only one is what's called for. What's worse, you do not have complete control over when or even if paintComponent is called. So instead start your animation thread once and likely in your class's constructor.
Consider following points
Your startAnimation() method should be called once only and not in paintComponent() method which will instantiate a new thread on every repaint()
Apart from that keep the angle a double type as this will allow you to make arbitrarily small increments and decrements to it.
Thead.sleep() interval should be the single frame time.
Maintain a DAMPING_COFFICIENT to multiply to velocity when calculating new angle to slow down or speed up.
Here's modified slowed down code.
import java.awt.Color;
public class Planet {
private String name;
private int id;
private double radius = 1.0;
private double radiusOrbit = 5.0;
private double velocity = 1;
private Color color;
private double angle = 0;
private String parent;
public Planet(String name, int id, double rad, double radOrbit,
double velocity, Color color) {
this.name = name;
this.id = id;
this.radius = rad;
this.radiusOrbit = radOrbit;
this.velocity = velocity;
this.color = color;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
public double getRadiusOrbit() {
return radiusOrbit;
}
public void setRadiusOrbit(double radiusOrbit) {
this.radiusOrbit = radiusOrbit;
}
public double getVelocity() {
return velocity;
}
public void setVelocity(double velocity) {
this.velocity = velocity;
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public String getParent() {
return parent;
}
public void setParent(String parent) {
this.parent = parent;
}
public double getAngle() {
return angle;
}
public void setAngle(double angle) {
this.angle = angle;
}
}
Rest of the classes
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Toolkit;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ShowCosmos2 {
public static void main(String[] args) {
JFrame frame = new PlanetsFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
class PlanetsFrame extends JFrame {
private int width = Toolkit.getDefaultToolkit().getScreenSize().width;
private int height = Toolkit.getDefaultToolkit().getScreenSize().height;
public PlanetsFrame() {
setSize(width, height);
setTitle("Planets");
setContentPane(new PlanetsCanvas(width, height));
Container contentPane = getContentPane();
contentPane.setBackground(Color.BLACK);
}
}
class PlanetsCanvas extends JPanel {
private static final double DAMPING_COFFICIENT = 0.01;
private static final int FRAMES_PER_SECOND = 60;
private static final long FRAME_DURATION = (1000 / FRAMES_PER_SECOND);
private int width, height;
private int centerX = 0;
private int centerY = 0;
private Thread runner;
private boolean running = false;
Planet[] planets = { new Planet("Venera", 1, 5.0, 50.0, 1, Color.GREEN),
new Planet("Mercury", 1, 3.0, 75.0, 1.5, Color.ORANGE),
new Planet("Earth", 1, 6.0, 100.0, 2, Color.BLUE),
new Planet("Jupiter", 1, 12.0, 150.0, 1, Color.RED) };
public PlanetsCanvas(int w, int h) {
width = w;
height = h;
centerX = (int) (w / 2);
centerY = (int) (h / 2);
startAnimation();
}
protected void drawFrame(Graphics g) {
// Sun
g.setColor(Color.YELLOW);
g.fillOval(width / 2 - 25, height / 2 - 25, 50, 50);
for (int i = 0; i < planets.length; i++) {
Planet p = planets[i];
g.setColor(p.getColor());
int newX = (int) (centerX + Math.cos(p.getAngle())
* p.getRadiusOrbit());
int newY = (int) (centerY - Math.sin(p.getAngle())
* p.getRadiusOrbit());
g.fillOval((int) (newX - p.getRadius()),
(int) (newY - p.getRadius()), (int) p.getRadius() * 2,
(int) p.getRadius() * 2);
// int angle = (int)(p.getAngle() + p.getVelocity());
// if (angle >= 360) angle = 0;
// p.setAngle(angle);
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
drawFrame(g);
}
public void startAnimation() {
runner = new Thread() {
public void run() {
try {
while (!Thread.interrupted()) {
repaint();
for (Planet p : planets) {
double angle = (p.getAngle() + p.getVelocity() * DAMPING_COFFICIENT);
//System.out.println(p.getName() + " : " + angle);
if (angle >= 360)
angle = 0;
p.setAngle(angle);
}
Thread.sleep(FRAME_DURATION);
}
} catch (Exception e) {
}
}
};
runner.start();
running = true;
}
}
.
Related
I am trying to make a program where object1 movement is warped by the gravity of object2. It should get flung. However, for some reason the velocity never decreases or goes negative. I expect for the velocity to change in both directions, but this does not happen. Can anyone tell me why? Here is the code.
Sim.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Sim extends JPanel {
private static final long serialVersionUID = -2669101810074157675L;
public static final int PREF_W = 800, PREF_H = 600;
private Mass object1, object2;
private Sim() {
this.setFocusable(true);
this.setBackground(Color.WHITE);
double[] vect1 = {1, 0}, vect2 = {0, 0};
object1 = new Mass(new Point(PREF_W / 2 - 100, PREF_H / 2 - 100), vect1, 10);
object2 = new Mass(new Point(PREF_W / 2 + 100, PREF_H / 2 + 100), vect2, 30);
object2.lock();
gameTimer.start();
}
private Timer gameTimer = new Timer(1000 / 30, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
double[] v = Mass.vector(object1, object2);
object1.dx += v[0];
object1.dy += v[1];
Point p = new Point(
(int) (object1.center.x + object1.dx),
(int) (object1.center.y + object1.dy)
);
object1.center = p;
System.out.println("[" + object1.center.x + "," + object1.dy + "]");
}
});
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.fillOval(
(int) object1.center.x - (int) object1.radius,
(int) object1.center.y - (int) object1.radius,
(int) object1.radius,
(int) object1.radius
);
g2.fillOval(
(int) object2.center.x - (int) object2.radius,
(int) object2.center.y - (int) object2.radius,
(int) object2.radius,
(int) object2.radius
);
g2.drawLine(object1.center.x, object1.center.y, object2.center.x, object2.center.y);
repaint();
}
/* METHODS FOR CREATING JFRAME AND JPANEL */
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("Gravity Simulation");
JPanel gamePanel = new Sim();
frame.getContentPane().add(gamePanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
Mass.java
import java.awt.Point;
public class Mass {
public static double G = 1f;
public static double deltaTime = 1;
public Point center;
public double mass;
public double radius;
public double dx = 0;
public double dy = 0;
public boolean locked = false;
public Mass(Point center, double[] vect, double mass) {
this.center = center;
this.dx = vect[0];
this.dy = vect[1];
this.mass = mass;
this.radius = mass;
}
public void lock() {
this.locked = true;
}
public void unlock() {
this.locked = false;
}
public static double distance(Mass obj1, Mass obj2) {
double dX = obj1.center.x - obj2.center.x;
double dY = obj1.center.y - obj2.center.y;
double ans = Math.sqrt(Math.pow(dX, 2) + Math.pow(dY, 2));
return (double) ans;
}
public static double force(Mass obj1, Mass obj2) {
double ans = ((obj1.mass * obj2.mass) / Math.pow(distance(obj1, obj2), 2)) * G;
return (double) ans;
}
public static double[] vector(Mass obj1, Mass obj2) {
double force = force(obj1, obj2);
double dX = Math.abs(obj1.center.x - obj2.center.x);
double dY = Math.abs(obj1.center.y - obj2.center.y);
double udX = dX / distance(obj1, obj2);
double udY = dY / distance(obj1, obj2);
double fx = -(udX * force);
double fy = -(udY * force);
double x = obj1.dx + fx / obj1.mass * deltaTime;
double y = obj1.dy + fy / obj1.mass * deltaTime;
double[] v = {x, y};
return v;
}
}
Thank you in advance. I derived my formula from F = G(m1*m2)/r^2.
Here is the task:
Ants move in one place in the region of their residence (for example, [0; 0]) in a straight line with a speed V, and then turn back to the point of their birth with the same speed.I have problems with the moving of objects. The object must stop at the certain point and go back to starting point. How should I fix my code? Some code I have written:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
class vThread extends Thread{
public void run(){
new LabSevenFirst();
System.out.println(Thread.currentThread().getName());
}
}
public class LabSevenFirst extends JPanel implements ActionListener {
private JFrame fr;
double x = 10;
double y = 10;
double r = 10;
public static double T=0, V;
private float x1, y1, x2, y2, xc, yc;
private int t0;
private Timer timer;
private JButton start, stop, apply;
private JLabel forx1, fory1, forx2, fory2, forV;
private JTextField fx1, fy1, fx2, fy2, fV;
public static void main(String[] args) throws InterruptedException {
vThread mt = new vThread();
mt.setName("Ants-labours");
mt.start();
Thread.yield();//позволяет досрочно завершить квант времени текущей нити
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName());
}
LabSevenFirst() {
t0 = 1000/60;
timer = new Timer(t0, this);
timer.setActionCommand("timer");
fr = new JFrame("Movement of ants-labours");
fr.setLayout(null);
fr.setSize(600, 600);
fr.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 50, 300, 300);
start = new JButton("Start");
stop = new JButton("Stop");
apply = new JButton("Apply");
forx1 = new JLabel("x1");
fory1 = new JLabel("y1");
forx2 = new JLabel("x2");
fory2 = new JLabel("y2");
forV = new JLabel("V");
fx1 = new JTextField(x1 + "");
fy1 = new JTextField(y1 + "");
fx2 = new JTextField(x2 + "");
fy2 = new JTextField(y2 + "");
fV = new JTextField(V + "");
forx1.setBounds(5, 380, 20, 20);
fory1.setBounds(5, 400, 20, 20);
forx2.setBounds(5, 420, 20, 20);
fory2.setBounds(5, 440, 20, 20);
forV.setBounds(5, 460, 20, 20);
fx1.setBounds(30, 380, 40, 20);
fy1.setBounds(30, 400, 40, 20);
fx2.setBounds(30, 420, 40, 20);
fy2.setBounds(30, 440, 40, 20);
fV.setBounds(30, 460, 40, 20);
start.setActionCommand("start");
stop.setActionCommand("stop");
apply.setActionCommand("apply");
start.addActionListener(this);
stop.addActionListener(this);
apply.addActionListener(this);
start.setBounds(300, 430, 80, 20);
stop.setBounds(390, 430, 80, 20);
apply.setBounds(210, 430, 80, 20);
fr.add(this);
fr.add(start);
fr.add(stop);
fr.add(apply);
fr.add(forx1);
fr.add(fory1);
fr.add(forx2);
fr.add(fory2);
fr.add(forV);
fr.add(fx1);
fr.add(fy1);
fr.add(fx2);
fr.add(fy2);
fr.add(fV);
fr.setVisible(true);
}
#Override
protected void paintComponent(Graphics g) {
int width = getWidth();
int height = getHeight();
//System.out.println("width" + width);
// System.out.println("height" + height);
g.setColor(Color.yellow);
g.fillRect(0, 0, width, height);
Graphics2D g2d = (Graphics2D) g;
g2d.setStroke(new BasicStroke(3f));
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
//double x = 0.5 * width;
//double y = 0.5 * height;
double r = 0.75 * Math.min(x, y);
double dx,dy;
double t,l;
l=Math.sqrt(Math.pow(x2-x1,2)+Math.pow(y2-y1,2));
// System.out.println("!!l!!"+l);
t= l/V;
//System.out.println("!!t!!"+t);
g2d.setColor(Color.black);
if(T<t) {
dx = ((x2 - x1) / (Math.sqrt(Math.pow(x2 - x1, 2)) + Math.pow(y2 - y1, 2)));
//System.out.println("!!dx!!" + dx);
dy = ((y2 - y1) / (Math.sqrt(Math.pow(x2 - x1, 2)) + Math.pow(y2 - y1, 2)));
//System.out.println("!!dy!!" + dy);
x += x1 + dx * V * T;//+ dx * (V * T);
//System.out.println("!!x!!" + x);
//System.out.println("!!x1!!" + x1);
y += y1 + dy * V * T;// + dy * (V * T);
r = Math.max(0.1 * r, 5);
// System.out.println("!!y!!" + y);
//System.out.println("!!y1!!" + x1);
}
if (x==x2 && y == y2 && T>t) {
dx = ((x2 - x1) / (Math.sqrt(Math.pow(x2 - x1, 2)) + Math.pow(y2 - y1, 2)));
dy = ((y2 - y1) / (Math.sqrt(Math.pow(x2 - x1, 2)) + Math.pow(y2 - y1, 2)));
x -= x1 + dx * V * T;//+ dx * (V * T);
y -= y1 + dy * V * T;// + dy * (V * T);
r = Math.max(0.1 * r, 5);
}
g2d.fill(circle(x,y,r));
//if (x == x2 && y == y2)
// x = x1 -
}
public Shape circle(double x, double y, double R){
return new Ellipse2D.Double(x - r, y - r, 2 * r, 2 * r);
}
#Override
public void actionPerformed(ActionEvent e) {
switch (e.getActionCommand()) {
case "stop": {
timer.stop();
break;
}
case "start": {
timer.start();
break;
}
case "apply": {
float ax1, ay1, bx2, by2, cv;
try {
ax1 = Float.parseFloat(fx1.getText());
ay1 = Float.parseFloat(fy1.getText());
bx2 = Float.parseFloat(fx2.getText());
by2 = Float.parseFloat(fy2.getText());
cv = Float.parseFloat(fV.getText());
x1 = ax1;
y1 = ay1;
x2 = bx2;
y2 = by2;
V = cv;
repaint();
} catch (NumberFormatException ex) {
JOptionPane.showMessageDialog(null, "Invalid input", "Error",
JOptionPane.ERROR_MESSAGE);
}
break;
}
case "timer": {
T += 0.6;
System.out.println("!!T!!"+T);
repaint();
break;
}
}
}
}
The OP defined a task:
Ants move in one place in the region of their residence (for example,
[0; 0]) in a straight line with a speed V, and then turn back to the
point of their birth with the same speed.I have problems with the
moving of objects. The object must stop at the certain point and go
back to starting point.
And then he asked?
How should I fix my code?
It's too late. There's too many lines of code to debug and test.
So let's start over.
Here's the first iteration of the new code.
import javax.swing.SwingUtilities;
public class MovingAnts implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new MovingAnts());
}
public MovingAnts() {
}
#Override
public void run() {
// TODO Auto-generated method stub
}
}
We can test this code by running it and observing that it does not abend.
So, let's add a bit more code. We know we're going to have to define one or more ants. So, let's create an Ant class.
import java.awt.Point;
import java.util.ArrayList;
import java.util.List;
import javax.swing.SwingUtilities;
public class MovingAnts implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new MovingAnts());
}
private List<Ant> ants;
public MovingAnts() {
ants = new ArrayList<>();
Point origin = new Point(10, 10);
Point destination = new Point(200, 300);
Ant ant = new Ant(5.0d, origin, destination);
ants.add(ant);
}
#Override
public void run() {
// TODO Auto-generated method stub
}
public class Ant {
private final double velocity;
private Point position;
private final Point startPosition;
private final Point endPosition;
public Ant(double velocity, Point startPosition,
Point endPosition) {
this.velocity = velocity;
this.startPosition = startPosition;
this.endPosition = endPosition;
}
public double getVelocity() {
return velocity;
}
public Point getPosition() {
return position;
}
public void setPosition(Point position) {
this.position = position;
}
public Point getStartPosition() {
return startPosition;
}
public Point getEndPosition() {
return endPosition;
}
}
}
We've defined a velocity (speed), a starting position, and an ending position. According to the task description, these values don't change, so we can mark them final and define them in the constructor.
We've also defined a current position. The current position will be important later when it's time to draw the ant on a drawing JPanel.
We will probably add more to the Ant class as we develop more code. But for now, we have a class that holds the important variables for a ant.
We defined an ant (one instance of the Ant class) and saved the ant in a List<Ant> in the MovingAnts constructor. We can define more later, but let's start with one ant.
Now, we can create the JFrame and drawing JPanel for the ants.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class MovingAnts implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new MovingAnts());
}
private Dimension drawingPanelSize;
private DrawingPanel drawingPanel;
private List<Ant> ants;
public MovingAnts() {
drawingPanelSize = new Dimension(400, 400);
ants = new ArrayList<>();
Point origin = new Point(10, 10);
Point destination = new Point(200, 300);
Ant ant = new Ant(5.0d, origin, destination);
ants.add(ant);
}
#Override
public void run() {
JFrame frame = new JFrame("Moving Ants");
frame.setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE);
drawingPanel = new DrawingPanel(
drawingPanelSize);
frame.add(drawingPanel, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
public DrawingPanel(Dimension drawingPanelSize) {
this.setPreferredSize(drawingPanelSize);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
}
}
public class Ant {
private final double velocity;
private Point position;
private final Point startPosition;
private final Point endPosition;
public Ant(double velocity, Point startPosition,
Point endPosition) {
this.velocity = velocity;
this.startPosition = startPosition;
this.endPosition = endPosition;
}
public double getVelocity() {
return velocity;
}
public Point getPosition() {
return position;
}
public void setPosition(Point position) {
this.position = position;
}
public Point getStartPosition() {
return startPosition;
}
public Point getEndPosition() {
return endPosition;
}
}
}
Notice how every method and class is short and to the point. No person can read and understand hundreds of lines of code in a single method.
We've added a little bit of code at a time and tested each bit of code by running the application. At his point, we have a GUI. We also don't have any abends. Both the GUI and the lack of abends are important.
We defined the size of the drawing panel. This is important. We don't care how big the JFrame is. We care how big the drawing JPanel is, so we can keep the ants within the bounds of the drawing panel.
We haven't put any code in the paintComponent method of the drawing panel yet. Before we can do that, we have to create an Animation class that will update the position of the ants.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class MovingAnts implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new MovingAnts());
}
private Animation animation;
private Dimension drawingPanelSize;
private DrawingPanel drawingPanel;
private List<Ant> ants;
public MovingAnts() {
drawingPanelSize = new Dimension(400, 400);
ants = new ArrayList<>();
Point origin = new Point(200, 200);
Point destination = new Point(300, 350);
Ant ant = new Ant(30.0d, origin, destination);
ants.add(ant);
}
#Override
public void run() {
JFrame frame = new JFrame("Moving Ants");
frame.setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE);
drawingPanel = new DrawingPanel(
drawingPanelSize);
frame.add(drawingPanel, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
animation = new Animation();
new Thread(animation).start();
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
public DrawingPanel(Dimension drawingPanelSize) {
this.setPreferredSize(drawingPanelSize);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.BLACK);
for (Ant ant : ants) {
Point position = ant.getPosition();
g.fillOval(position.x - 4,
position.y - 4, 8, 8);
}
}
}
public class Animation implements Runnable {
private volatile boolean running;
public Animation() {
this.running = true;
}
#Override
public void run() {
int fps = 20;
long delay = 1000L / fps;
while (running) {
calculateAntPosition(fps);
updateDrawingPanel();
sleep(delay);
}
}
private void calculateAntPosition(int fps) {
for (Ant ant : ants) {
ant.calculatePosition(fps);
// System.out.println(ant.getPosition());
}
}
private void updateDrawingPanel() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
drawingPanel.repaint();
}
});
}
private void sleep(long duration) {
try {
Thread.sleep(duration);
} catch (InterruptedException e) {
// Deliberately left empty
}
}
public synchronized void setRunning(
boolean running) {
this.running = running;
}
}
public class Ant {
private boolean returning;
private double totalDistance;
private double traveledDistance;
private double theta;
private final double velocity;
private Point position;
private final Point startPosition;
private final Point endPosition;
public Ant(double velocity, Point startPosition,
Point endPosition) {
this.velocity = velocity;
this.startPosition = startPosition;
this.position = startPosition;
this.endPosition = endPosition;
this.returning = false;
this.theta = calculateTheta();
this.totalDistance = calculateTotalDistance();
this.traveledDistance = 0d;
}
private double calculateTheta() {
return Math.atan2((endPosition.y - startPosition.y),
endPosition.x - startPosition.x);
}
private double calculateTotalDistance() {
double diffX = endPosition.x - startPosition.x;
double diffY = endPosition.y - startPosition.y;
return Math.sqrt((diffX * diffX) + (diffY * diffY));
}
public double getVelocity() {
return velocity;
}
public Point getPosition() {
return position;
}
public void calculatePosition(int fps) {
double distance = velocity / fps;
double angle = theta;
if (returning) {
angle += Math.PI;
}
int x = (int) Math.round(
position.x + distance * Math.cos(angle));
int y = (int) Math.round(
position.y + distance * Math.sin(angle));
traveledDistance += distance;
if (traveledDistance > totalDistance) {
returning = !returning;
traveledDistance = 0d;
}
this.position = new Point(x, y);
}
public Point getStartPosition() {
return startPosition;
}
public Point getEndPosition() {
return endPosition;
}
}
}
I added way too much code this iteration, but we now have an ant that walks back and forth between two points.
The Animation class is a Runnable that runs in a Thread. You could use a Swing Timer, but it's easier for me to create the Runnable.
The Ant class grew some chest hair. All the trigonomic calculations can be found in the Ant class. Basically, I used polar coordinates to calculate the position of the ant.
The paintComponent method of the drawing panel simply draws the ants.
Every method and class is small and hopefully, easy to understand. Write short methods. Write short classes.
Hopefully, this code will provide a solid base for you to expand your project.
I was making a class for rectangles that are able to change angle, fill color, line color, line thickness. And the rectangles are to be rendered that way. I decided to do a little check up. During the check, I found out that the rectangles are rendered too slowly. I could see them being rendered from top to bottom. What might be the reason?
GEdit
import javax.swing.*;
public class GEdit {
public static void main(String[] args)
{
frame app = new frame();
app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
app.setSize(1000,1000);
app.setVisible(true);
}
}
Figure.java
import java.awt.*;
import java.awt.geom.Point2D;
public abstract class Figure
{
public static final int TOPLEFT = 0;
public static final int TOPCENTER = 1;
public static final int TOPRIGHT = 2;
public static final int CENTERLEFT = 3;
public static final int CENTER = 4;
public static final int CENTERRIGHT = 5;
public static final int BOTTOMLEFT = 6;
public static final int BOTTOMCENTER = 7;
public static final int BOTTOMRIGHT = 8;
private boolean fill_able;
private int fill_option;
private Color fill_color;
private int fill_transparency;
private Color line_color;
private float line_thickness;
private int line_transparency;
private int width;
private int height;
private float rotate_angle;
private boolean width_height_ratio_fixed;
private Point location;
private int base;
public Figure(boolean fill_able,
int fill_option,
Color fill_color,
int fill_transparency,
Color line_color,
float line_thickness,
int line_transparency,
int width, int height,
float rotate_angle,
boolean width_height_ratio_fixed,
int x, int y,
int base)
{
this.fill_able = fill_able;
this.fill_option = fill_option;
this.fill_color = fill_color;
this.fill_transparency = fill_transparency;
this.line_color = line_color;
this.line_thickness = line_thickness;
this.line_transparency = line_transparency;
this.width = width;
this.height = height;
this.rotate_angle = rotate_angle;
this.width_height_ratio_fixed = width_height_ratio_fixed;
this.location = new Point(x,y);
this.base = base;
}
public boolean is_fill_able()
{
return fill_able;
}
public int get_fill_option()
{
return fill_option;
}
public Color get_fill_color()
{
return fill_color;
}
public int get_fill_transparency()
{
return fill_transparency;
}
public Color get_line_color()
{
return line_color;
}
public float get_line_thickness()
{
return line_thickness;
}
public int get_line_transparency()
{
return line_transparency;
}
public int get_width()
{
return width;
}
public int get_height()
{
return height;
}
public float get_rotate_angle()
{
return rotate_angle;
}
public boolean is_width_height_ratio_fixed()
{
return width_height_ratio_fixed;
}
public Point get_location()
{
return location;
}
public Point get_render_location()
{
int x, y;
switch(base)
{
case TOPLEFT:
x = location.x;
y = location.y;
break;
case TOPCENTER:
x = location.x - (int)Math.round(width / 2);
y = location.y;
break;
case TOPRIGHT:
x = location.x - width;
y = location.y;
break;
case CENTERLEFT:
x = location.x;
y = location.y - (int)Math.round(height / 2);
break;
case CENTER:
x = location.x - (int)Math.round(width / 2);
y = location.y - (int)Math.round(height / 2);
break;
case CENTERRIGHT:
x = location.x - width;
y = location.y - (int)Math.round(height / 2);
break;
case BOTTOMLEFT:
x = location.x;
y = location.y - height;
break;
case BOTTOMCENTER:
x = location.x - (int)Math.round(width / 2);
y = location.y - height;
break;
case BOTTOMRIGHT:
x = location.x - width;
y = location.y - height;
break;
default:
x = 0; y = 0;
break;
}
return new Point(x, y);
}
public int get_base()
{
return base;
}
public void set_fill_option(int option)
{
this.fill_option = option;
}
public void set_fill_color(Color color)
{
this.fill_color = color;
}
public void set_fill_transparency(int transparency)
{
this.fill_transparency = transparency;
}
public void set_line_color(Color color)
{
this.line_color = color;
}
public void set_line_transparency(int transparency)
{
this.line_transparency = transparency;
}
public void set_width(int width)
{
if(this.width_height_ratio_fixed)
{
float ratio = this.height / this.width;
this.width = width;
this.height = (int)Math.round(width * ratio);
}
else { this.width = width;}
}
public void set_height(int height)
{
if(this.width_height_ratio_fixed)
{
float ratio = this.width / this.height;
this.height = height;
this.width = (int)Math.round(height * ratio);
}
else{ this.height = height;}
}
public void set_rotate_angle(float angle)
{
if(angle > 360)
{
this.rotate_angle = angle % 360;
}
else this.rotate_angle = angle;
}
public void set_location(int x, int y)
{
this.location.setLocation(x, y);
}
public void set_location(Point location)
{
this.location = location;
}
public void set_base(int base)
{
this.base = base;
}
abstract public void render(Graphics2D g);
}
Rectangle.java
import java.awt.*;
import java.awt.Graphics2D;
import java.awt.geom.*;
public class Rectangle extends Figure {
public Rectangle(Color fill_color, Color line_color, float line_thickness, int width, int height, int x, int y)
{
super(true,
0,
fill_color,
255,
line_color,
line_thickness,
255,
width, height,
0,
false,
x, y,
TOPLEFT);
}
public void render(Graphics2D g)
{
Rectangle2D rectangle = new Rectangle2D.Float(get_width(),get_height(),get_render_location().x,get_render_location().y);
g.rotate(Math.toRadians(get_rotate_angle()),rectangle.getCenterX(),rectangle.getCenterY());
if(is_fill_able())
{
Color color = new Color(
get_fill_color().getRed(),
get_fill_color().getGreen(),
get_fill_color().getBlue(),
get_fill_transparency());
g.setColor(color);
g.fill(rectangle);
}
Stroke old_stroke = g.getStroke();
g.setStroke(new BasicStroke(get_line_thickness()));
Color line_color = new Color(
get_line_color().getRed(),
get_line_color().getGreen(),
get_line_color().getBlue(),
get_line_transparency());
g.setColor(line_color);
g.draw(rectangle);
g.setStroke(old_stroke);
g.rotate(Math.toRadians(get_rotate_angle() * -1),rectangle.getCenterX(),rectangle.getCenterY());
}
}
frame.java
import javax.print.DocFlavor;
import javax.swing.JFrame;
import java.awt.*;
import java.util.Random;
public class frame extends JFrame{
public frame()
{
super("test");
}
#Override
public void paint(Graphics g) {
Graphics2D graphics2D = (Graphics2D)g;
RenderingHints renderingHints1 = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
RenderingHints renderingHints2 = new RenderingHints(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
graphics2D.setRenderingHints(renderingHints1);
graphics2D.setRenderingHints(renderingHints2);
Random random = new Random();
g.setColor(Color.WHITE);
g.fillRect(0,0,1000,1000);
for(int i =0;i < 10; i++)
{
Rectangle rectangle = new Rectangle(Color.CYAN,Color.BLACK,random.nextInt(10),random.nextInt(1000),random.nextInt(1000),random.nextInt(300),random.nextInt(300));
rectangle.set_fill_transparency(random.nextInt(255));
rectangle.set_rotate_angle(random.nextInt(180));
rectangle.render(graphics2D);
}
}
}
Don't override paint of top level containers like JFrame, start with something like JPanel and override it's paintComponent method - that way you get double buffering for free.
Consider not creating new objects on each paint cycle - this is create a number of short lived objects which can affect performance
You could perform some of the operations outside of the paint method (setting the properties) and just focus on painting the inside the paint method
Remember, transformations (such as rotate and translate) are compounding, this means they will affect everything painted after them. You either need to take a snapshot of the Graphics context before hand and dispose of it when you're finished, or reverse the transformations.
You might like to take a look at Performing Custom Painting and Painting in AWT and Swing for more details about how painting works in Swing.
Without changing anything, other then using a JPanel and paintComponent, it works fine.
I even threw in a Swing Timer and repainted the panel at 25fps a second without issue. And just for fun, I set it to 200fps without issue
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Rectangle2D;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Random random = new Random();
public TestPane() {
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
RenderingHints renderingHints1 = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
RenderingHints renderingHints2 = new RenderingHints(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHints(renderingHints1);
g2d.setRenderingHints(renderingHints2);
for (int i = 0; i < 10; i++) {
Rectangle rectangle = new Rectangle(Color.CYAN,
Color.BLACK,
random.nextInt(10),
random.nextInt(200),
random.nextInt(200),
random.nextInt(200), random.nextInt(200));
rectangle.set_fill_transparency(random.nextInt(255));
rectangle.set_rotate_angle(random.nextInt(180));
Graphics2D iDontTrustYou = (Graphics2D) g2d.create();
rectangle.render(iDontTrustYou);
iDontTrustYou.dispose();
}
g2d.dispose();
}
}
public abstract class Figure {
public static final int TOPLEFT = 0;
public static final int TOPCENTER = 1;
public static final int TOPRIGHT = 2;
public static final int CENTERLEFT = 3;
public static final int CENTER = 4;
public static final int CENTERRIGHT = 5;
public static final int BOTTOMLEFT = 6;
public static final int BOTTOMCENTER = 7;
public static final int BOTTOMRIGHT = 8;
private boolean fill_able;
private int fill_option;
private Color fill_color;
private int fill_transparency;
private Color line_color;
private float line_thickness;
private int line_transparency;
private int width;
private int height;
private float rotate_angle;
private boolean width_height_ratio_fixed;
private Point location;
private int base;
public Figure(boolean fill_able,
int fill_option,
Color fill_color,
int fill_transparency,
Color line_color,
float line_thickness,
int line_transparency,
int width, int height,
float rotate_angle,
boolean width_height_ratio_fixed,
int x, int y,
int base) {
this.fill_able = fill_able;
this.fill_option = fill_option;
this.fill_color = fill_color;
this.fill_transparency = fill_transparency;
this.line_color = line_color;
this.line_thickness = line_thickness;
this.line_transparency = line_transparency;
this.width = width;
this.height = height;
this.rotate_angle = rotate_angle;
this.width_height_ratio_fixed = width_height_ratio_fixed;
this.location = new Point(x, y);
this.base = base;
}
public boolean is_fill_able() {
return fill_able;
}
public int get_fill_option() {
return fill_option;
}
public Color get_fill_color() {
return fill_color;
}
public int get_fill_transparency() {
return fill_transparency;
}
public Color get_line_color() {
return line_color;
}
public float get_line_thickness() {
return line_thickness;
}
public int get_line_transparency() {
return line_transparency;
}
public int get_width() {
return width;
}
public int get_height() {
return height;
}
public float get_rotate_angle() {
return rotate_angle;
}
public boolean is_width_height_ratio_fixed() {
return width_height_ratio_fixed;
}
public Point get_location() {
return location;
}
public Point get_render_location() {
int x, y;
switch (base) {
case TOPLEFT:
x = location.x;
y = location.y;
break;
case TOPCENTER:
x = location.x - (int) Math.round(width / 2);
y = location.y;
break;
case TOPRIGHT:
x = location.x - width;
y = location.y;
break;
case CENTERLEFT:
x = location.x;
y = location.y - (int) Math.round(height / 2);
break;
case CENTER:
x = location.x - (int) Math.round(width / 2);
y = location.y - (int) Math.round(height / 2);
break;
case CENTERRIGHT:
x = location.x - width;
y = location.y - (int) Math.round(height / 2);
break;
case BOTTOMLEFT:
x = location.x;
y = location.y - height;
break;
case BOTTOMCENTER:
x = location.x - (int) Math.round(width / 2);
y = location.y - height;
break;
case BOTTOMRIGHT:
x = location.x - width;
y = location.y - height;
break;
default:
x = 0;
y = 0;
break;
}
return new Point(x, y);
}
public int get_base() {
return base;
}
public void set_fill_option(int option) {
this.fill_option = option;
}
public void set_fill_color(Color color) {
this.fill_color = color;
}
public void set_fill_transparency(int transparency) {
this.fill_transparency = transparency;
}
public void set_line_color(Color color) {
this.line_color = color;
}
public void set_line_transparency(int transparency) {
this.line_transparency = transparency;
}
public void set_width(int width) {
if (this.width_height_ratio_fixed) {
float ratio = this.height / this.width;
this.width = width;
this.height = (int) Math.round(width * ratio);
} else {
this.width = width;
}
}
public void set_height(int height) {
if (this.width_height_ratio_fixed) {
float ratio = this.width / this.height;
this.height = height;
this.width = (int) Math.round(height * ratio);
} else {
this.height = height;
}
}
public void set_rotate_angle(float angle) {
if (angle > 360) {
this.rotate_angle = angle % 360;
} else {
this.rotate_angle = angle;
}
}
public void set_location(int x, int y) {
this.location.setLocation(x, y);
}
public void set_location(Point location) {
this.location = location;
}
public void set_base(int base) {
this.base = base;
}
abstract public void render(Graphics2D g);
}
public class Rectangle extends Figure {
public Rectangle(Color fill_color, Color line_color, float line_thickness, int width, int height, int x, int y) {
super(true,
0,
fill_color,
255,
line_color,
line_thickness,
255,
width, height,
0,
false,
x, y,
TOPLEFT);
}
public void render(Graphics2D g) {
Rectangle2D rectangle = new Rectangle2D.Float(get_width(), get_height(), get_render_location().x, get_render_location().y);
g.rotate(Math.toRadians(get_rotate_angle()), rectangle.getCenterX(), rectangle.getCenterY());
if (is_fill_able()) {
Color color = new Color(
get_fill_color().getRed(),
get_fill_color().getGreen(),
get_fill_color().getBlue(),
get_fill_transparency());
g.setColor(color);
g.fill(rectangle);
}
Stroke old_stroke = g.getStroke();
g.setStroke(new BasicStroke(get_line_thickness()));
Color line_color = new Color(
get_line_color().getRed(),
get_line_color().getGreen(),
get_line_color().getBlue(),
get_line_transparency());
g.setColor(line_color);
g.draw(rectangle);
g.setStroke(old_stroke);
g.rotate(Math.toRadians(get_rotate_angle() * -1), rectangle.getCenterX(), rectangle.getCenterY());
}
}
}
A slightly better optimised version might look something like...
public class TestPane extends JPanel {
private Random random = new Random();
private List<Rectangle> rectangles = new ArrayList<>(25);
public TestPane() {
for (int i = 0; i < 1000; i++) {
Rectangle rectangle = new Rectangle(Color.CYAN,
Color.BLACK,
random.nextInt(10),
random.nextInt(200),
random.nextInt(200),
random.nextInt(200), random.nextInt(200));
rectangle.set_fill_transparency(random.nextInt(255));
rectangle.set_rotate_angle(random.nextInt(180));
rectangles.add(rectangle);
}
Timer timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (Rectangle rectangle : rectangles) {
rectangle.set_height(random.nextInt(200));
rectangle.set_width(random.nextInt(200));
rectangle.set_location(random.nextInt(200), random.nextInt(200));
rectangle.set_fill_transparency(random.nextInt(255));
rectangle.set_rotate_angle(random.nextInt(180));
}
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
RenderingHints renderingHints1 = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
RenderingHints renderingHints2 = new RenderingHints(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHints(renderingHints1);
g2d.setRenderingHints(renderingHints2);
for (Rectangle rectangle : rectangles) {
Graphics2D iDontTrustYou = (Graphics2D) g2d.create();
rectangle.render(iDontTrustYou);
iDontTrustYou.dispose();
}
g2d.dispose();
}
}
If you still find that the performance isn't up to what you want, you might consider having a look at BufferStrategy which will give you complete control over the painting process
You are overriding the method paint, which is usually a bad idea.
You are also calculating all the information needed DURING the paint, which is another bad idea: they should be calculated before. If you need 4x10 random numbers, maybe calculate them before the paint and store the 40 numbers in a int[] array.
As someone mentioned, you should want to use a JPanel and the method paintComponent.
The following link might be an interesting reading for you:
http://www.billharlan.com/papers/Improving_Swing_Performance.html
I am wring the bouncing ball program in java. And I Now have one bouncing ball, I would like to have at least five bouncing balls. I have tried a few ways to do it, however, I only end up with one ball or error.
Do you have any suggestions on how to proceed? This in the piece of code used for the one ball, is it possible to rewrite this piece of code to get multiple balls in a neat way?
import javafx.scene.shape.Rectangle;
public class World {
private final double width, height;
private Ball[] balls;
private final Rectangle pad;
public World(double width, double height) {
this.width = width;
this.height = height;
balls = new Ball[1];
balls[0] = new Ball(10, 10);
balls[0].setVelocity(75.0, 100.0);
pad = new Rectangle(width / 2, 0.9 * height,
width / 8, height / 32);
}
public void move(long elapsedTimeNs) {
balls[0].move(elapsedTimeNs);
constrainBall(balls[0]);
checkForCollisionWithPad(balls[0]);
}
public Ball[] getBalls() {
return (Ball[]) balls.clone();
}
public Rectangle getPad() {
return pad;
}
public void setPadX(double x) {
if (x > width) {
x = width;
}
if (x < 0) {
x = 0;
}
pad.setX(x);
}
private void constrainBall(Ball ball) {
double x = ball.getX(), y = ball.getY();
double dx = ball.getDx(), dy = ball.getDy();
double radius = ball.getRadius();
if (x < radius) {
dx = Math.abs(dx);
} else if (x > width - radius) {
dx = -Math.abs(dx);
}
if (y < radius) {
dy = Math.abs(dy);
} else if (y > height - radius) {
dy = -Math.abs(dy);
}
ball.setVelocity(dx, dy);
}
private void checkForCollisionWithPad(Ball ball) {
if (ball.intersectsArea(
pad.getX(), pad.getY(), pad.getWidth(), pad.getHeight())) {
double dx = ball.getDx();
// set dy negative, i.e. moving "up"
double newDy = -Math.abs(ball.getDy());
ball.setVelocity(dx, newDy);
}
}
}
Main
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Alert;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Bounce extends Application {
private World world;
private Canvas canvas;
private AnimationTimer timer;
protected class BounceTimer extends AnimationTimer {
private long previousNs = 0;
#Override
public void handle(long nowNs) {
if (previousNs == 0) {
previousNs = nowNs;
}
world.move(nowNs - previousNs);
previousNs = nowNs;
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setFill(Color.WHITESMOKE);
gc.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
Rectangle pad = world.getPad();
gc.setFill(Color.BLACK);
double x = pad.getX(), y = pad.getY(),
w = pad.getWidth(), h = pad.getHeight();
gc.fillRoundRect(x, y, w, h, h, h);
for (Ball b : world.getBalls()) {
b.paint(gc);
}
}
}
#Override
public void start(Stage stage) {
Group root = new Group();
Scene scene = new Scene(root, 300, 300, Color.WHITESMOKE);
canvas = new Canvas(scene.getWidth(), scene.getHeight());
root.getChildren().add(canvas);
stage.setTitle("Bounce");
stage.setScene(scene);
stage.setResizable(false);
stage.sizeToScene();
stage.show();
world = new World(canvas.getWidth(), canvas.getHeight());
timer = new BounceTimer();
timer.start();
canvas.addEventHandler(MouseEvent.MOUSE_DRAGGED,
new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent me) {
world.setPadX(me.getX());
}
});
}
public static void main(String[] args) {
launch(args);
}
private void showAlert(String message) {
alert.setHeaderText("");
alert.setTitle("Alert!");
alert.setContentText(message);
alert.show();
}
private final Alert alert = new Alert(Alert.AlertType.INFORMATION);
}
Ball
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
public class Ball {
public static final double BILLION = 1_000_000_000.0;
private double x, y; // position of the balls center
private double dx, dy; // velocity measured in pixels/second
private double radius;
private Color color;
public Ball(double x0, double y0) {
x = x0;
y = y0;
radius = 10;
color = Color.MAGENTA;
}
public Ball(double x0, double y0, double rad, Color col) {
x = x0;
y = y0;
radius = rad;
color = col;
}
Ball(int i, int i0, Color BLUEVIOLET) {
throw new UnsupportedOperationException("Not supported yet.");
}
public void setColor(Color col) { // setColor
color = col; }
public double getX() {
return x;
}
public double getY() {
return y;
}
public void setX(double newX) {
x = newX;
}
public void setY(double newY) {
y = newY;
}
public double getRadius() {
return radius;
}
public double getDx() {
return dx;
}
public double getDy() {
return dy;
}
public void setVelocity(double newDx, double newDy) {
dx = newDx;
dy = newDy;
}
public void moveTo(double newX, double newY) {
x = newX;
y = newY;
}
public void move(long elapsedTimeNs) {
x += dx * elapsedTimeNs / BILLION;
y += dy * elapsedTimeNs / BILLION;
}
public void paint(GraphicsContext gc) {
gc.setFill(color);
// arguments to fillOval: see the javadoc for GraphicsContext
gc.fillOval(x - radius, y - radius, radius * 2, radius * 2);
}
public boolean intersectsArea(
double rectX, double rectY,
double rectWidth, double rectHeight) {
double closestX = clamp(x, rectX, rectX + rectWidth);
double closestY = clamp(y, rectY, rectY + rectHeight);
double distanceX = x - closestX;
double distanceY = y - closestY;
return (distanceX * distanceX) + (distanceY * distanceY)
< (radius * radius);
}
private double clamp(double value, double lower, double upper) {
if (value < lower) {
return lower;
}
if (value > upper) {
return upper;
}
return value;
}
}
As Stormblessed said, you are only targeting one ball in your move method.
You should do:
public void move(Ball ball, long elapsedTimeNs) {
ball.move(elapsedTimeNs);
constrainBall(ball);
checkForCollisionWithPad(ball);
}
Edit: Since you want the handler method to accept only the elapsedTimeNs argument, do:
public void move(long elapsedTimeNs) {
for (Ball ball : balls) {
ball.move(elapsedTimeNs);
constrainBall(ball);
checkForCollisionWithPad(ball);
}
}
Edit 2: You should probably have a method that creates a new ball, for convenience:
public Ball newBall(double x, double y, double velocity1, double velocity2) {
Ball tmp = new Ball(x, y);
tmp.setVelocity(velocity1, velocity2);
balls.add(tmp);
return tmp;
}
Edit 3: The reason it throws an error is that you designated balls to have only one index position by using balls = new Ball[1]. You should use an ArrayList (java.util.ArrayList) instead, like so:
import java.util.ArrayList;
ArrayList<Ball> balls = new ArrayList<>;
You should now use balls.add and balls.get instead of = and []. References have been updated accordingly.
Main class this is main class of my program
public class Main {
public static void main(String[] args) {
BallWorld frm = new BallWorld(3);
frm.setVisible(true);
for (int i=0; i<1000; i++){
frm.stepTheBall();
}
}
}
BallWorld.java class this class related with JFrame
import java.awt.Graphics;
import java.awt.Point;
import javax.swing.JFrame;
public class BallWorld extends JFrame {
public final int FRAMEWIDTH = 600;
public final int FRAMEHEIGHT = 400;
private Ball[] ballArr;
private int ballCnt;
public BallWorld(int ballCnt){
super();
setSize(FRAMEWIDTH, FRAMEHEIGHT);
setTitle("My Bouncing Ball Application");
ballArr = new Ball[ballCnt];
this.ballCnt = ballCnt;
for (int i=0; i < ballCnt; i++){
ballArr[i] = new Ball(new Point(50,50), 5);
int ddx = (int) (5*Math.random()); //Exercise 1
int ddy = (int) (4*Math.random()); //Exercise 1
ballArr[i].setMotion(ddx, ddy);
}
}
public void paint(Graphics g){
super.paint(g);
for (int i=0; i < ballCnt; i++){
ballArr[i].paint(g);
}
}
public void stepTheBall(){
for (int i=0; i < ballCnt; i++){
ballArr[i].move();
Point loc = ballArr[i].getLocation();
if (loc.x < ballArr[i].getRadius() ||
loc.x > FRAMEWIDTH-ballArr[i].getRadius()){
ballArr[i].reclectVert();
}
if (loc.y < ballArr[i].getRadius() ||
loc.y > FRAMEHEIGHT-ballArr[i].getRadius()){
ballArr[i].reclectHoriz();
}
}
repaint();
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Ball.java class this class related with balls information
public class Ball {
private Point location;
private int radius;
private Color color;
private int dx, dy;
public Ball(Point l, int r, Color c){
location = l;
radius = r;
color = c;
}
public Ball(Point l, int r){
location = l;
radius = r;
color = Color.RED;
}
public Point getLocation() {
return location;
}
public void setLocation(Point location) {
this.location = location;
}
public int getRadius() {
return radius;
}
public void setRadius(int radius) {
this.radius = radius;
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public void setMotion(int dx, int dy){
this.dx = dx;
this.dy = dy;
}
public void move(){
location.translate(dx, dy);
}
public void moveTo(int x, int y){
location.move(x, y);
}
public void paint (Graphics g) {
g.setColor (color);
g.fillOval (location.x-radius, location.y-radius, 2*radius, 2*radius);
}
public void reclectHoriz() {
dy = -dy;
}
public void reclectVert() {
dx = -dx;
}
}
I want to add yellow,blue,red balls and different radius that includes. How can i write that informations
These are the lines at fault:
ballArr[i] = new Ball(new Point(50,50), 5);
Here, you call the two-argument constructor from Ball. It looks like this:
public Ball(Point l, int r){
location = l;
radius = r;
color = Color.RED;
}
So your balls will all be red, have a radius of 5 and be at position 50,50. You do have a three-argument constructor that also sets the color of a ball. If you want these things to be random, use a Random object, select a random color, radius and point for each ball, and there you go.
It should be clear how you get random numbers for radius and point. In case you're wondering about Color, here's a way: Define an array containing Color objects.
Color[] colors = {Color.Red, Color.Blue, Color.Yellow};
Get a random number based on the size of the array,
int colornumber = random.nextInt(colors.length);
and retrieve the color
Color c = colors[colornumber]
Then, create balls with random properties.
EDIT
public class BallWorld {
....
private Random random = new Random();
private Color[] colors={Color.red,Color.blue,Color.yellow};
public BallWorld(int ballCnt){
super();
setSize(FRAMEWIDTH, FRAMEHEIGHT);
setTitle("My Bouncing Ball Application");
ballArr = new Ball[ballCnt];
this.ballCnt = ballCnt;
for (int i=0; i < ballCnt; i++){
----> // Create attributes here
int bcn = random.nextInt(colors.length);
Color ballcolor = colors[bcn];
int ballradius = random.nextInt(10); // change to suit your needs
----> int posx = random.nextInt(200); // change to suit your needs
----> int posy = random.nextInt(200); // change to suit your needs
// this creates a ball given the above calculated parameters
----> ballArr[i] = new Ball(new Point(posx,posy), ballradius, ballcolor);
int ddx = (int) (5*Math.random()); //Exercise 1
int ddy = (int) (4*Math.random()); //Exercise 1
ballArr[i].setMotion(ddx, ddy);
}
}
Instead of this:
ballArr[i] = new Ball(new Point(50,50), 5);
use another constructor:
ballArr[i] = new Ball(new Point(50,50), 5, Color.xxx);