I want to create a method that creates 5 balls on a panel. Can somebody please help me get around this using the paint component method or creating my own draw method. As you can see bellow, i have a paint component method with a for loop that will loop 5 and create a ball in a random location, but unfortunately only one ball is being created.
import java.awt.*;
import java.util.Random;
import javax.swing.*;
public class AllBalls extends JPanel {
int Number_Ball=5;
int x,y;
Graphics g;
AllBalls(){
Random r = new Random();
x=r.nextInt(320);
y=r.nextInt(550);
}
public static JFrame frame;
public static void main(String[] args) {
AllBalls a= new AllBalls();
frame= new JFrame("Bouncing Balls");
frame.setSize(400,600);
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.add(a);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for(int i=0;i<Number_Ball;i++){
g.fillOval(x, y, 30, 30);
}
repaint();
}
}
In my paintComponent method, i created a for loop in which i wanted 5 ball to be created, but only one is being created on the screen.
Recommendations as per my comments:
Get rid of the Graphics g variable as that will only hurt you.
Consider creating a non-GUI Ball class, one that does not extend any Swing component and has its own paint(Graphics) method.
If you need 5 balls, create an array of Ball objects in your AllBalls class.
In paintComponent, iterate through the Balls painting each one by calling its paint or draw method (whatever you name the method).
If you need to move the balls, do so in a Swing Timer that moves the balls and then calls repaint(). Whatever you do, do not call repaint() within the paintComponent method as this results in a bastardization of the method, and results in a completely uncontrolled animation. This method should be for painting and painting only and not for animation or code logic. Use the Swing Timer instead.
Also don't randomize your ball locations within paintComponent, another recommendation made in a now-deleted answer. Again, paintComponent should be for painting and painting only and not for program logic or to change the state of your class. The randomization should be within your Swing Timer or game loop.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.*;
#SuppressWarnings({"serial", "unused"})
public class BallImages extends JPanel {
private static final int PREF_W = 800;
private static final int PREF_H = PREF_W;
private static final int BALL_COUNT = 15;
private static final Color[] COLORS = { Color.RED, Color.BLACK, Color.BLUE,
Color.CYAN, Color.GREEN, Color.ORANGE, Color.PINK, Color.WHITE,
Color.MAGENTA, Color.YELLOW };
private static final int MIN_SPEED = 2;
private static final int MAX_SPEED = 10;
private static final int MIN_WIDTH = 10;
private static final int MAX_WIDTH = 30;
private static final int TIMER_DELAY = 15;
private List<Ball> balls = new ArrayList<>();
private Random random = new Random();
public BallImages() {
for (int i = 0; i < BALL_COUNT; i++) {
int ballWidth = MIN_WIDTH + random.nextInt(MAX_WIDTH - MIN_WIDTH);
Color ballColor = COLORS[random.nextInt(COLORS.length)];
int ballX = ballWidth / 2 + random.nextInt(PREF_W - ballWidth / 2);
int ballY = ballWidth / 2 + random.nextInt(PREF_H - ballWidth / 2);
double direction = 2 * Math.PI * Math.random();
double speed = MIN_SPEED + random.nextInt(MAX_SPEED - MIN_SPEED);
Ball ball = new Ball(ballWidth, ballColor, ballX, ballY, direction,
speed);
balls.add(ball);
}
new Timer(TIMER_DELAY, new TimerListener()).start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for (Ball ball : balls) {
ball.draw(g2);
}
}
#Override
// set the component's size in a safe way
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (!BallImages.this.isDisplayable()) {
((Timer) e.getSource()).stop();
}
for (Ball ball : balls) {
ball.move(PREF_W, PREF_H);
}
repaint();
}
}
private class Ball {
private int width;
private Color color;
private double x;
private double y;
private double direction;
private double speed;
private double deltaX;
private double deltaY;
public Ball(int width, Color color, int x, int y, double direction,
double speed) {
this.width = width;
this.color = color;
this.x = x;
this.y = y;
this.direction = direction;
this.speed = speed;
deltaX = speed * Math.cos(direction);
deltaY = speed * Math.sin(direction);
}
public int getWidth() {
return width;
}
public Color getColor() {
return color;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public double getDirection() {
return direction;
}
public double getSpeed() {
return speed;
}
public void draw(Graphics2D g2) {
g2.setColor(color);
int x2 = (int) (x - width / 2);
int y2 = (int) (y - width / 2);
g2.fillOval(x2, y2, width, width);
}
public void move(int containerWidth, int containerHeight) {
double newX = x + deltaX;
double newY = y + deltaY;
if (newX - width / 2 < 0) {
deltaX = Math.abs(deltaX);
newX = x + deltaX;
}
if (newY - width / 2 < 0) {
deltaY = Math.abs(deltaY);
newY = y + deltaY;
}
if (newX + width / 2 > containerWidth) {
deltaX = -Math.abs(deltaX);
newX = x + deltaX;
}
if (newY + width / 2 > containerHeight) {
deltaY = -Math.abs(deltaY);
newY = y + deltaY;
}
x = newX;
y = newY;
}
}
private static void createAndShowGui() {
BallImages mainPanel = new BallImages();
JFrame frame = new JFrame("BallImages");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.setResizable(false);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Related
I'm currently working on a 2D game in Java for school. We have to use an Abstract Factory design pattern. For the 2D implementation I use a factory as follows:
public class Java2DFact extends AbstractFactory {
public Display display;
private Graphics g;
public Java2DFact() {
display = new Display(2000, 1200);
}
#Override
public PlayerShip getPlayership()
{
return new Java2DPlayership(display.panel);
}
In my display class I create a JFrame and Jpanel
public class Display {
public JFrame frame;
public JPanel panel;
public int width, height;
public Display(int width, int height) {
this.width = width;
this.height = height;
frame = new JFrame();
frame.setTitle("SpaceInvaders");
frame.setSize(1200,800);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
panel = new JPanel(){
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
}
};
panel.setFocusable(true);
frame.add(panel);
}
}
Now from my main gameloop I call the visualize method inside the Java2DPLayership class to visualize my Playership
public class Java2DPlayership extends PlayerShip {
private JPanel panel;
private Graphics2D g2d;
private Image image;
private BufferStrategy bs;
public Java2DPlayership(JPanel panel) {
super();
this.panel = panel;
}
public void visualize() {
try {
image = ImageIO.read(new File("src/Bee.gif"));
Graphics2D g = (Graphics2D) bs.getDrawGraphics();
//g.setColor(new Color(0, 0, 0));
//g.fillRect(10, 10, 12, 8);
g.drawImage(image, (int) super.getMovementComponent().x, (int) super.getMovementComponent().y, null);
Toolkit.getDefaultToolkit().sync();
g.dispose();
panel.repaint();
} catch(Exception e){
System.out.println(e.toString());
}
}
}
My goal is to pass around the JPanel to every entity and let it draw its contents onto the panel before showing it. However I can't seem to figure out how to do this. When using this approach by changing the Graphics of the panel I get a lot of flickering.
Here is a fully functional, albeit simple example, I wrote some time ago. It just has a bunch of balls bouncing off the sides of the panel. Notice that the render method of the Ball class accepts the graphics context from paintComponent. If I had more classes that needed to be rendered, I could have created a Renderable interface and have each class implement it. Then I could have a list of Renderable objects and just go thru them and call the method. But as I also said, that would need to happen quickly to avoid tying up the EDT.
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Bounce extends JPanel {
private static final int COLOR_BOUND = 256;
private final static double INC = 1;
private final static int DIAMETER = 40;
private final static int NBALLS = 20;
private final static int DELAY = 5;
private final static int PANEL_WIDTH = 800;
private final static int PANEL_HEIGHT = 600;
private final static int LEFT_EDGE = 0;
private final static int TOP_EDGE = 0;
private JFrame frame;
private double rightEdge;
private double bottomEdge;
private List<Ball> balls = new ArrayList<>();
private Random rand = new Random();
private List<Long> times = new ArrayList<>();
private int width;
private int height;
public Bounce(int width, int height) {
this.width = width;
this.height = height;
frame = new JFrame("Bounce");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(this);
addComponentListener(new MyComponentListener());
frame.pack();
frame.setLocationRelativeTo(null);
rightEdge = width - DIAMETER;
bottomEdge = height - DIAMETER;
for (int j = 0; j < NBALLS; j++) {
int r = rand.nextInt(COLOR_BOUND);
int g = rand.nextInt(COLOR_BOUND);
int b = rand.nextInt(COLOR_BOUND);
Ball bb = new Ball(new Color(r, g, b), DIAMETER);
balls.add(bb);
}
frame.setVisible(true);
}
public Dimension getPreferredSize() {
return new Dimension(width, height);
}
public static void main(String[] args) {
new Bounce(PANEL_WIDTH, PANEL_HEIGHT).start();
}
public void start() {
/**
* Note: Using sleep gives a better response time than
* either the Swing timer or the utility timer. For a DELAY
* of 5 msecs between updates, the sleep "wakes up" every 5
* to 6 msecs while the other two options are about every
* 15 to 16 msecs. Not certain why this is happening though
* since the other timers are run on threads.
*
*/
Timer timer = new Timer(0,(ae)-> {repaint();
for (Ball b : balls) {
b.updateDirection();
}} );
timer.setDelay(5); // 5 ms.
timer.start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for (Ball ball : balls) {
ball.render(g2d);
}
}
class MyComponentListener extends ComponentAdapter {
public void componentResized(ComponentEvent ce) {
Component comp = ce.getComponent();
rightEdge = comp.getWidth() - DIAMETER;
bottomEdge = comp.getHeight() - DIAMETER;
for (Ball b : balls) {
b.init();
}
}
}
class Ball {
private Color color;
public double x;
private double y;
private double yy;
private int ydir = 1;
private int xdir = 1;
private double slope;
private int diameter;
public Ball(Color color, int diameter) {
this.color = color;
this.diameter = diameter;
init();
}
public void init() {
// Local constants not uses outside of method
// Provides default slope and direction for ball
slope = Math.random() * .25 + .50;
x = (int) (rightEdge * Math.random());
yy = (int) (bottomEdge * Math.random()) + diameter;
xdir = Math.random() > .5 ? -1
: 1;
ydir = Math.random() > .5 ? -1
: 1;
y = yy;
}
public void render(Graphics2D g2d) {
g2d.setColor(color);
g2d.fillOval((int) x, (int) y, diameter, diameter);
}
public void updateDirection() {
x += (xdir * INC);
yy += (ydir * INC);
y = yy * slope;
if (x < LEFT_EDGE || x > rightEdge) {
xdir = -xdir;
}
if (y < TOP_EDGE || y > bottomEdge) {
ydir = -ydir;
}
}
}
}
I am trying to make a simple screen-saver, and I think I have everything else implemented properly. I just don't know how to make the shapes move. Looking at the code a bit more, I think it has something to do with the way I might have implemented the shapes themselves (the if (line), etc.)? I have looked at other answers regarding this question, but I couldn't quite find the answer I was looking for with the way my code is implemented. Any advice is super helpful, thank you!
Also, just for a side note, is the -40 necessary for the "if" conditions? I thought I heard somewhere that it is useful to leave a little space in-between the frame, but I can't remember why.
import javax.swing.*;
import javax.swing.Timer;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;
public class MyScreenSaver extends JComponent
{
ArrayList <Shapes> randomShapes = new ArrayList <>();
Shapes randomShape;
public MyScreenSaver ()
{
class TimerListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent event)
{
// Create random generation of colors and shapes...
Shape shape = null;
int frameWidth = 800;
int frameHeight = 500;
for (int i = 1; i <= 10; i ++)
{
Random rng = new Random();
boolean line = rng.nextBoolean();
boolean rectangle = rng.nextBoolean();
int red = ((int) (Math.random() * 255));
int green = ((int) (Math.random() * 255));
int blue = ((int) (Math.random() * 255));
Color color = new Color (red, green, blue);
int width = (10 + (int) (40 * Math.random()));
int height = (10 + (int) (40 * Math.random()));
int x = (int) (Math.random() * (getWidth() - width));
int y = (int) (Math.random() * (getHeight() - height));
int velX = 2;
int velY = 2;
int newX = velX + x;
int newY = velY + y;
if (line)
{
shape = new Line2D.Double(x, y, x + width, y + height);
}
else if (rectangle)
{
shape = new Rectangle2D.Double(x, y, width, height);
}
else
{
shape = new Ellipse2D.Double(x, y, width, height);
}
// Here, we want the shapes to stop appearing after reaching a certain size...
if (randomShapes.size() >= 20)
{
break;
}
Shapes randomShape = new Shapes (color, shape);
// Add the shapes to the randomShapes ArrayList...
randomShapes.add (randomShape);
// Here, we are moving the shapes...
for (Shapes shapeMove : randomShapes)
{
if (x < 0 || x > frameWidth - 40)
{
velX = velX * -1;
}
else
{
x = newX;
}
if (y < 0 || y > frameHeight - 40)
{
velY = velY * -1;
}
else
{
y = newY;
}
}
repaint();
}
}
}
ActionListener listener = new TimerListener();
final int DELAY = 100;
Timer t = new Timer(DELAY, listener);
t.start();
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (Shapes shape : randomShapes)
{
System.out.println (randomShapes.size());
shape.paint(g2d);
}
g2d.dispose();
}
public static void main (String [] args)
{
// Set up the main frame...
final int FRAME_WIDTH = 800;
final int FRAME_HEIGHT = 500;
JFrame screenSaverFrame = new JFrame ();
screenSaverFrame.setTitle("Homework 6");
screenSaverFrame.setSize (FRAME_WIDTH, FRAME_HEIGHT);
screenSaverFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final MyScreenSaver component = new MyScreenSaver ();
screenSaverFrame.add (component);
screenSaverFrame.setVisible (true);
}
}
Here is the loop specifically...
for (Shapes shapeMove : randomShapes)
{
if (x < 0 || x > frameWidth - 40)
{
velX = velX * -1;
}
else
{
x = newX;
}
if (y < 0 || y > frameHeight - 40)
{
velY = velY * -1;
}
else
{
y = newY;
}
}
I was thinking about placing the contents of the loop inside a void method, but I really don't think that is necessary.
Also, here is my Shape class as well.
import java.awt.*;
public class Shapes
{
private final Color color;
private final Shape shape;
public Shapes (Color color, Shape shape)
{
this.color = color;
this.shape = shape;
}
public Color getColor ()
{
return color;
}
public Shape getShape ()
{
return shape;
}
public void paint(Graphics2D g2d)
{
g2d.setColor(color);
g2d.draw(shape);
g2d.fill(shape);
}
}
The following mre is based on your code. Note the comments documenting the changes made:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.Timer;
public class MyScreenSaver extends JComponent
{
private final ArrayList <ColoredShape> randomShapes = new ArrayList <>();
//define constants
private static final int FRAME_WIDTH = 800, FRAME_HEIGHT = 500, DELAY = 100, MAX_SHAPE_HEIGHT = 40,
MAX_SHAPE_WIDTH = 40, MIN_SHAPE_HEIGHT = 10, MIN_SHAPE_WIDTH = 40, MAX_SHAPE_COUNT = 20,
LINE_WIDTH = 5, X_STEP = 5, Y_STEP = 5;
//define shape types
enum ShapeType {LINE, RECTANGLE, ELIPSE};
public MyScreenSaver ()
{
ActionListener listener = new TimerListener();
Timer t = new Timer(DELAY, listener);
t.start();
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setStroke(new BasicStroke(LINE_WIDTH));
for (ColoredShape shape : randomShapes) //draw stored shapes
{
shape.paint(g2d);
}
g2d.dispose();
}
//invoked repeatedly by Timer. Each time it adds a shape to the collection
//moves all shapes and repaints
class TimerListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent event)
{
// Create random generation of colors and shapes and location
//up to MAX_SHAPE_COUNT
if (randomShapes.size() < MAX_SHAPE_COUNT)
{
int red = (int) (Math.random() * 255);
int green = (int) (Math.random() * 255);
int blue = (int) (Math.random() * 255);
Color color = new Color (red, green, blue);
int width = MIN_SHAPE_WIDTH + (int) (MAX_SHAPE_WIDTH * Math.random());
int height = MIN_SHAPE_HEIGHT + (int) (MAX_SHAPE_HEIGHT* Math.random());
int x = (int) (Math.random() * (getWidth() - width));
int y = (int) (Math.random() * (getHeight() - height));
//select a random shape type
int typeIndex = (int) (Math.random()* ShapeType.values().length);
ShapeType type = ShapeType.values()[typeIndex];
// Add the shapes to the randomShapes ArrayList...
randomShapes.add (new ColoredShape (type,color,x, y,width,height));
}
//move the shapes...
for (ColoredShape shape : randomShapes)
{
int x = shape.getX();//get current x position
if (x <= 0 || x > getWidth() - shape.getWidth())
{
shape.setXDirection(-1*shape.getXDirection());//change direction
}
shape.setX(shape.getX() + shape.getXDirection()*X_STEP);//increment
int y = shape.getY();
if (y <= 0 || y > getHeight()- shape.getHeight())
{
shape.setYDirection(-1*shape.getYDirection());
}
shape.setY(shape.getY() + shape.getYDirection()*Y_STEP);
}
repaint();
}
}
public class ColoredShape
{
private int x, y;
private final int width, height;
private int xDirection, yDirection;
private final Color color;
private final ShapeType type;
public ColoredShape(ShapeType type, Color color, int x, int y, int width, int height)
{
this.type = type;
this.color = color;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
xDirection = yDirection = 1; //1 means increase value, -1 decrease value
}
public Color getColor ()
{
return color;
}
private Shape constructShape ()
{
//construct new shape using updated bounds
if(type == ShapeType.LINE)
return new Line2D.Double(x, y, x + width, y + height);
else if (type == ShapeType.RECTANGLE)
return new Rectangle2D.Double(x, y, width, height);
else
return new Ellipse2D.Double(x, y, width, height);
}
public void paint(Graphics2D g2d)
{
g2d.setColor(color);
Shape shape = constructShape();
g2d.draw(shape);
g2d.fill(shape);
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getXDirection() {
return xDirection;
}
public void setXDirection(int xDirextion) {
xDirection = xDirextion;
}
public int getYDirection() {
return yDirection;
}
public void setYDirection(int yDirection) {
this.yDirection = yDirection;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
}
public static void main (String [] args)
{
// Set up the main frame...
JFrame screenSaverFrame = new JFrame ();
screenSaverFrame.setTitle("Homework 6");
screenSaverFrame.setSize (FRAME_WIDTH, FRAME_HEIGHT);
screenSaverFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
screenSaverFrame.add (new MyScreenSaver ());
screenSaverFrame.setVisible (true);
}
}
I made a this test program for a project im working on. It draws a car, and moves it across the JFrame from left to right. What I am trying to do is make it so once the car object has completely passed the other side of the frame it will loop back on the other side. I think I have to use an outside loop for the timer in the main class called animationTester but Im not entirely sure what I want to put in the loop. Also Ive been told that I cannot hard code the size of the window into the program. anyway here are my classes.
ShapeIcon class
import java.awt.*;
import java.util.*;
import javax.swing.*;
/**
An icon that contains a moveable shape.
*/
public class ShapeIcon implements Icon
{
private int width;
private int height;
private MoveableShape shape;
public ShapeIcon(MoveableShape shape,
int width, int height)
{
this.shape = shape;
this.width = width;
this.height = height;
}
public int getIconWidth()
{
return width;
}
public int getIconHeight()
{
return height;
}
public void paintIcon(Component c, Graphics g, int x, int y)
{
Graphics2D g2 = (Graphics2D) g;
shape.draw(g2); // has draw method since it implements MoveableShape
}
}
CarShape Class
import java.awt.*;
import java.awt.geom.*;
import java.util.*;
/**
A car that can be moved around.
*/
public class CarShape implements MoveableShape
{
private int x;
private int y;
private int width;
/**
Constructs a car item.
#param x the left of the bounding rectangle
#param y the top of the bounding rectangle
#param width the width of the bounding rectangle
*/
public CarShape(int x, int y, int width)
{
this.x = x;
this.y = y;
this.width = width;
}
public void translate(int dx, int dy)
{
x += dx;
y += dy;
}
public void draw(Graphics2D g2)
{
Rectangle2D.Double body
= new Rectangle2D.Double(x, y + width / 6,
width - 1, width / 6);
Ellipse2D.Double frontTire
= new Ellipse2D.Double(x + width / 6, y + width / 3,
width / 6, width / 6);
Ellipse2D.Double rearTire
= new Ellipse2D.Double(x + width * 2 / 3, y + width / 3,
width / 6, width / 6);
// The bottom of the front windshield
Point2D.Double r1
= new Point2D.Double(x + width / 6, y + width / 6);
// The front of the roof
Point2D.Double r2
= new Point2D.Double(x + width / 3, y);
// The rear of the roof
Point2D.Double r3
= new Point2D.Double(x + width * 2 / 3, y);
// The bottom of the rear windshield
Point2D.Double r4
= new Point2D.Double(x + width * 5 / 6, y + width / 6);
Line2D.Double frontWindshield
= new Line2D.Double(r1, r2);
Line2D.Double roofTop
= new Line2D.Double(r2, r3);
Line2D.Double rearWindshield
= new Line2D.Double(r3, r4);
g2.draw(body);
g2.draw(frontTire);
g2.draw(rearTire);
g2.draw(frontWindshield);
g2.draw(roofTop);
g2.draw(rearWindshield);
}
}
MoveableShape class
import java.awt.*;
/**
A shape that can be moved around.
*/
public interface MoveableShape
{
/**
Draws the shape.
#param g2 the graphics context
*/
void draw(Graphics2D g2);
/**
Moves the shape by a given amount.
#param dx the amount to translate in x-direction
#param dy the amount to translate in y-direction
*/
void translate(int dx, int dy);
}
Main class
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
This program implements an animation that moves
a car shape.
*/
public class AnimationTester
{
private static final int ICON_WIDTH = 400;
private static final int ICON_HEIGHT = 100;
private static final int CAR_WIDTH = 100;
public static void main(String[] args)
{
JFrame frame = new JFrame();
final MoveableShape shape
= new CarShape(0, 0, CAR_WIDTH);
ShapeIcon icon = new ShapeIcon(shape,
ICON_WIDTH, ICON_HEIGHT);
final JLabel label = new JLabel(icon);
frame.setLayout(new FlowLayout());
frame.add(label);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
final int DELAY = 100;
// Milliseconds between timer ticks
Timer t = new Timer(DELAY, new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
shape.translate(1, 0);
label.repaint();
}
});
t.start();
}
}
You may modify translate method to allow an end parameter, so the car start again when it gets to the end of the frame:
public void translate(int dx, int dy, int end) {
x += dx;
y += dy;
if (end == x) {
x = -100;
}
}
Then pass the frame.getWidth() to the translate call on AnimationTester.class:
Timer t = new Timer(DELAY, new ActionListener() {
public void actionPerformed(ActionEvent event) {
car.translate(1, 0, frame.getWidth());
label.repaint();
}
});
I hope it helps :)
I have below code, I need to alter it so that the balls are generated with a mouse click rather than all of them generating at once. I know I need to use a mouse listener but I do not know how I can integrate that into what I have without "breaking" the app.
No changes needed
import javax.swing.JFrame;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferStrategy;
public class Display {
public final int width;
public final int height;
private JFrame frame;
private boolean closeRequested;
private long lastFrameTime;
private BufferStrategy bufferStrategy;
private Graphics2D graphics;
public Display(int width, int height){
this.width = width;
this.height = height;
initialize();
}
private void initialize() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.setAutoRequestFocus(true);
frame.setResizable(false);
frame.addWindowListener(new WindowAdapter(){
#Override
public void windowClosing(WindowEvent e){
closeRequested = true;
}
});
Canvas canvas = new Canvas();
canvas.setIgnoreRepaint(true);
canvas.setPreferredSize(new Dimension(width, height));
frame.getContentPane().add(canvas);
frame.setVisible(true);
frame.pack();
frame.setLocationRelativeTo(null);
canvas.createBufferStrategy(2);
bufferStrategy = canvas.getBufferStrategy();
graphics = (Graphics2D) bufferStrategy.getDrawGraphics();
lastFrameTime = System.currentTimeMillis();
}
public boolean isCloseRequested(){
return closeRequested;
}
public void destroy() {
frame.dispose();
}
public void update(){
if (bufferStrategy.contentsLost()){
graphics.dispose();
graphics = (Graphics2D) bufferStrategy.getDrawGraphics();
}
bufferStrategy.show();
}
public Graphics2D getGraphics() {
return graphics;
}
public void sync(int fps) {
if (fps < 1){
return;
}
long currentFrameTime = System.currentTimeMillis();
long deltaTime = currentFrameTime - lastFrameTime;
long timeToSleep = (1000/fps) - deltaTime;
while (System.currentTimeMillis() - currentFrameTime < timeToSleep){
try{
Thread.sleep(1L);
} catch (InterruptedException e) {
}
}
lastFrameTime = System.currentTimeMillis();
}
}
No changes needed
import java.awt.Color;
public class Ball {
public float x;
public float y;
public float sX;
public float sY;
public int radius;
public Color color;
public Ball(float x, float y, float sX, float sY, int radius, Color color){
this.x = x;
this.y = y;
this.sX = sX;
this.sY = sY;
this.radius = radius;
this.color = color;
}
}
This is where I think the mouse listener would be added in for generating the new balls.
How to change addBalls() logic to generate ball with mouse click?
import java.awt.Color;
import java.awt.Graphics2D;
import java.util.ArrayList;
import java.util.Random;
public class BouncingBallsApp {
private Display display;
private ArrayList<Ball> balls = new ArrayList<>();
public BouncingBallsApp() {
display = new Display(800,600);
addBalls();
mainLoop();
display.destroy();
}
private void mainLoop() {
while (!display.isCloseRequested()){
updatePhysics();
draw(display.getGraphics());
display.update();
display.sync(60);
}
}
//Question????How to change this logic to generate ball with mouse click
private void addBalls() {
int numberOfBalls = 20;
Random random = new Random();
for (int i = 0; i < numberOfBalls; i++){
int radius = random.nextInt(40) + 10;
int x = random.nextInt(display.width - radius * 2) + radius;
int y = random.nextInt(display.height - radius * 2) + radius;
float sX = random.nextFloat() * 10f + 3f;
float sY = random.nextFloat() * 10f + 3f;
Color color;
switch (random.nextInt(4)){
case 0:
color = Color.red;
break;
case 1:
color = Color.green;
break;
case 2:
color = Color.yellow;
break;
default:
color = Color.blue;
break;
}
Ball ball = new Ball(x, y, sX, sY, radius, color);
balls.add(ball);
}
}
private void updatePhysics() {
for (Ball ball : balls){
ball.x += ball.sX;
ball.y += ball.sY;
if (ball.x - ball.radius < 0){
ball.sX = Math.abs(ball.sX);
} else if (ball.x + ball.radius > display.width){
ball.sX = -Math.abs(ball.sX);
}
if (ball.y - ball.radius < 0){
ball.sY = Math.abs(ball.sY);
} else if (ball.y + ball.radius > display.height){
ball.sY = -Math.abs(ball.sY);
}
}
}
private void draw(Graphics2D g) {
g.setBackground(Color.black);
g.clearRect(0,0, display.width, display.height);
for (Ball ball : balls){
g.setColor(ball.color);
int x = (int) (ball.x - ball.radius);
int y = (int) (ball.y - ball.radius);
int size = ball.radius * 2;
g.fillOval(x, y, size, size);
}
}
public static void main(String[] args) {
new BouncingBallsApp();
}
}
In BouncingBallsApp constructor do the following changes:
public BouncingBallsApp() {
display = new Display(800,600);
//instead of calling add balls directly, use a mouse listener
//addBalls();
display.addMouseListener(getListener());
mainLoop();
display.destroy();
}
Add getListener() method to BouncingBallsApp:
private MouseListener getListener() {
return new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
addBalls(1); //call add balls when mouse pressed
}
};
}
And slightly change addBalls() so that numberOfBalls becomes an argument:
private void addBalls(int numberOfBalls) {
//int numberOfBalls = 20;
.....
Add mouse listener support to Display:
//add mouse listener to canvas
void addMouseListener(MouseListener listener) {
canvas.addMouseListener(listener); //requiers to make canvas a field
}
All done.
To generate balls, simply click the canvas.
(A link to the full code (you can run it online). )
i have looked all over the internet and in my school books but I can't seem to slove my problem.
In my program "bouncing ball" (got the code from our teacher) i need to change the size of the ball from small to bigger and reverse. I understand that i need a boolean to do that and maybe alsow an if statment. This is what I have in the Ball class rigth now regarding the size change:
private boolean changeSize = true;
int maxSize = 10;
int minSize = 1;
public void changeSize(boolean size ){
if(size == maxSize ){
return minSize;
}
else return maxSize;
}
public void changeBallSize(int d, int f){
diameter = d*f;
This is the whole code for the class Ball:
class Ball {
static int defaultDiameter = 10;
static Color defaultColor = Color.yellow;
static Rectangle defaultBox = new Rectangle(0,0,100,100);
// Position
private int x, y;
// Speen and angel
private int dx, dy;
// Size
private int diameter;
// Color
private Color color;
// Bouncing area
private Rectangle box;
// New Ball
public Ball( int x0, int y0, int dx0, int dy0 ) {
x = x0;
y = y0;
dx = dx0;
dy = dy0;
color = defaultColor;
diameter = defaultDiameter;
}
// New color
public void setColor( Color c ) {
color = c;
}
public void setBoundingBox( Rectangle r ) {
box = r;
}
// ball
public void paint( Graphics g ) {
// Byt till bollens färg
g.setColor( color );
g.fillOval( x, y, diameter, diameter );
}
void constrain() {
// Ge absoluta koordinater för det rektangulära området
int x0 = box.x;
int y0 = box.y;
int x1 = x0 + box.width - diameter;
int y1 = y0 + box.height - diameter;
// Setting speed and angels
if (x < x0)
dx = Math.abs(dx);
if (x > x1)
dx = -Math.abs(dx);
if (y < y0)
dy = Math.abs(dy);
if (y > y1)
dy = -Math.abs(dy);
}
// movingt the ball
x = x + dx;
y = y + dy;
constrain();
}
}
I am a total rookie of java! Thanks for the help!
Add the following into your Ball class:
private int changeFlag=-1;
In your constrain() function, just before the last line, after moving the ball:
if(diameter==maxSize) {
changeFlag=-1;
}
else if (diameter==minSize) {
changeFlag=1;
}
diameter=diameter+changeFlag;
Add this code to your Ball Class
private minSize = 1;
private maxSize = 10;
public void setDiameter(int newDiameter) {
this.diameter = newDiameter;
}
public int getMinSize() {
return minSize;
}
public int getMinSize() {
return maxSize;
}
Use this when you use the ball
Ball ball = new Ball(1,1,1,1);
int newDiameter = 10;
if(newDiameter == ball.getMinSize()) {
ball.setDiameter(ball.getMaxSize());
}
id(newDiameter == ball.getMaxSize()) {
ball.setDiameter (ball.getMinSize());
}
I've edited it, is this what you mean?
Main Class:
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
public class Main extends JFrame {
public static void main(String[] args) {
new Main();
}
public Main() {
// configure JFrame
setSize(640, 360);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// create ball
Ball ball = new Ball(this.getWidth()/2, this.getHeight()/2, 50, 100);
add(ball);
Thread t = new Thread(ball);
t.start();
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
}
}
Ball Class:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JComponent;
public class Ball extends JComponent implements Runnable {
private int x, y, minDiameter, maxDiameter, currentDiameter, growRate = -1;
private Color color = Color.BLUE;
public Ball(int x, int y, int minDiameter, int maxDiameter) {
this.x = x;
this.y = y;
this.minDiameter = minDiameter;
this.maxDiameter = maxDiameter;
this.currentDiameter = minDiameter;
setVisible(true);
}
#Override
public void run() {
while (true) {
// coerce max and min size
if (this.currentDiameter + growRate > maxDiameter) {
this.currentDiameter = maxDiameter;
this.growRate = -1;
}
if (this.currentDiameter + growRate < minDiameter) {
this.currentDiameter = minDiameter;
this.growRate = 1;
}
this.currentDiameter += this.growRate;
repaint();
try {
Thread.sleep(10);
}
catch(Exception e) {
System.out.println(e.toString());
}
}
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
g2.setColor(this.color);
g2.fillOval(this.x, this.y, this.currentDiameter, this.currentDiameter);
}
}