So this is part of my game panel for my game. I got my start button to work, and created a reset button but I don't know how to reset the game every single time it is pressed. Does anyone know how to reset the game to its original position after you press "Reset"? Right now it will display the words "You have died", but it doesn't do anything when I press Reset.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
public class GamePanel extends JPanel
{
private Brick[][] bary;
private static final Color BACKGROUND = Color.black;
private BufferedImage myImage;
private Graphics myBuffer;
private Ball ball = new Ball();
public Bumper bumper;
private Timer t;
private int hits = 0;
private boolean isPlayingGame = false;
public GamePanel()
{
myImage = new BufferedImage(600, 800, BufferedImage.TYPE_INT_RGB);
myBuffer = myImage.getGraphics();
myBuffer.setColor(BACKGROUND);
myBuffer.fillRect(0, 0, 600,800);
JButton sbutton = new JButton("Start");
sbutton.setFocusable(false);
sbutton.addActionListener(new Listener());
add(sbutton);
JButton rbutton = new JButton("Reset");
rbutton.setFocusable(false);
rbutton.addActionListener(new Listener());
add(rbutton);
bary = new Brick[5][14];
bumper = new Bumper(270, 775, 60, 10, Color.BLUE);
ball = new Ball(300, 400, 10, Color.RED);
t = new Timer(5, new Listener());
setFocusable(true);
//addKeyListener(new Key());
}
// tick method is called every 10ms by Arkanoid.java
// only does stuff if game is actually being played
public void tick()
{
if(isPlayingGame)
{
ball.move(600,800, 600, 800);
death(ball);
}
}
// these get called by the BumperLitsener, which is added to the whole frame
public void moveBumperLeft()
{
bumper.setX(bumper.getX()-20);
//System.out.println("moving bumper left");
}
public void moveBumperRight()
{
bumper.setX(bumper.getX()+20);
//System.out.println("moving bumper right");
}
/*
public class Key extends KeyAdapter
{
public void keyPressed(KeyEvent e)
{
if(e.getKeyCode() == KeyEvent.VK_A)
bumper.setX(bumper.getX()+10 );
if(e.getKeyCode() == KeyEvent.VK_S)
bumper.setX(bumper.getX()-10 );
}
}
*/
public void startGame()
{
isPlayingGame = true;
}
public void paintComponent(Graphics g)
{
// draw myBuffer, then draw that onto g
myBuffer.setColor(BACKGROUND);
myBuffer.fillRect(0,0,600,800);
setLayout(new GridLayout(5, 14));
myBuffer.setColor(Color.WHITE);
int b=1;
int d=40;
for(int r = 0; r < bary.length; r++)
{
for(int c = 0; c < bary[0].length; c++)
{
bary[r][c] = new Brick(b, d,40,20, Color.BLUE);
b=b+43;
bary[r][c].draw(myBuffer);
}
d=d+23;
b=1;
}
ball.draw(myBuffer);
bumper.draw(myBuffer);
repaint();
if(ball.getColor()==Color.BLACK)
{
myBuffer.setFont(new Font("MS Comic Sans", Font.ITALIC, 45));
myBuffer.drawString("GAME OVER!", 168, 300);
ball.move(0, 0, 0, 0);
}
g.drawImage(myImage, 0, 0, getWidth(), getHeight(), null);
}
private class Listener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
GamePanel.this.getTopLevelAncestor().requestFocus();
startGame(); // starts game when button is pressed
System.out.println("Game is starting!");
}
}
private void death(Ball ball)
{
double d = ball.getY() + ball.getRadius();
if(d>800.0)
{
ball.setX(300);
ball.setY(400);
ball.setdx(ball.getdx()*-1);
ball.setdy(ball.getdy()*-1);
ball.setColor(Color.BLACK);
}
}
/* private void collide(Ball ball, Bumper b)
{
double d = distance(ball.getX(), ball.getY(), b.getX(), b.getY());
if(d <= 37.5)
{
ball.move();
hits++;
}
}*/
// private double distance(double x1, double y1, double x2, double y2)
// {
// return(Math.sqrt(Math.pow(x2 - x1, 2.0) + Math.pow(y2 - y1, 2.0))); // enter the calculation here.
//}
}
Thank you so much!!!
Right now, you have a single Listener class and two buttons that have registered against the listener (sbutton and rbutton). It'd be easier if you had different classes that implemented ActionListener, but let's move past that for now.
You need to make modifications to your actionPerformed method such that:
You can differentiate the source of the action - i.e. which button was pressed - Reset or the Start? Hint: use the getSource() method from ActionEvent
If the button pressed was the Reset button, you need to reset the GUI to its initial state
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);
}
}
}
}
I have created a simple paint program. All the functionalities are working, but when I go to create a new line, the program does not recognize that it is creating a new line, and instead just creates one huge line. The code will compile if you run it. Any help would be greatly appreciated. Thank you!
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.ArrayList;
import javax.swing.*;
public class SimplePaint extends JFrame implements ActionListener{
private static final long serialVersionUID = 1L;
JButton action = new JButton();
JButton red = new JButton();
JButton blue = new JButton();
JButton yellow = new JButton();
Color initial = Color.MAGENTA;
JButton thin = new JButton();
JButton medium = new JButton();
JButton thick = new JButton();
Stroke stroke = new BasicStroke(3);
private static ArrayList<Point> points = new ArrayList<Point>();
JButton erase = new JButton();
JButton drawing = new JButton();
Point start = null;
Point end = null;
Line2D draw = new Line2D.Float();
JPanel panel = new JPanel();
public SimplePaint(){
getContentPane().add(panel);
setSize(450, 450);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
design();
addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent e){
//points.clear();
points.add(e.getPoint());
// repaint();
}
public void mouseReleased(MouseEvent e){
points.add(e.getPoint());
// points.clear();
//repaint();
}
});
addMouseMotionListener(new MouseMotionAdapter(){
#Override
public void mouseDragged(MouseEvent e){
points.add(e.getPoint());
repaint();
}
});
blue.addActionListener(this);
red.addActionListener(this);
yellow.addActionListener(this);
thin.addActionListener(this);
medium.addActionListener(this);
thick.addActionListener(this);
erase.addActionListener(this);
drawing.addActionListener(this);
}
public void design(){
panel.setBackground(Color.BLACK);
blue.setBackground(Color.BLUE);
blue.setPreferredSize(new Dimension(50, 25));
panel.add(blue);
red.setBackground(Color.RED);
red.setPreferredSize(new Dimension(50, 25));
panel.add(red);
yellow.setBackground(Color.yellow);
yellow.setPreferredSize(new Dimension(50, 25));
panel.add(yellow);
thin.setText("Thin");
panel.add(thin);
medium.setText("Medium");
panel.add(medium);
thick.setText("Thick");
panel.add(thick);
erase.setText("Erase");
panel.add(erase);
drawing.setText("Draw");
panel.add(drawing);
}
public void actionPerformed(ActionEvent e){
if(e.getSource() == blue){
initial = Color.BLUE;
}else if(e.getSource() == red){
initial = Color.RED;
}else if(e.getSource() == yellow){
initial = Color.YELLOW;
}else if(e.getSource() == thin){
stroke = new BasicStroke(1);
}else if(e.getSource() == medium){
stroke = new BasicStroke(5);
}else if(e.getSource() == thick){
stroke = new BasicStroke(10);
}else if(e.getSource() == erase){
initial = Color.BLACK;
}
//repaint();
}
#Override
public void paint(Graphics g){
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(initial);
g2.setStroke(stroke);
if(points != null && points.size() > 1){
for(int p = 0; p < points.size() - 1; p++){
int x1 = points.get(p).x;
int y1 = points.get(p).y;
int x2 = points.get(p + 1).x;
int y2 = points.get(p + 1).y;
g2.drawLine(x1, y1, x2, y2);
}
}
g2.dispose();
}
public static void main(String []args){
SimplePaint s =new SimplePaint();
s.setVisible(true);
}
}
It looks to me as though you need to be storing the line segments separately rather than in one list of points. To do that you need to define a class to hold them and store a list of segments rather than points.
private class Segment {
private final List<Point> points = new ArrayList<Point>();
private final Color color = initial;
private final Stroke stroke = SimplePaint.this.stroke;
}
private final List<Segment> segments = new ArrayList<>();
Then create a new segment each time the mouse is pressed and add the point to the current segment each time the mouse is dragged:
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
segments.add(0, new Segment());
segments.get(0).points.add(e.getPoint());
}
});
addMouseMotionListener(new MouseMotionAdapter() {
public void mouseDragged(MouseEvent e) {
segments.get(0).points.add(e.getPoint());
repaint();
}
});
You don't need to do anything when the mouse is released as the point will already have been added on drag. You can delete that listener.
Your paint method then needs to iterate through all segments:
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
for (Segment segment : segments) {
g2.setColor(segment.color);
g2.setStroke(segment.stroke);
for (int p = 0; p < segment.points.size() - 1; p++) {
Point p1 = segment.points.get(p);
Point p2 = segment.points.get(p + 1);
g2.drawLine(p1.x, p1.y, p2.x, p2.y);
}
}
g2.dispose();
}
I've removed your check to ignore lines with less than two points: the for loop will skip those anyway so it's redundant.
Inserting the new segments at index 0 makes the code to add points simple but has the disadvantage that lines drawn first overwrite later lines. Simple solution would be to use a Deque and peekLast rather than List and get(0).
There are lots of other improvements (particularly performance) you can make but I've tried those changes myself and they work fine.
I'm trying to make a kind of brickbreaker game but once I press the start button ("starten" = true). I can't use the keyPressed()-utility anymore but before i pressed the start button I could use the keyPressed()-utility. Could somebody please tell me why I suddenly can't use the keyPressed()-utility anymore and give me a possible solution too?
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class VGKernel extends JPanel implements KeyListener {
public Rectangle screen, ball, block; // The screen area and ball location/size.
public Rectangle bounds; // The boundaries of the drawing area.
public JFrame frame; // A JFrame to put the graphics into.
public VGTimerTask vgTask; // The TimerTask that runs the game.
public boolean down, right, starten = false; // Direction of ball's travel.
public JButton start;
public VGKernel(){
super();
screen = new Rectangle(0, 0, 600, 400);
ball = new Rectangle(0, 0, 20, 20);
block = new Rectangle(260, 350, 40, 10);
bounds = new Rectangle(0, 0, 600, 400); // Give some starter values.
frame = new JFrame("VGKernel");
vgTask = new VGTimerTask();
addKeyListener(this);
setFocusable(true);
start = new JButton("Start");
start.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
starten = true;
}
});
add(start);
}
class VGTimerTask extends TimerTask{
public void run(){
repaint();
if(starten){
moveBall();
frame.repaint();
}
}
}
// Now the instance methods:
public void paintComponent(Graphics g){
// Get the drawing area bounds for game logic.
bounds = g.getClipBounds();
// Clear the drawing area, then draw the ball.
g.clearRect(screen.x, screen.y, screen.width, screen.height);
g.fillRect(ball.x, ball.y, ball.width, ball.height);
g.fillRect(block.x, block.y, block.width, block.height);
}
public void moveBall(){
// Ball should really be its own class with this as a method.
if (right) ball.x+=1; // If right is true, move ball right,
else ball.x-=1; // otherwise move left.
if (down) ball.y+=1; // Same for up/down.
else ball.y-=1;
if (ball.x > (bounds.width - ball.width)) // Detect edges and bounce.
{ right = false; ball.x = bounds.width - ball.width; }
if (ball.y > (bounds.height - ball.height))
{ down = false; ball.y = bounds.height - ball.height;}
if (ball.x == 0) { right = true; ball.x = 0; }
if (ball.y == 0) { down = true; ball.y = 0; }
}
public void keyPressed(KeyEvent evt) {
if(evt.getKeyCode() == KeyEvent.VK_G && block.x > 0) {
block.x -= 20;
}
if(evt.getKeyCode() == KeyEvent.VK_H && block.x < 540) {
block.x += 20;
}
}
public void keyTyped(KeyEvent evt){ }
public void keyReleased(KeyEvent evt){ }
public void startActionPerformed(java.awt.event.ActionEvent evt) {
starten = true;
}
public static void main(String arg[]){
java.util.Timer vgTimer = new java.util.Timer(); // Create a Timer.
VGKernel panel = new VGKernel(); // Create and instance of our kernel.
// Set the intial ball movement direction.
panel.down = true;
panel.right = true;
// Set up our JFRame
panel.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel.frame.setSize(panel.screen.width, panel.screen.height);
panel.frame.setContentPane(panel);
panel.frame.setVisible(true);
// Set up a timer to do the vgTask regularly.
vgTimer.schedule(panel.vgTask, 0, 10);
}
}
You need to make sure whatever component you're listening for KeyEvents on has the focus.
Once you click the button, that button has focus instead of your JPanel. More info here: http://docs.oracle.com/javase/tutorial/uiswing/misc/focus.html
You can use the requestFocusInWindow() method on your JPanel, or you can call setFocusable(false) on your JButton.
Or you could use key bindings instead: http://docs.oracle.com/javase/tutorial/uiswing/misc/keybinding.html
Add VGKernel.this.grabFocus(); to the ActionListener to return focus to the VGKernel panel after clicking the button.
start.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
starten = true;
VGKernel.this.grabFocus();
}
});
I have a screen with say 500 width and 400 height, and I have a vector with a bunch of shapes. let say the vector has 2 different shapes for example. I want the object to randomly pop up from the bottom of the screen reach a certain ascent and then fall back down (similar to game fruit ninja, where the fruits are my shapes).
In my main (view) I have a vector of shapes of which i instantiate the timers, add to array and place them in the buttom of the screen using the translate function. My timer takes in an action listener which basically changes the translate of the shape to move up till ascent and then down, but my problem is that all the shapes start at the same time regardless.
Something like this:
Shape f = new Shape(new Area(new Ellipse2D.Double(0, 50, 50, 50)));
f.translate(0, 400);
f.timer = new Timer( 10 , taskPerformer);
f.timer.start();
vector.add(f);
Shape f2 = new Shape(new Area(new Rectangle2D.Double(0, 50, 50, 50)));
f2.translate(200, 400);
f2.timer = new Timer( 10 , taskPerformer);
f2.timer.setInitialDelay(5000);
f2.timer.start();
vector.add(f2);
and my action listener:
Random generator = new Random();
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
//...Perform a task...
for (Shape s : model.getShapes()) {
// Scale object using translate
// once reached ascent drop down
// translate to diffrenet part of the bottom of the screen
// delay its timer
}
update();
//basically repaints
}
};
I'm running into problems that all shapes follow the same timer, and begin to pop up at the same time (no delay) ...
Any suggestions on how to avoid this or if there is a different approach i should try
"I want the object to randomly pop up from the bottom of the screen reach a certain ascent and then fall back down"
See the runnable example below. What I do is pass a radomDelayedStart to the Shape. Every tick of the timer, the randomDelayedStart decreases til it reaches 0, that's when the flag to be drawn in raised. Most of the logic is in the Shape class methods, which are called in the Timers Actionlistener. Everything is done in one Timer. For the ascent, I just used a hard coded 50, but you can also pass a random ascent to the Shape. Let me know if you have any questions. I tried to made the code as clear as possible.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
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.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class RandomShape extends JPanel {
private static final int D_HEIGHT = 500;
private static final int D_WIDTH = 400;
private static final int INCREMENT = 8;
private List<Shape> shapes;
private List<Color> colors;
private Timer timer = null;
public RandomShape() {
colors = createColorList();
shapes = createShapeList();
timer = new Timer(30, new ActionListener() {
public void actionPerformed(ActionEvent e) {
for (Shape shape : shapes) {
shape.move();
shape.decreaseDelay();
repaint();
}
}
});
JButton start = new JButton("Start");
start.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
timer.start();
}
});
JButton reset = new JButton("Reset");
reset.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
shapes = createShapeList();
timer.restart();
}
});
JPanel panel = new JPanel();
panel.add(start);
panel.add(reset);
setLayout(new BorderLayout());
add(panel, BorderLayout.PAGE_START);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Shape shape : shapes) {
shape.drawShape(g);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(D_WIDTH, D_HEIGHT);
}
private List<Color> createColorList() {
List<Color> list = new ArrayList<>();
list.add(Color.BLUE);
list.add(Color.GREEN);
list.add(Color.ORANGE);
list.add(Color.MAGENTA);
list.add(Color.CYAN);
list.add(Color.PINK);
return list;
}
private List<Shape> createShapeList() {
List<Shape> list = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < 20; i++) {
int randXLoc = random.nextInt(D_WIDTH);
int randomDelayedStart = random.nextInt(100);
int colorIndex = random.nextInt(colors.size());
Color color = colors.get(colorIndex);
list.add(new Shape(randXLoc, randomDelayedStart, color));
}
return list;
}
class Shape {
int randXLoc;
int y = D_HEIGHT;
int randomDelayedStart;
boolean draw = false;
boolean down = false;
Color color;
public Shape(int randXLoc, int randomDelayedStart, Color color) {
this.randXLoc = randXLoc;
this.randomDelayedStart = randomDelayedStart;
this.color = color;
}
public void drawShape(Graphics g) {
if (draw) {
g.setColor(color);
g.fillOval(randXLoc, y, 30, 30);
}
}
public void move() {
if (draw) {
if (y <= 50) {
down = true;
}
if (down) {
y += INCREMENT;
} else {
y -= INCREMENT;
}
}
}
public void decreaseDelay() {
if (randomDelayedStart <= 0) {
draw = true;
} else {
randomDelayedStart -= 1;
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.add(new RandomShape());
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
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.