I have my program running where I have a variable radius so that the balls can be of a size int, but I would like to know how to make this program more complex with adding random sizes of balls. small,medium,big etc. On line 148 I tried changing it to int radius = (int)(5*Math.random); but it did not work. Must I do something else to my code in order for this to work? And if there are any other hints you may have, they'd be much appreciated. Thanks
import javax.swing.Timer;
import java.util.ArrayList;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class MultipleBallApp extends JApplet
{
public MultipleBallApp()
{
add(new BallControl());
}
class BallControl extends JPanel {
private BallPanel ballPanel = new BallPanel();
private JButton jbtSuspend = new JButton("Suspend");
private JButton jbtResume = new JButton("Resume");
private JButton jbtAdd = new JButton("+1");
private JButton jbtSubtract = new JButton("-1");
private JScrollBar jsbDelay = new JScrollBar();
public BallControl() {
// Group buttons in a panel
JPanel panel = new JPanel();
panel.add(jbtSuspend);
panel.add(jbtResume);
panel.add(jbtAdd);
panel.add(jbtSubtract);
// Add ball and buttons to the panel
ballPanel.setBorder(new javax.swing.border.LineBorder(Color.red));
jsbDelay.setOrientation(JScrollBar.HORIZONTAL);
ballPanel.setDelay(jsbDelay.getMaximum());
setLayout(new BorderLayout());
add(jsbDelay, BorderLayout.NORTH);
add(ballPanel, BorderLayout.CENTER);
add(panel, BorderLayout.SOUTH);
// Register listeners
jbtSuspend.addActionListener(new Listener());
jbtResume.addActionListener(new Listener());
jbtAdd.addActionListener(new Listener());
jbtSubtract.addActionListener(new Listener());
jsbDelay.addAdjustmentListener(new AdjustmentListener()
{
#Override
public void adjustmentValueChanged(AdjustmentEvent e) {
ballPanel.setDelay(jsbDelay.getMaximum() - e.getValue());
}
});
}
class Listener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == jbtSuspend)
ballPanel.suspend();
else if (e.getSource() == jbtResume)
ballPanel.resume();
else if (e.getSource() == jbtAdd)
ballPanel.add();
else if (e.getSource() == jbtSubtract)
ballPanel.subtract();
}
}
}
class BallPanel extends JPanel
{
private int delay = 10;
private ArrayList<Ball> list = new ArrayList<Ball>();
// Create a timer with the initial dalay
protected Timer timer = new Timer(delay, new ActionListener() {
#Override /** Handle the action event */
public void actionPerformed(ActionEvent e)
{
repaint();
}
});
public BallPanel()
{
timer.start();
}
public void add()
{
list.add(new Ball());
}
public void subtract()
{
if (list.size() > 0)
list.remove(list.size() - 1); // Remove the last ball
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
for (int i = 0; i < list.size(); i++)
{
Ball ball = (Ball)list.get(i); // Get a ball
g.setColor(ball.color); // Set ball color
// Check boundaries
if (ball.x < 0 || ball.x > getWidth())
ball.dx = -ball.dx;
if (ball.y < 0 || ball.y > getHeight())
ball.dy = -ball.dy;
// Adjust ball position
ball.x += ball.dx;
ball.y += ball.dy;
g.fillOval(ball.x - ball.radius, ball.y - ball.radius,
ball.radius * 2, ball.radius * 2);
}
}
public void suspend()
{
timer.stop();
}
public void resume()
{
timer.start();
}
public void setDelay(int delay)
{
this.delay = delay;
timer.setDelay(delay);
}
}
class Ball
{
int x = 0;
int y = 0; // Current ball position
int dx = 10; // Increment on ball's x-coordinate
int dy = 10; // Increment on ball's y-coordinate
int radius = 5; // Ball radius
Color color = new Color((int)(Math.random() * 256),
(int)(Math.random() * 256), (int)(Math.random() * 256));
}
/** Main method */
public static void main(String[] args)
{
JFrame frame = new JFrame();
JApplet applet = new MultipleBallApp();
frame.add(applet);
frame.setTitle("MultipleBallApp");
frame.setLocationRelativeTo(null); // Center the frame
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 200);
frame.setLocationRelativeTo(null); // Center the frame
frame.setVisible(true);
}
}
Make sure you do not get 0 as a radius when trying to randomize the ball's radius.
int radius = (int)(4*Math.random()+1);
Related
Im trying to code a simple 2D game. I have a Square that you can move with a/w/d. I got jumping working with a sort of gravity but now I cant get myself to land on a platform. I know how to detect collision between two things but even still idk what to do. My gravity always pulls me down until I reach groundLevel which is part of the problem. Here is my code so far. There's a lot of experimenting happing so its pretty messy.
import javax.swing.Timer;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.geom.*;
/**
* Custom Graphics Example: Using key/button to move a line left or right.
*/
public class PlatformingGame extends JFrame implements ActionListener, KeyListener{
// Name-constants for the various dimensions
public static final int CANVAS_WIDTH = 800;
public static final int CANVAS_HEIGHT = 400;
public static final Color LINE_COLOR = Color.BLACK;
public static final Color CANVAS_BACKGROUND = Color.WHITE;
public int GRAVITY = 10;
public int TERMINAL_VELOCITY = 300;
public int vertical_speed = 0;
public int jumpSpeed;
public int groundLevel = CANVAS_HEIGHT;
int Shapes;
private DrawCanvas canvas; // the custom drawing canvas (extends JPanel)
public enum STATE {
PLAYING,
PAUSED,
ONGROUND,
INAIR
};
JButton btnStartRestat, btnExit;
Timer timer;
Rectangle2D.Double guy, platform, platform2;
Shape[] shapeArr = new Shape[10];
int dx, dy;
/** Constructor to set up the GUI */
ArrayList myKeys = new ArrayList<Character>();
public static STATE gameState = STATE.PAUSED;
//public static STATE playerState = STATE.ONGROUND;
public PlatformingGame() {
dx = 3;
dy = 3;
guy = new Rectangle2D.Double( CANVAS_WIDTH/2 - 20, CANVAS_HEIGHT, 30, 20);
platform2 = new Rectangle2D.Double( CANVAS_WIDTH/4, CANVAS_HEIGHT/2+130, 50, 10);
platform = new Rectangle2D.Double( CANVAS_WIDTH/3, CANVAS_HEIGHT/2+50, 50, 10);
timer = new Timer(10, this);
// Set up a panel for the buttons
JPanel btnPanel = new JPanel();
// btnPanel.setPreferredSize(new Dimension(CANVAS_WIDTH/2, CANVAS_HEIGHT));
btnPanel.setLayout(new FlowLayout());
btnStartRestat = new JButton("Start/Restart");
btnExit = new JButton("Exit");
btnPanel.add(btnStartRestat);
btnPanel.add(btnExit);
btnStartRestat.addActionListener(this);
btnExit.addActionListener(this);
// Set up a custom drawing JPanel
canvas = new DrawCanvas();
canvas.setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT));
// Add both panels to this JFrame
Container cp = getContentPane();
cp.setLayout(new BorderLayout());
cp.add(canvas, BorderLayout.CENTER);
cp.add(btnPanel, BorderLayout.SOUTH);
// "this" JFrame fires KeyEvent
addKeyListener(this);
requestFocus(); // set the focus to JFrame to receive KeyEvent
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Handle the CLOSE button
pack(); // pack all the components in the JFrame
setVisible(true); // show it
}
public void generateSpikes(){
Rectangle2D.Double spikes = null;
for (int i = 0; i < 10; i++) {
spikes = new Rectangle2D.Double (CANVAS_WIDTH - 300 + i*20 , CANVAS_HEIGHT - 30 , 20, 30);
shapeArr[i] = spikes;
}
}
public void actionPerformed(ActionEvent e)
{
if (e.getSource()==btnStartRestat)
{
generateSpikes();
dx = 3;
dy = 3;
guy = new Rectangle2D.Double( 100, CANVAS_HEIGHT - guy.height, 15, 30);
platform = new Rectangle2D.Double( CANVAS_WIDTH/3, CANVAS_HEIGHT/2+50, 50, 10);
platform2 = new Rectangle2D.Double( CANVAS_WIDTH/4, CANVAS_HEIGHT/2+130, 50, 10);
gameState = STATE.PLAYING;
}
else if (e.getSource()==btnExit)
{
// requestFocus(); // change the focus to JFrame to receive KeyEvent
}
else if (e.getSource()== timer){
if (isHitDetected(platform2, guy)){
// playerState = STATE.ONGROUND;
guy.y = platform2.y;
}
if (myKeys.contains('a')
&& (guy.x > 0)){
guy.x = guy.x - 5; }
if (myKeys.contains('d')
&& (guy.x < CANVAS_WIDTH - guy.width)){
guy.x = guy.x + 5; }
{
updateGuyPosition();
}
requestFocus();
canvas.repaint();
}
}
public void updateGuyPosition(){
double guyHeight = guy.height;
if (guy.x >= CANVAS_WIDTH - guy.width){
}
if(gameState == STATE.PLAYING) {
guy.y += jumpSpeed;
if (guy.y < groundLevel - guyHeight) {
// if(playerState == STATE.INAIR) {
jumpSpeed += 1;
}
// }
else {
// if(playerState == STATE.INAIR) {
//playerState = STATE.ONGROUND;
jumpSpeed = 0;
guy.y = groundLevel - guyHeight;
}
// }
if (myKeys.contains('w') == true && guy.y == groundLevel - guyHeight) {
jumpSpeed = -15;
// playerState = STATE.INAIR;
}
}
}
public static boolean isHitDetected(Shape shapeA, Shape shapeB) {
Area areaA = new Area(shapeA);
areaA.intersect(new Area(shapeB));
return !areaA.isEmpty();
}
public void keyPressed(KeyEvent evt) {
if (!myKeys.contains(evt.getKeyChar())){
myKeys.add(evt.getKeyChar());
}
}
public void keyReleased(KeyEvent evt) {
myKeys.remove(myKeys.indexOf(evt.getKeyChar()));
}
public void keyTyped(KeyEvent evt) {
}
/** The entry main() method */
public static void main(String[] args) {
PlatformingGame myProg = new PlatformingGame(); // Let the constructor do the job
myProg.timer.start();
}
/**
* DrawCanvas (inner class) is a JPanel used for custom drawing
*/
class DrawCanvas extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
setBackground(CANVAS_BACKGROUND);
g.setColor(LINE_COLOR);
Graphics2D g2d = (Graphics2D)g;
if(gameState == STATE.PLAYING) {
g2d.fill(guy);
g.setColor(Color.lightGray);
g2d.fill(platform);
g2d.fill(platform2);
for (int i = 0; i < 10; i++) {
g2d.fill(shapeArr[i]);
}
}
if(gameState == STATE.PAUSED) {
g.drawString("Game Paused", CANVAS_WIDTH/2, CANVAS_HEIGHT/2);
}
}
}
}
So this is what I currently have. After I click the button, it should bounce from center to right and then left and then back to its original position. Then I should be able to click the button again so that it would start another cycle.
public class Bounce extends JFrame {
private static JButton btnMovement = new JButton("Click");
private Container container;
private Timer timer;
private int x = 290;
private int y = 350;
private int radius = 100;
private int moves = 2;
public Bounce() {
container = getContentPane();
container.setLayout(new FlowLayout());
final MoveListener ml = new MoveListener();
btnMovement.addActionListener(ml);
timer = new Timer(5, ml);
}
private void Move() {
x += moves;
if (x + (radius * 2) > getWidth()) {
x = getWidth() - (radius * 2);
moves *= -1;
} else if (x < 0) {
x = 0;
moves *= -1;
}
repaint();
}
class MoveListener implements ActionListener {
public void actionPerformed(final ActionEvent event) {
if (!timer.isRunning()){
timer.start();
} else if (timer.isRunning() && x == 290 && y == 350){ // I don't know what condition to put
timer.stop();
}
Move();
}
}
public void paint (Graphics g){
super.paint(g);
g.setColor(Color.black);
g.fillOval(x - 5, y - radius - 5, radius + 110, radius + 110);
g.setColor(Color.red);
g.fillOval(x, y - radius, radius * 2, radius * 2);
}
public static void main(String args[]){
final JFrame window = new Bounce();
window.add(btnMovement);
window.setSize(800, 800);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
}
}
The ActionListener for the Timer should be seperate - it acts as pseudo loop. Basically, once the ball bounces of the left side, you change a flag to indicate that it should stop once it reaches or passes the mid point, for example...
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Timer timer;
private int xPos;
private int yPos;
public TestPane() {
JButton btn = new JButton("Start");
xPos = 95;
yPos = 95;
timer = new Timer(5, new ActionListener() {
private int xDelta = 1;
private boolean hasBounced = false;
#Override
public void actionPerformed(ActionEvent e) {
xPos += xDelta;
int middleX = (getWidth() / 2) - 5;
if (xPos + 10 > getWidth()) {
xPos = getWidth() - 10;
xDelta *= -1;
} else if (xPos < 0) {
xPos = 0;
xDelta *= -1;
hasBounced = true;
} else if (hasBounced && xPos >= middleX) {
timer.stop();
btn.setEnabled(true);
hasBounced = false;
}
repaint();
}
});
setLayout(new BorderLayout());
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
btn.setEnabled(false);
timer.start();
}
});
add(btn, BorderLayout.SOUTH);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawOval(xPos, yPos, 10, 10);
g2d.dispose();
}
}
}
A more complicated solution would be to record the current position as the timer starts and use that as the "end point", but I'll leave that up to you
http://pastebin.com/PXLFJjNG
I'm trying to figure out threading and animations so I made this really basic program where you can press buttons to move a rectangle up and down. The problem is that if you keep pressing the UP button till the rectangle reaches the top of the screen there's suddenly an extra blue rectangle.
This problem is kinda hard to explain just run the program and keep pressing UP and when you get to the top there's suddenly 2 rectangles being drawn. Is it a problem with my paint component?
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class RectangleMovement implements Runnable {
JFrame frame;
MyPanel panel;
private int x = 100;
private int y = 100;
private int playerX = 400;
private int playerY = 350;
private int horizontalGoal = 0;
private int verticalGoal = 0;
public static void main(String[] args) {
new RectangleMovement().go();
}
private void go() {
frame = new JFrame("Worst Game Ever");
panel = new MyPanel();
MyPanel buttonPanel = new MyPanel();
JButton left = new JButton("Left");
JButton right = new JButton("Right");
JButton down = new JButton("Down");
JButton up = new JButton("Up");
left.addActionListener(new LeftButton());
right.addActionListener(new RightButton());
down.addActionListener(new DownButton());
up.addActionListener(new UpButton());
buttonPanel.setLayout(new FlowLayout());
buttonPanel.add(left);
buttonPanel.add(down);
buttonPanel.add(up);
buttonPanel.add(right);
frame.getContentPane().add(BorderLayout.CENTER, panel);
frame.getContentPane().add(BorderLayout.SOUTH, buttonPanel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setSize(500, 500);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Thread player = new Thread(this);
player.start();
animate();
}
private class MyPanel extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
public void paintComponent(Graphics g) {
g.setColor(Color.RED);
g.fillRect(x, y, 10, 10);
g.setColor(Color.BLUE);
g.fillRect(playerX, playerY, 10, 10);
}
}
class LeftButton implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
horizontalGoal = -5;
}
}
class RightButton implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
horizontalGoal = 5;
}
}
class UpButton implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
verticalGoal = -5;
}
}
class DownButton implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
verticalGoal = 5;
}
}
/*
* Animates the player
*/
public void run() {
while (true) {
if (horizontalGoal < 0 && playerX > 0) {
playerX--;
horizontalGoal++;
} else if (horizontalGoal > 0 && playerX + 20 < frame.getWidth()) {
playerX++;
horizontalGoal--;
}
if (verticalGoal < 0 && playerY > 0) {
playerY--;
verticalGoal++;
} else if (verticalGoal > 0 && playerY + 10 < 400) {
playerY++;
verticalGoal--;
}
try {
Thread.sleep(15);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (intersects()){
playerX = 400;
playerY = 350;
}
frame.repaint();
}
}
private void animate() {
boolean direction = true;
while (true) {
if (y <= 100) {
direction = true;
} else if (y >= 400) {
direction = false;
}
if (direction) {
y++;
} else {
y--;
}
try {
Thread.sleep(15);
} catch (InterruptedException e) {
e.printStackTrace();
}
frame.repaint();
}
}
private boolean intersects() {
if (x <= playerX && playerX <= x + 10 && y <= playerY
&& playerY <= y + 10) {
return true;
}
return false;
}
}
Your buttonPanel is the Problem. It is also a MyPanel which uses your own paintComponent method. So if your player should be drawn playerY = 10. It is drawn on your game panel and your buttonPanel.
Change your ButtonPanel from
MyPanel buttonPanel = new MyPanel();
to
Panel buttonPanel = new Panel();
and your probelm is gone :-)
I want to learn some tricks about JAVA for my project.
I want to animate my Rectangle leftoright and righttoleft but I can't apply the same functions for ball animation.
In addition,how can I start my ball in different x-direction with a border of y-coordinate ?
Thanks a lot for your advices and helping.
My codes:
import javax.swing.Timer;
import java.util.ArrayList;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class MultipleBall extends JApplet {
public MultipleBall() {
add(new BallControl());
}
class BallControl extends JPanel {
private BallPanel ballPanel = new BallPanel();
private JButton Suspend = new JButton("Suspend");
private JButton Resume = new JButton("Resume");
private JButton Add = new JButton("+1");
private JButton Subtract = new JButton("-1");
private JScrollBar Delay = new JScrollBar();
public BallControl() {
// Group buttons in a panel
JPanel panel = new JPanel();
panel.add(Suspend);
panel.add(Resume);
panel.add(Add);
panel.add(Subtract);
// Add ball and buttons to the panel
ballPanel.setBorder(new javax.swing.border.LineBorder(Color.red));
Delay.setOrientation(JScrollBar.HORIZONTAL);
ballPanel.setDelay(Delay.getMaximum());
setLayout(new BorderLayout());
add(Delay, BorderLayout.NORTH);
add(ballPanel, BorderLayout.CENTER);
add(panel, BorderLayout.SOUTH);
// Register listeners
Suspend.addActionListener(new Listener());
Resume.addActionListener(new Listener());
Add.addActionListener(new Listener());
Subtract.addActionListener(new Listener());
Delay.addAdjustmentListener(new AdjustmentListener() {
public void adjustmentValueChanged(AdjustmentEvent e) {
ballPanel.setDelay(Delay.getMaximum() - e.getValue());
}
});
}
class Listener implements ActionListener {
public void actionPerformed(ActionEvent e) {
if (e.getSource() == Suspend)
ballPanel.suspend();
else if (e.getSource() == Resume)
ballPanel.resume();
else if (e.getSource() == Add)
ballPanel.add();
else if (e.getSource() == Subtract)
ballPanel.subtract();
}
}
}
class BallPanel extends JPanel {
private int delay = 30;
private ArrayList<Ball> list = new ArrayList<Ball>();
// Create a timer with the initial delay
protected Timer timer = new Timer(delay, new ActionListener() {
/** Handle the action event */
public void actionPerformed(ActionEvent e) {
repaint();
}
});
public BallPanel() {
timer.start();
}
public void add() {
list.add(new Ball());
}
public void subtract() {
if (list.size() > 0)
list.remove(list.size() - 1); // Remove the last ball
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawRect(185, 279, 50, 15);
g.setColor(Color.RED);
g.fillRect(185, 279, 50, 15);
for (int i = 0; i < list.size(); i++) {
Ball ball = (Ball) list.get(i); // Get a ball
g.setColor(ball.color); // Set ball color
// Check boundaries
if (ball.x < 0 || ball.x > getWidth())
ball.dx = -ball.dx;
if (ball.y < 0 || ball.y > getHeight())
ball.dy = -ball.dy;
// Adjust ball position
ball.x += ball.dx;
// ball.y += ball.dy;
g.fillOval(ball.x - ball.radius, ball.y - ball.radius,
ball.radius * 2, ball.radius * 2);
}
}
public void suspend() {
timer.stop();
}
public void resume() {
timer.start();
}
public void setDelay(int delay) {
this.delay = delay;
timer.setDelay(delay);
}
}
class Ball {
int x = 20;
int y = 20; // Current ball position
int dx = 2; // Increment on ball's x-coordinate
int dy = 2; // Increment on ball's y-coordinate
int radius = 15; // Ball radius
Color color = new Color((int) (Math.random() * 256),
(int) (Math.random() * 256), (int) (Math.random() * 256));
}
/** Main method */
public static void main(String[] args) {
JFrame frame = new JFrame();
JApplet applet = new MultipleBallApp();
frame.add(applet);
frame.setTitle("MultipleBallApp");
frame.setLocationRelativeTo(null); // Center the frame
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 400);
frame.setLocationRelativeTo(null); // Center the frame
frame.setVisible(true);
}
}
can't apply the same functions for ball animation
This is probably your first mistake. In fact, this is exactly what you should be trying to do. The idea is, you should be trying to devise a means by what is painted/animated is abstract so it doesn't matter what the shape is you want to paint, you can apply it to the sam basic animation process...
For example, you could start with some kind interface which describes the basic properties of an animated entity...
public interface AnimatedShape {
public void update(Rectangle bounds);
public void paint(JComponent parent, Graphics2D g2d);
}
This says that an animated entity can be updated (moved) and painted. By convention (and because I'm lazy), I like to create an abstract implementation which implements the most common aspects...
public abstract class AbstractAnimatedShape implements AnimatedShape {
private Rectangle bounds;
private int dx, dy;
public AbstractAnimatedShape() {
}
public void setBounds(Rectangle bounds) {
this.bounds = bounds;
}
public Rectangle getBounds() {
return bounds;
}
public int getDx() {
return dx;
}
public int getDy() {
return dy;
}
public void setDx(int dx) {
this.dx = dx;
}
public void setDy(int dy) {
this.dy = dy;
}
#Override
public void update(Rectangle parentBounds) {
Rectangle bounds = getBounds();
int dx = getDx();
int dy = getDy();
bounds.x += dx;
bounds.y += dy;
if (bounds.x < parentBounds.x) {
bounds.x = parentBounds.x;
setDx(dx *= -1);
} else if (bounds.x + bounds.width > parentBounds.x + parentBounds.width) {
bounds.x = parentBounds.x + (parentBounds.width - bounds.width);
setDx(dx *= -1);
}
if (bounds.y < parentBounds.y) {
bounds.y = parentBounds.y;
setDy(dy *= -1);
} else if (bounds.y + bounds.height > parentBounds.y + parentBounds.height) {
bounds.y = parentBounds.y + (parentBounds.height - bounds.height);
setDy(dy *= -1);
}
}
}
And then start creating implementations...
public class AnimatedBall extends AbstractAnimatedShape {
private Color color;
public AnimatedBall(int x, int y, int radius, Color color) {
setBounds(new Rectangle(x, y, radius * 2, radius * 2));
this.color = color;
setDx(Math.random() > 0.5 ? 2 : -2);
setDy(Math.random() > 0.5 ? 2 : -2);
}
public Color getColor() {
return color;
}
#Override
public void paint(JComponent parent, Graphics2D g2d) {
Rectangle bounds = getBounds();
g2d.setColor(getColor());
g2d.fillOval(bounds.x, bounds.y, bounds.width, bounds.height);
}
}
In this manner, you can customise the way that the entity is animated and painted, but the basic logic for each instance of the entity is the same...
But what's all the point of this...
Basically, what it allows us to do is produce a "virtual" concept of all the animated objects and simplify there management, for example...
Instead of using a "tightly" coupled List, we can use a loosely couple List instead...
private ArrayList<AnimatedShape> list = new ArrayList<AnimatedShape>();
Then when we want the entities to be updated, we simply need to iterate the List and ask the entities to update...
protected Timer timer = new Timer(delay, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (AnimatedShape ball : list) {
ball.update(getBounds());
}
repaint();
}
});
And when they need to be painted...
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
for (AnimatedShape ball : list) {
ball.paint(this, g2d);
}
}
Because the BallPane doesn't care what actually type of entity it is, but only that it's a type of AnimatedShape...makes life easier...
Now, my implementation of the AnimatedBall already randomise the direction of each instance of the ball, but you can also randomise the starting position when the ball is added using something like...
public void add() {
int radius = 15;
// Randomised position
int x = (int)(Math.random() * (getWidth() - (radius * 2))) + radius;
int y = (int)(Math.random() * (getHeight() - (radius * 2))) + radius;
Color color = new Color((int) (Math.random() * 256),
(int) (Math.random() * 256), (int) (Math.random() * 256));
AnimatedBall ball = new AnimatedBall(x, y, radius, color);
list.add(ball);
}
But how does this help you with adding a rectangle?
You now need to create an AnimatedRectangle that extends from AbstractAnimatedShape and implemented the required methods and add instances of this to the List of AnimatedShapes in the BallPane.
If you don't want the rectangle to be managed within the same list, you could create another list and manage it sepearatly (it create two additional methods, update(List<AnimatedShape>) and paint(List<AnimatedShape>, Graphics2D) passing in each individual list so as to reduce the duplicate code, but that's me)...
You can restrict the rectangles vertical movement by overriding the setDy method and ignoring any changes, for example
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class MultipleBall {
public MultipleBall() {
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("MultipleBallApp");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new BallControl());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class BallControl extends JPanel {
private BallPanel ballPanel = new BallPanel();
private JButton Suspend = new JButton("Suspend");
private JButton Resume = new JButton("Resume");
private JButton Add = new JButton("+1");
private JButton Subtract = new JButton("-1");
private JScrollBar Delay = new JScrollBar();
public BallControl() {
// Group buttons in a panel
JPanel panel = new JPanel();
panel.add(Suspend);
panel.add(Resume);
panel.add(Add);
panel.add(Subtract);
// Add ball and buttons to the panel
ballPanel.setBorder(new javax.swing.border.LineBorder(Color.red));
Delay.setOrientation(JScrollBar.HORIZONTAL);
ballPanel.setDelay(Delay.getMaximum());
setLayout(new BorderLayout());
add(Delay, BorderLayout.NORTH);
add(ballPanel, BorderLayout.CENTER);
add(panel, BorderLayout.SOUTH);
// Register listeners
Suspend.addActionListener(new Listener());
Resume.addActionListener(new Listener());
Add.addActionListener(new Listener());
Subtract.addActionListener(new Listener());
Delay.addAdjustmentListener(new AdjustmentListener() {
public void adjustmentValueChanged(AdjustmentEvent e) {
ballPanel.setDelay(Delay.getMaximum() - e.getValue());
}
});
}
class Listener implements ActionListener {
public void actionPerformed(ActionEvent e) {
if (e.getSource() == Suspend) {
ballPanel.suspend();
} else if (e.getSource() == Resume) {
ballPanel.resume();
} else if (e.getSource() == Add) {
ballPanel.add();
} else if (e.getSource() == Subtract) {
ballPanel.subtract();
}
}
}
}
class BallPanel extends JPanel {
private int delay = 30;
private ArrayList<AnimatedShape> list = new ArrayList<AnimatedShape>();
private AnimatedRectange rectangle;
public BallPanel() {
this.rectangle = new AnimatedRectange(-25, 200, 50, 25, Color.RED);
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
// Create a timer with the initial delay
protected Timer timer = new Timer(delay, new ActionListener() {
/**
* Handle the action event
*/
#Override
public void actionPerformed(ActionEvent e) {
for (AnimatedShape ball : list) {
ball.update(getBounds());
}
rectangle.update(getBounds());
repaint();
}
});
public void add() {
int radius = 15;
// Randomised position
int x = (int) (Math.random() * (getWidth() - (radius * 2))) + radius;
int y = (int) (Math.random() * (getHeight() - (radius * 2))) + radius;
Color color = new Color((int) (Math.random() * 256),
(int) (Math.random() * 256), (int) (Math.random() * 256));
AnimatedBall ball = new AnimatedBall(x, y, radius, color);
list.add(ball);
}
public void subtract() {
if (list.size() > 0) {
list.remove(list.size() - 1); // Remove the last ball
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
for (AnimatedShape ball : list) {
ball.paint(this, g2d);
}
rectangle.paint(this, g2d);
}
public void suspend() {
timer.stop();
}
public void resume() {
timer.start();
}
public void setDelay(int delay) {
this.delay = delay;
timer.setDelay(delay);
}
}
public interface AnimatedShape {
public void update(Rectangle bounds);
public void paint(JComponent parent, Graphics2D g2d);
}
public abstract class AbstractAnimatedShape implements AnimatedShape {
private Rectangle bounds;
private int dx, dy;
public AbstractAnimatedShape() {
}
public void setBounds(Rectangle bounds) {
this.bounds = bounds;
}
public Rectangle getBounds() {
return bounds;
}
public int getDx() {
return dx;
}
public int getDy() {
return dy;
}
public void setDx(int dx) {
this.dx = dx;
}
public void setDy(int dy) {
this.dy = dy;
}
#Override
public void update(Rectangle parentBounds) {
Rectangle bounds = getBounds();
int dx = getDx();
int dy = getDy();
bounds.x += dx;
bounds.y += dy;
if (bounds.x < parentBounds.x) {
bounds.x = parentBounds.x;
setDx(dx *= -1);
} else if (bounds.x + bounds.width > parentBounds.x + parentBounds.width) {
bounds.x = parentBounds.x + (parentBounds.width - bounds.width);
setDx(dx *= -1);
}
if (bounds.y < parentBounds.y) {
bounds.y = parentBounds.y;
setDy(dy *= -1);
} else if (bounds.y + bounds.height > parentBounds.y + parentBounds.height) {
bounds.y = parentBounds.y + (parentBounds.height - bounds.height);
setDy(dy *= -1);
}
}
}
public class AnimatedBall extends AbstractAnimatedShape {
private Color color;
public AnimatedBall(int x, int y, int radius, Color color) {
setBounds(new Rectangle(x, y, radius * 2, radius * 2));
this.color = color;
setDx(Math.random() > 0.5 ? 2 : -2);
setDy(Math.random() > 0.5 ? 2 : -2);
}
public Color getColor() {
return color;
}
#Override
public void paint(JComponent parent, Graphics2D g2d) {
Rectangle bounds = getBounds();
g2d.setColor(getColor());
g2d.fillOval(bounds.x, bounds.y, bounds.width, bounds.height);
}
}
public class AnimatedRectange extends AbstractAnimatedShape {
private Color color;
public AnimatedRectange(int x, int y, int width, int height, Color color) {
setBounds(new Rectangle(x, y, width, height));
this.color = color;
setDx(2);
}
// Don't want to adjust the vertical speed
#Override
public void setDy(int dy) {
}
#Override
public void paint(JComponent parent, Graphics2D g2d) {
Rectangle bounds = getBounds();
g2d.setColor(color);
g2d.fill(bounds);
}
}
/**
* Main method
*/
public static void main(String[] args) {
new MultipleBall();
}
}
Amendment
You really should avoid adding JApplet to a JFrame, an applet has a prescribed life cycle and management process which you are ignoring. Better to focus on just using the BallControl panel as the core UI element and then add this to what ever top level container you want
You may find a JSlider more piratical then a JScrollBar, not to mention, it will look better on different platforms, most uses understand what a slider is used for...
Add a static variable like ballCount and add 1 to it every time you make a ball. In the Ball class, change the definition of y to something likey = 20 + ballcount*(radius*2+distanceInBalls)
public class RandomTests extends JApplet {
public RandomTests() {
add(new BallControl());
}
static int ballCount = 0;
class BallControl extends JPanel {
private BallPanel ballPanel = new BallPanel();
private JButton Suspend = new JButton("Suspend");
private JButton Resume = new JButton("Resume");
private JButton Add = new JButton("+1");
private JButton Subtract = new JButton("-1");
private JScrollBar Delay = new JScrollBar();
public BallControl() {
// Group buttons in a panel
JPanel panel = new JPanel();
panel.add(Suspend);
panel.add(Resume);
panel.add(Add);
panel.add(Subtract);
// Add ball and buttons to the panel
ballPanel.setBorder(new javax.swing.border.LineBorder(Color.red));
Delay.setOrientation(JScrollBar.HORIZONTAL);
ballPanel.setDelay(Delay.getMaximum());
setLayout(new BorderLayout());
add(Delay, BorderLayout.NORTH);
add(ballPanel, BorderLayout.CENTER);
add(panel, BorderLayout.SOUTH);
// Register listeners
Suspend.addActionListener(new Listener());
Resume.addActionListener(new Listener());
Add.addActionListener(new Listener());
Subtract.addActionListener(new Listener());
Delay.addAdjustmentListener(new AdjustmentListener() {
public void adjustmentValueChanged(AdjustmentEvent e) {
ballPanel.setDelay(Delay.getMaximum() - e.getValue());
}
});
}
class Listener implements ActionListener {
public void actionPerformed(ActionEvent e) {
if (e.getSource() == Suspend) ballPanel.suspend();
else if (e.getSource() == Resume) ballPanel.resume();
else if (e.getSource() == Add) ballPanel.add();
else if (e.getSource() == Subtract) ballPanel.subtract();
}
}
}
class BallPanel extends JPanel {
private int delay = 30;
private ArrayList<Ball> list = new ArrayList<Ball>();
// Create a timer with the initial delay
protected Timer timer = new Timer(delay, new ActionListener() {
/** Handle the action event */
public void actionPerformed(ActionEvent e) {
repaint();
}
});
public BallPanel() {
timer.start();
}
public void add() {
list.add(new Ball());
ballCount++;
}
public void subtract() {
if (list.size() > 0) list.remove(list.size() - 1); // Remove the last ball
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawRect(185, 279, 50, 15);
g.setColor(Color.RED);
g.fillRect(185, 279, 50, 15);
for (int i = 0; i < list.size(); i++) {
Ball ball = (Ball) list.get(i); // Get a ball
g.setColor(ball.color); // Set ball color
// Check boundaries
if (ball.x < 0 || ball.x > getWidth()) ball.dx = -ball.dx;
if (ball.y < 0 || ball.y > getHeight()) ball.dy = -ball.dy;
// Adjust ball position
ball.x += ball.dx;
// ball.y += ball.dy;
g.fillOval(ball.x - ball.radius, ball.y - ball.radius, ball.radius * 2, ball.radius * 2);
}
}
public void suspend() {
timer.stop();
}
public void resume() {
timer.start();
}
public void setDelay(int delay) {
this.delay = delay;
timer.setDelay(delay);
}
}
class Ball {
int radius = 15; // Ball radius
int x = radius;
int y = 20 + (radius * ballCount * 2 + 15); // Current ball position
int dx = 2; // Increment on ball's x-coordinate
int dy = 2; // Increment on ball's y-coordinate
Color color = new Color((int) (Math.random() * 256), (int) (Math.random() * 256), (int) (Math.random() * 256));
}
public static void main(String[] args) {
JFrame frame = new JFrame();
JApplet applet = new RandomTests();
frame.add(applet);
frame.setTitle("MultipleBallApp");
frame.setLocationRelativeTo(null); // Center the frame
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 400);
frame.setLocationRelativeTo(null); // Center the frame
frame.setVisible(true);
}
}
I don't usually do this, and I hate when other does... But I found my self spending the last 6 hours to figure out where I wrong.
I rewrote those lines dozen of times, change several approaches, and still couldn't figure out what the hake is wrong!
The issue is as followed:
Clicking on the [Add] button should add additional circle to the panel (starting from x=0,y=0 - top left corner).
currently only the first circle is loaded, the rest is loaded to the arrayList but not to the panel.
Any thoughts?
BallControl:
import javax.swing.*;
import javax.swing.border.LineBorder;
import java.awt.event.*;
import java.awt.*;
import java.util.ArrayList;
public class BallControl extends JPanel {
private int delay = 10; // Create a timer with delay 1000 ms
private Timer timer = new Timer(delay, new TimerListener());
private ArrayList<Ball> balls = new ArrayList<Ball>();
private JButton jbtSuspend = new JButton("Suspend");
private JButton jbtResume = new JButton("Resume");
private JButton jbtAdd = new JButton("Add");
private JButton jbtRemove = new JButton("Remove");
private JButton jbtDirection = new JButton("Direction");
private JScrollBar jsbDelay = new JScrollBar();
private JPanel jplBalls = new JPanel(new BorderLayout());
public BallControl() { // Group buttons in a panel
JPanel panel = new JPanel();
panel.add(jbtSuspend);
panel.add(jbtResume);
panel.add(jbtAdd);
panel.add(jbtRemove);
panel.add(jbtDirection);
balls.add(new Ball());
// Add panel-ball and buttons to the panel
jsbDelay.setOrientation(JScrollBar.HORIZONTAL);
setDelay(jsbDelay.getMaximum());
timer.setDelay(delay);
jplBalls.setBorder(new LineBorder(Color.blue));
jplBalls.setVisible(true);
setLayout(new BorderLayout());
add(jsbDelay, BorderLayout.NORTH);
add(jplBalls, BorderLayout.CENTER);
add(panel, BorderLayout.SOUTH);
timer.start();
jplBalls.add(balls.get(balls.size()-1));
// Register listeners
jbtSuspend.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
timer.stop();
}
});
jbtResume.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
timer.start();
}
});
jbtAdd.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
addNewBallToPanel();
if(jbtRemove.isEnabled()==false)
jbtRemove.setEnabled(true);
}
});
jbtRemove.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
removeBallFromPanel();
if (balls.size()==0)
jbtRemove.setEnabled(false);
}
});
jbtDirection.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
for(Ball b : balls)
b.changeDirection();
}
});
jsbDelay.addAdjustmentListener(new AdjustmentListener() {
public void adjustmentValueChanged(AdjustmentEvent e) {
setDelay(jsbDelay.getMaximum() - e.getValue());
timer.setDelay(delay);
}
});
}
public void addNewBallToPanel(){
balls.add(new Ball());
jplBalls.add(balls.get(balls.size()-1));
}
public void paintComponent(Graphics g){
super.paintComponent(g);
for (Ball b : balls){
b.paintComponent(g);
}
}
private class TimerListener implements ActionListener {
/** Handle the action event */
public void actionPerformed(ActionEvent e) {
repaint();
}
}
public void removeBallFromPanel(){
jplBalls.remove(balls.get(balls.size()-1));
balls.remove(balls.size()-1);
}
public void setDelay(int delay) {
this.delay = delay;
}
}
Ball:
import javax.swing.Timer;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Ball extends JPanel {
//private int delay = 10; // Create a timer with delay 1000 ms
//private Timer timer = new Timer(delay, new TimerListener());
private int x = 0;
private int y = 0; // Current ball position
private int radius = 5; // Ball radius
private int dx = 2; // Increment on ball's x-coordinate
private int dy = 2; // Increment on ball's y-coordinate
private Color c;
public void setC(Color c) {
this.c = c;
}
public Ball() {
c = new Color((int)(Math.random()*255), (int)(Math.random()*255), (int)(Math.random()*255));
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(c);
if (x < radius)
dx = Math.abs(dx); // Check boundaries
if (x > getWidth() - radius)
dx = -Math.abs(dx);
if (y < radius)
dy = Math.abs(dy);
if (y > getHeight() - radius)
dy = -Math.abs(dy);
x += dx; // Adjust ball position
y += dy;
g.fillOval(x - radius, y - radius, radius * 2, radius * 2);
}
public void changeDirection(){
dx=-dx;
dy=-dy;
}
}
BounceBallApp:
import java.awt.*;
import javax.swing.*;
public class BounceBallApp extends JApplet {
public BounceBallApp() {
add(new BallControl());
}
public void init(){
this.setSize(500, 300);
}
}
Two edits seems to fix problem:
In BallControl
private JPanel jplBalls = new JPanel(null);
jplBalls.setOpaque(false);
In Ball
if (x > getParent().getWidth() - radius)
dx = -Math.abs(dx);
if (y > getParent().getHeight() - radius)
dy = -Math.abs(dy);
But i should suggest you to have one panel and draw balls from array in it's paintComponent method.
Now you have created a sandwich from multiple panels and they are overlap each other.
Another way is make ball a widget, and change it's coordinates.
Swing components use layout managers. You created your balls panel with a BorderLayout. A BorderLayout can only display a single component in the CENTER, which is where you are adding all your Balls.
When you add additional Balls you never revalidate() the panel, so by default all the Balls have a 0 size which means there is nothing to paint. Even if you did revalidate() the panel the only the last panel added with display since by default a panel is opaque so the last ball added would paint over top of the other Balls. So the simple answer to your question is that you probably need to make the Balls non-opaque.
However, that is still not a very good design because you will potentially be adding many Balls to the panel and each ball will be sized at roughly (500, 300). Since all the Balls are non-opaque the painting system will need to do a lot of work to find an opaque background which needs to be painted before all the Balls are painted.
If you want to deal with components. Then each component should be non-opaque and the size of each component should only be the size of the actual oval that you are painting. You would need to use a null layout so you can randomly position every ball on the panel. Then you need to set the size/location of every Ball so that it can be painted properly.
Or another approach is to not use components at all but instead to just keep information about every Ball in an ArrayList and then do custom painting that simply iterates through the ArrayList to paint each Ball.
For an example of this last approach check out the DrawOnComponent example from Custom Painting Approaches. The code is not exactly what you want, but the concept is the same except your user will be clicking a button to add a circle instead of using the mouse.
Your problem is that your Ball is a panel. You really only want the Ball to be a data class that holds the data of how to draw a class. The problem with your code is that you r trying to add a new ball panel when really you only want to add a ball to an existing panel. So the logic from your Ball class, you need to move that to the BallControll. Here's a running example. I tweeked your code. You can get an idea from it, what needs to be fixed.
import javax.swing.Timer;
import java.util.ArrayList;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class BounceBallApp extends JFrame {
public BounceBallApp() {
add(new BallControl());
}
public static void main(String[] args) {
JFrame frame = new BounceBallApp();
frame.setTitle("MultipleBallApp");
frame.setSize(300, 200);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
class BallControl extends JPanel {
private BallPanel ballPanel = new BallPanel();
private JButton jbtSuspend = new JButton("Suspend");
private JButton jbtResume = new JButton("Resume");
private JButton jbtAdd = new JButton("Add");
private JButton jbtSubtract = new JButton("Remove");
private JScrollBar jsbDelay = new JScrollBar();
public BallControl() {
// Group buttons in a panel
JPanel panel = new JPanel();
panel.add(jbtSuspend);
panel.add(jbtResume);
panel.add(jbtAdd);
panel.add(jbtSubtract);
// Add ball and buttons to the panel
ballPanel.setBorder(
new javax.swing.border.LineBorder(Color.red));
jsbDelay.setOrientation(JScrollBar.HORIZONTAL);
ballPanel.setDelay(jsbDelay.getMaximum());
setLayout(new BorderLayout());
add(jsbDelay, BorderLayout.NORTH);
add(ballPanel, BorderLayout.CENTER);
add(panel, BorderLayout.SOUTH);
// Register listeners
jbtSuspend.addActionListener(new Listener());
jbtResume.addActionListener(new Listener());
jbtAdd.addActionListener(new Listener());
jbtSubtract.addActionListener(new Listener());
jsbDelay.addAdjustmentListener(new AdjustmentListener() {
#Override
public void adjustmentValueChanged(AdjustmentEvent e) {
ballPanel.setDelay(jsbDelay.getMaximum() - e.getValue());
}
});
}
class Listener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == jbtSuspend) {
ballPanel.suspend();
} else if (e.getSource() == jbtResume) {
ballPanel.resume();
} else if (e.getSource() == jbtAdd) {
ballPanel.add();
} else if (e.getSource() == jbtSubtract) {
ballPanel.subtract();
}
}
}
}
class BallPanel extends JPanel {
private int delay = 10;
private ArrayList<Ball> list = new ArrayList<Ball>();
// Create a timer with the initial delay
protected Timer timer = new Timer(delay, new ActionListener() {
#Override
/**
* Handle the action event
*/
public void actionPerformed(ActionEvent e) {
repaint();
}
});
public BallPanel() {
timer.start();
}
public void add() {
list.add(new Ball());
}
public void subtract() {
if (list.size() > 0) {
list.remove(list.size() - 1); // Remove the last ball
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (int i = 0; i < list.size(); i++) {
Ball ball = (Ball) list.get(i); // Get a ball
g.setColor(ball.color); // Set ball color
// Check boundaries
if (ball.x < 0 || ball.x > getWidth()) {
ball.dx = -ball.dx;
}
if (ball.y < 0 || ball.y > getHeight()) {
ball.dy = -ball.dy;
}
// Adjust ball position
ball.x += ball.dx;
ball.y += ball.dy;
g.fillOval(ball.x - ball.radius, ball.y - ball.radius,
ball.radius * 2, ball.radius * 2);
}
}
public void suspend() {
timer.stop();
}
public void resume() {
timer.start();
}
public void setDelay(int delay) {
this.delay = delay;
timer.setDelay(delay);
}
}
class Ball {
int x = 0;
int y = 0; // Current ball position
int dx = 2; // Increment on ball's x-coordinate
int dy = 2; // Increment on ball's y-coordinate
int radius = 5; // Ball radius
Color color = new Color((int) (Math.random() * 256),
(int) (Math.random() * 256), (int) (Math.random() * 256));
}
}
Note: I left the direction part out. Id was confusing me. You can fix that.