Change ball color when colliding - java

I'm making an java app (for exercise) where there must be a panel and 2 buttons.
Each time you press the start button a ball must be displayed and it moves based on thread. The user can display all the way up to 10 independent balls.
By pressing the stop button, 1 ball must be removed with each time the stop button is pressed (example, when there is 4 balls, the user must press 4 times stop button to remove all the balls independently)
All the x- and y coordinates of the balls must be stored in a Matrix
When 1 or more ball(s) are colliding with each other, only the colliding balls must change color from red to blue
Ok I'm almost done with it completely ( from point 1 to 4 ), but here comes my problem. When a ball is colliding with another, instead of changing the colors of the colliding balls to blue, my code is changing all the balls color from red to blue. I know that my error lies at the new Balls().setColor(Color.Blue), but I have no idea how to change only the colliding balls.
Below follows a screen shot of the java app and the code.
Can anyone help me with this headache?
Printscreen:
source code:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;`
public class BouncingBalls extends JPanel implements ActionListener {
protected List<Ball> balls = new ArrayList<Ball>(10);
private final Container container;
private final DrawCanvas canvas;
private int canvasWidth;
private int canvasHeight;
public JButton start, stop;
int [][] coords= new int[11][2];
int ammountOfBalls = 0;
static Color clr= Color.RED;
public static int random(int maxRange) {
return (int) Math.round((Math.random() * maxRange));
}
public BouncingBalls(int width, int height) {
setLayout(new FlowLayout());
start= new JButton("start");
start.addActionListener(this);
stop= new JButton("stop");
stop.addActionListener(this);
add(start);
add(stop);
add(new JLabel(""));
container = new Container();
canvasWidth = width;
canvasHeight = height;
canvas = new DrawCanvas();
this.setLayout(new FlowLayout());
this.add(canvas);
start();
}
public void start() {
Thread t = new Thread() {
#Override
public void run() {
while (true) {
update();
getPositions();
collisionDetection();
repaint();
try {
Thread.sleep(30);
} catch (InterruptedException e) {
}
}
}
private void collisionDetection() {
// The algorithm that detects collision
for(int i=0;i<ammountOfBalls;i++){
for(int j=i+1; j<ammountOfBalls; j++){
if(collisionMethod(i,j)){ // my collision method
//HOW DO I CHANGE ONLY THE COLLIDING BALLS COLOR HERE????
new Ball().setColor(Color.BLUE); // this line here changes the color of all the balls on the panel
System.out.println("Its a hit");
}
}
}
}
private void getPositions() {
int row=0;
for (Ball ball : balls) {
int x =ball.getXPosition();
int y =ball.getYPosition();
coords[row][0]=x;
coords[row][1]=y;
row++;
}
}
private boolean collisionMethod(int i, int j) {
float xd = coords[i][0]-coords[j][0];
float yd=coords[i][1]-coords[j][1];
float radius= new Ball().ballRadius;
float sqrRadius= radius * radius;
float distSqr= (xd * xd) + (yd * yd);
if(distSqr <= sqrRadius)
return true;
return false;
}
};
t.start();
}
public void update() {
for (Ball ball : balls) {
ball.move(container);
}
}
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() == start){
if(ammountOfBalls < 10){
// to limit the ammount of balls to 10
balls.add(new Ball());
ammountOfBalls++;
}
}
else if( e.getSource() == stop){
if(ammountOfBalls > 0){
ammountOfBalls --;
balls.remove(ammountOfBalls);
}
}
}
class DrawCanvas extends JPanel {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
container.draw(g);
for (Ball ball : balls) {
ball.draw(g);
}
}
#Override
public Dimension getPreferredSize() {
return (new Dimension(700, 400));
}
}
public static void main(String[] args) {
JFrame f = new JFrame("Bouncing Balls");
f.setDefaultCloseOperation(f.EXIT_ON_CLOSE);
f.setPreferredSize(new Dimension(800,500));
f.setContentPane(new BouncingBalls(800,800));
f.pack();
f.setVisible(true);
}
public static class Ball {
public int random(int maxRange) {
return (int) Math.round(Math.random() * maxRange);
}
int x = random(700); // method to get random coords for the x-value
int y = random(400); // method to get random coords for the y-value ...... these are used to get random positions for the balls instead of only static
int xMovement = 10;
int yMovement = 10;
int ballRadius = 20;
int i = 0;
public Color getColor(){
return clr;
}
public Color setColor(Color color){
clr=color;
return clr;
}
public void draw(Graphics g) {
g.setColor(getColor());
g.fillOval(x,y,ballRadius,ballRadius);
}
public int getXPosition(){
return x;
}
public int getYPosition(){
return y;
}
public void move(Container container) {
x += xMovement;
y += yMovement;
if (x - ballRadius < 0) {
xMovement = -xMovement;
x = ballRadius;
}
else if (x + ballRadius > 700) {
xMovement = -xMovement;
x = 700 - ballRadius;
}
if (y - ballRadius < 0) {
yMovement = -yMovement;
y = ballRadius;
}
else if (y + ballRadius > 400) {
yMovement = -yMovement;
y = 400 - ballRadius;
}
}
}
public static class Container {
private static final int hightPanel = 800;
private static final int widthPanel= 800;
public void draw(Graphics g) {
g.setColor(Color.WHITE);
g.fillRect(0, 0, widthPanel, hightPanel);
}
}
}
`

You have defined clr as a static field of your application. When your Ball class calls setColor() you are changing the value of clr to blue... and then any Ball that calls getColor() will see that clr is blue.
Solution: Don't make clr an application-wide static field. Define it in the Ball class as a non-static field, so each Ball has its own color.

Related

Snake game in Java but my restart button does not work

My restart button of the game does not work and it multiples when it is clicked. I do not understand Java perfectly I am considering myself good.
Main of the game
package snake_game;
public class snake {
public static void main(String arg[]) {
new GameFrame();
// is exacly the same as frame f = new frame();
// this is shorter and does the same job
}
}
GamePanel
package snake_game;
// import java.awt.event.ActionEvent;
// import java.awt.event.ActionListener;
// import java.awt.Graphics;
// import java.awt.event.KeyAdapter;
// import java.awt.event.KeyEvent;
// or I could write simply
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.Random;
import javax.swing.JPanel;
public class GamePanel extends JPanel implements ActionListener {
// panal dimentions
static final int SCREEN_WIDTH = 600;
static final int SCREEN_HEIGHT = 600;
// panal dimentions
// size
static final int UNIT_SIZE = 25;
// size to make 600 * 600 = 1200 px equel between 25 px
static final int GAME_UNITS = (SCREEN_WIDTH * SCREEN_HEIGHT) / UNIT_SIZE;
// size
// delay how fast the game will be
static final int DELAY = 75;
// delay
// dimentions
final int x[] = new int[GAME_UNITS];
final int y[] = new int[GAME_UNITS];
// dimentions
// snake
int bodyParts = 6;
// snake
// apple
int appleEaten;
int appleX;
int appleY;
// apple
char direction = 'R';
boolean running = false;
Timer timer;
Random random;
GamePanel game;
JButton resetButton;
GamePanel() {
random = new Random();
this.setPreferredSize(new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT));
this.setBackground(Color.black);
this.setFocusable(true);
this.addKeyListener(new MyKeyAdapter());
startGame();
// when we want to make the program to continie we must say what the programm
// must execute next
}
public void startGame() {
newApple();
running = true;
timer = new Timer(DELAY, this);
timer.start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
draw(g);
}
public void draw(Graphics g) {
if (running) {
for (int i = 0; i < SCREEN_HEIGHT / UNIT_SIZE; i++) {
g.drawLine(i * UNIT_SIZE, 0, i * UNIT_SIZE, SCREEN_HEIGHT);
g.drawLine(0, i * UNIT_SIZE, i * SCREEN_WIDTH, i * UNIT_SIZE);
}
g.setColor(Color.red);
g.fillOval(appleX, appleY, UNIT_SIZE, UNIT_SIZE);
for (int i = 0; i < bodyParts; i++) {
if (i == 0) {
g.setColor(Color.green);
g.fillRect(x[i], y[i], UNIT_SIZE, UNIT_SIZE);
} else {
g.setColor(new Color(45, 180, 0));
// random color
g.setColor(new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255)));
// random color
g.fillRect(x[i], y[i], UNIT_SIZE, UNIT_SIZE);
}
}
g.setColor(Color.red);
g.setFont(new Font("Ink Free", Font.BOLD, 30));
FontMetrics metrics = getFontMetrics(g.getFont());
g.drawString("SCORE:" + appleEaten, (SCREEN_WIDTH - metrics.stringWidth("SCORE:" + appleEaten)) / 2,
g.getFont().getSize());
} else {
gameOver(g);
}
}
public void newApple() {
appleX = random.nextInt((int) (SCREEN_WIDTH / UNIT_SIZE)) * UNIT_SIZE;
appleY = random.nextInt((int) (SCREEN_HEIGHT / UNIT_SIZE)) * UNIT_SIZE;
}
public void move() {
for (int i = bodyParts; i > 0; i--) {
x[i] = x[i - 1];
y[i] = y[i - 1];
}
switch (direction) {
case 'U':
y[0] = y[0] - UNIT_SIZE;
break;
case 'D':
y[0] = y[0] + UNIT_SIZE;
break;
case 'L':
x[0] = x[0] - UNIT_SIZE;
break;
case 'R':
x[0] = x[0] + UNIT_SIZE;
break;
}
}
public void checkApple() {
if ((x[0] == appleX) && (y[0] == appleY)) {
bodyParts++;
appleEaten++;
newApple();
}
}
public void checkCollisions() {
// check if head collides with body
for (int i = bodyParts; i > 0; i--) {
if ((x[0] == x[i]) && (y[0] == y[i])) {
running = false;
}
}
// check if head touches left border
if (x[0] < 0) {
running = false;
}
// check if head touches right border
if (x[0] > SCREEN_WIDTH) {
running = false;
}
// check if head touches top border
if (y[0] < 0) {
running = false;
}
// check if head touches bottom border
if (y[0] > SCREEN_HEIGHT) {
running = false;
}
if (!running) {
timer.stop();
}
}
public void gameOver(Graphics g) {
// score
g.setColor(Color.red);
g.setFont(new Font("Ink Free", Font.BOLD, 30));
FontMetrics metrics1 = getFontMetrics(g.getFont());
g.drawString("SCORE:" + appleEaten, (SCREEN_WIDTH - metrics1.stringWidth("SCORE:" + appleEaten)) / 2,
g.getFont().getSize());
// game over text
g.setColor(Color.red);
g.setFont(new Font("Ink Free", Font.BOLD, 75));
FontMetrics metrics2 = getFontMetrics(g.getFont());
g.drawString("Game Over", (SCREEN_WIDTH - metrics2.stringWidth("Game Over")) / 2, SCREEN_HEIGHT / 2);
// restart button
resetButton = new JButton();
resetButton.setText("Restart");
resetButton.setSize(100, 50);
resetButton.setLocation(150, 150);
resetButton.addActionListener(this);
game = new GamePanel();
this.add(resetButton);
this.add(game);
this.setVisible(true);
// restart button
}
#Override
public void actionPerformed(ActionEvent e) {
if (running) {
move();
checkApple();
checkCollisions();
}
repaint();
// restart button
if (e.getSource() == resetButton) {
this.remove(game);
game = new GamePanel();
this.add(game);
resetButton.setVisible(false);
SwingUtilities.updateComponentTreeUI(this);
// restart button
}
}
public class MyKeyAdapter extends KeyAdapter {
#Override
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_LEFT:
if (direction != 'R') {
direction = 'L';
}
break;
case KeyEvent.VK_RIGHT:
if (direction != 'L') {
direction = 'R';
}
break;
case KeyEvent.VK_UP:
if (direction != 'D') {
direction = 'U';
}
break;
case KeyEvent.VK_DOWN:
if (direction != 'U') {
direction = 'D';
}
break;
}
}
}
}
GameFrame
package snake_game;
import javax.swing.JFrame;
public class GameFrame extends JFrame {
GameFrame() {
GamePanel panel = new GamePanel();
this.add(panel);
this.setTitle("Snake");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setResizable(false);
this.pack();
this.setVisible(true);
this.setLocationRelativeTo(null);
this.setUndecorated(false);
}
}
Introduction
I copied your code into my Eclipse IDE and ran it as is. I received the following runtime error.
Exception in thread "main" java.awt.IllegalComponentStateException: The frame is displayable.
at java.desktop/java.awt.Frame.setUndecorated(Frame.java:926)
at com.ggl.testing.SnakeGame$GameFrame.<init>(SnakeGame.java:41)
at com.ggl.testing.SnakeGame.main(SnakeGame.java:24)
Oracle has a helpful tutorial, Creating a GUI With Swing. Skip the Learning Swing with the NetBeans IDE section. Pay close attention to the Concurrency in Swing section.
I've been writing Swing code for over 10 years, and I have the Oracle website bookmarked in my browser. I still look up how to use certain components to make sure I'm using them correctly.
The first thing I did was to slow down your snake so I could test the game. I changed the delay of 75 to a delay of 750. Here's a screenshot of your current GUI.
Looking over your code, you extend a JFrame. You don't need to extend a JFrame. You're not changing any JFrame functionality. It's much simpler to use a JFrame. This leads to one of my Java rules.
Don't extend a Swing component, or any Java class, unless you intend
to override one or more of the class methods.
You do extend a JPanel. That's fine because you override the paintComponent method.
Finally, your JPanel class is doing too much work. You also make heavy use of static fields. Even though you'll only create one JPanel, it's a good habit to treat every class as if you will create multiple instances of the class. This creates fewer problems for yourself down the road.
You have the right idea, creating three classes.
So let's rework your code, using some basic patterns and Swing best practices. At this time, I don't know how many classes we'll wind up creating.
Explanation
When I write a Swing GUI, I use the model–view–controller (MVC) pattern. The name implies that you create a model first, then the view, then the controller(s).
An application model consists of one or more plain Java getter/setter classes.
A view consists of a JFrame, one or more JPanels, and any other necessary Swing components.
The controller consists of one or more Actions or ActionListeners. In Swing, there's usually not one controller to "rule them all".
To summarize:
The view reads information from the model
The view does not update the model
The controllers update the model and repaint/revalidate your view.
Model
I created two model classes, SnakeModel and Snake.
The SnakeModel class is a plain Java getter/setter class that holds one Snake instance, the apple eaten count, the apple location, the size of the game area, and a couple of booleans. One boolean indicates whether or not the game loop is running and the other boolean indicates whether or not the game is over.
The game area uses a java.awt.Dimension to hold the width and height of the game area. The width and the height do not have to have the same value. The game area can be rectangular.
The game area is measured in units. In the view, I convert the units into pixels. That's the opposite of what you did. If you want to change the game area, all you have to do is change the dimensions in the SnakeModel class. Everything in the view is based on the game area dimension.
The Snake class holds a java.util.List of java.awt.Point objects and a char direction. A java.awt.Point object holds an X and Y value. Since we're dealing with objects, rather than int values, we have to be careful to clone the object when we want a new Point.
View
All Swing applications must start with a call to the SwingUtilities invokeLater method. This method ensures that the Swing components are created and executed on the Event Dispatch Thread.
I created a JFrame, a drawing JPanel, and a separate button JPanel. Generally, it's not a good idea to add Swing components to a drawing JPanel. By creating a separate button JPanel, I get the added feature of a "Start Game" button at almost no extra cost. The button is disabled while the game is running.
The JFrame methods must be called in a specific order. The setVisible method must be called last.
I made the drawing JPanel more complicated by adding a separate area for the score.
I made the drawing JPanel less complicated by only drawing the state of the game, based on the application model. Period. Nothing else.
I limited the random colors to the white end of the color spectrum to maintain the contrast between the snake and the drawing JPanel background.
I used key bindings instead of a key listener. One advantage is that the drawing JPanel doesn't have to be in focus. Since I have a separate button JPanel, the drawing JPanel doesn't have focus.
Another advantage is that I can add the WASD keys with four lines of additional code.
One disadvantage is that the key bindings code looks more complicated than a key listener. Once you've coded a few key bindings, you'll appreciate the advantages.
Controller
I created three controller classes, ButtonListener, TimerListener, and MovementAction.
The ButtonListener class implements ActionListener. The ButtonListener class initializes the game model and restarts the timer.
The TimerListener class implements ActionListener. The TimerListener class is the game loop. This class moves the snake, checks to see if the apple is eaten, checks to see if the snake has moved outside the game area or touched itself, and repaints the drawing JPanel. I used your code as a model for the code in this class.
The MovementAction class extends AbstractAction. The AbstractAction class implements Action. This class changes the direction of the snake, based on the key presses.
I create four instances of the MovementAction class, one for each direction. This makes the actionPerformed method of the class much simpler.
Images
Here's what the revised GUI looks like when you start the game.
Here's the revised GUI during the game.
Here's the revised GUI when the game is over.
Code
Here's the complete runnable code. I made all the additional classes inner classes so I could post this code as one block.
You should put the separate classes in separate files.
When setting up a Swing GUI project, I create separate packages for the model, view, and controller. This helps me keep the code organized.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.BorderFactory;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class SnakeGame implements Runnable {
public static void main(String arg[]) {
SwingUtilities.invokeLater(new SnakeGame());
}
private final GamePanel gamePanel;
private final JButton restartButton;
private final SnakeModel model;
public SnakeGame() {
this.model = new SnakeModel();
this.restartButton = new JButton("Start Game");
this.gamePanel = new GamePanel(model);
}
#Override
public void run() {
JFrame frame = new JFrame("Snake");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(gamePanel, BorderLayout.CENTER);
frame.add(createButtonPanel(), BorderLayout.SOUTH);
frame.pack();
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private JPanel createButtonPanel() {
JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
panel.setBackground(Color.black);
restartButton.addActionListener(new ButtonListener(this, model));
panel.add(restartButton);
return panel;
}
public JButton getRestartButton() {
return restartButton;
}
public void repaint() {
gamePanel.repaint();
}
public class GamePanel extends JPanel {
private static final long serialVersionUID = 1L;
private final int margin, scoreAreaHeight, unitSize;
private final Random random;
private final SnakeModel model;
public GamePanel(SnakeModel model) {
this.model = model;
this.margin = 10;
this.unitSize = 25;
this.scoreAreaHeight = 36 + margin;
this.random = new Random();
this.setBackground(Color.black);
Dimension gameArea = model.getGameArea();
int width = gameArea.width * unitSize + 2 * margin;
int height = gameArea.height * unitSize + 2 * margin + scoreAreaHeight;
this.setPreferredSize(new Dimension(width, height));
setKeyBindings();
}
private void setKeyBindings() {
InputMap inputMap = this.getInputMap(JPanel.WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = this.getActionMap();
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "up");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "down");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "left");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "right");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0), "up");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0), "down");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0), "left");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0), "right");
actionMap.put("up", new MovementAction(model, 'U', 'D'));
actionMap.put("down", new MovementAction(model, 'D', 'U'));
actionMap.put("left", new MovementAction(model, 'L', 'R'));
actionMap.put("right", new MovementAction(model, 'R', 'L'));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Dimension gameArea = model.getGameArea();
drawHorizontalGridLines(g, gameArea);
drawVerticalGridLines(g, gameArea);
drawSnake(g);
drawScore(g, gameArea);
if (model.isGameOver) {
drawGameOver(g, gameArea);
} else {
drawApple(g);
}
}
private void drawHorizontalGridLines(Graphics g, Dimension gameArea) {
int y1 = scoreAreaHeight + margin;
int y2 = y1 + gameArea.height * unitSize;
int x = margin;
for (int index = 0; index <= gameArea.width; index++) {
g.drawLine(x, y1, x, y2);
x += unitSize;
}
}
private void drawVerticalGridLines(Graphics g, Dimension gameArea) {
int x1 = margin;
int x2 = x1 + gameArea.width * unitSize;
int y = margin + scoreAreaHeight;
for (int index = 0; index <= gameArea.height; index++) {
g.drawLine(x1, y, x2, y);
y += unitSize;
}
}
private void drawApple(Graphics g) {
// Draw apple
g.setColor(Color.red);
Point point = model.getAppleLocation();
if (point != null) {
int a = point.x * unitSize + margin + 1;
int b = point.y * unitSize + margin + scoreAreaHeight + 1;
g.fillOval(a, b, unitSize - 2, unitSize - 2);
}
}
private void drawScore(Graphics g, Dimension gameArea) {
g.setColor(Color.red);
g.setFont(new Font("Ink Free", Font.BOLD, 36));
FontMetrics metrics = getFontMetrics(g.getFont());
int width = 2 * margin + gameArea.width * unitSize;
String text = "SCORE: " + model.getApplesEaten();
int textWidth = metrics.stringWidth(text);
g.drawString(text, (width - textWidth) / 2, g.getFont().getSize());
}
private void drawSnake(Graphics g) {
// Draw snake
Snake snake = model.getSnake();
List<Point> cells = snake.getCells();
Point cell = cells.get(0);
drawSnakeCell(g, cell, Color.green);
for (int index = 1; index < cells.size(); index++) {
// Color color = new Color(45, 180, 0);
// random color
Color color = new Color(getColorValue(), getColorValue(),
getColorValue());
cell = cells.get(index);
drawSnakeCell(g, cell, color);
}
}
private void drawSnakeCell(Graphics g, Point point, Color color) {
int x = margin + point.x * unitSize;
int y = margin + scoreAreaHeight + point.y * unitSize;
if (point.y >= 0) {
g.setColor(color);
g.fillRect(x, y, unitSize, unitSize);
}
}
private int getColorValue() {
// White has color values of 255
return random.nextInt(64) + 191;
}
private void drawGameOver(Graphics g, Dimension gameArea) {
g.setColor(Color.red);
g.setFont(new Font("Ink Free", Font.BOLD, 72));
FontMetrics metrics = getFontMetrics(g.getFont());
String text = "Game Over";
int textWidth = metrics.stringWidth(text);
g.drawString(text, (getWidth() - textWidth) / 2, getHeight() / 2);
}
}
public class ButtonListener implements ActionListener {
private final int delay;
private final SnakeGame view;
private final SnakeModel model;
private final Timer timer;
public ButtonListener(SnakeGame view, SnakeModel model) {
this.view = view;
this.model = model;
this.delay = 750;
this.timer = new Timer(delay, new TimerListener(view, model));
}
#Override
public void actionPerformed(ActionEvent event) {
JButton button = (JButton) event.getSource();
String text = button.getText();
if (text.equals("Start Game")) {
button.setText("Restart Game");
}
button.setEnabled(false);
model.initialize();
timer.restart();
}
}
public class TimerListener implements ActionListener {
private final SnakeGame view;
private final SnakeModel model;
public TimerListener(SnakeGame view, SnakeModel model) {
this.view = view;
this.model = model;
}
#Override
public void actionPerformed(ActionEvent event) {
moveSnake();
checkApple();
model.checkCollisions();
if (model.isGameOver()) {
Timer timer = (Timer) event.getSource();
timer.stop();
model.setRunning(false);
view.getRestartButton().setEnabled(true);
}
view.repaint();
}
private void moveSnake() {
Snake snake = model.getSnake();
Point head = (Point) snake.getHead().clone();
switch (snake.getDirection()) {
case 'U':
head.y--;
break;
case 'D':
head.y++;
break;
case 'L':
head.x--;
break;
case 'R':
head.x++;
break;
}
snake.removeTail();
snake.addHead(head);
// System.out.println(Arrays.toString(cells.toArray()));
}
private void checkApple() {
Point appleLocation = model.getAppleLocation();
Snake snake = model.getSnake();
Point head = snake.getHead();
Point tail = (Point) snake.getTail().clone();
if (head.x == appleLocation.x && head.y == appleLocation.y) {
model.incrementApplesEaten();
snake.addTail(tail);
model.generateRandomAppleLocation();
}
}
}
public class MovementAction extends AbstractAction {
private static final long serialVersionUID = 1L;
private final char newDirection, oppositeDirection;
private final SnakeModel model;
public MovementAction(SnakeModel model, char newDirection,
char oppositeDirection) {
this.model = model;
this.newDirection = newDirection;
this.oppositeDirection = oppositeDirection;
}
#Override
public void actionPerformed(ActionEvent event) {
if (model.isRunning()) {
Snake snake = model.getSnake();
char direction = snake.getDirection();
if (direction != oppositeDirection && direction != newDirection) {
snake.setDirection(newDirection);
// System.out.println("New direction: " + newDirection);
}
}
}
}
public class SnakeModel {
private boolean isGameOver, isRunning;
private int applesEaten;
private Dimension gameArea;
private Point appleLocation;
private Random random;
private Snake snake;
public SnakeModel() {
this.random = new Random();
this.snake = new Snake();
this.gameArea = new Dimension(24, 24);
}
public void initialize() {
this.isRunning = true;
this.isGameOver = false;
this.snake.initialize();
this.applesEaten = 0;
Point point = generateRandomAppleLocation();
// Make sure first apple isn't under snake
int y = (point.y == 0) ? 1 : point.y;
this.appleLocation = new Point(point.x, y);
}
public void checkCollisions() {
Point head = snake.getHead();
// Check for snake going out of the game area
if (head.x < 0 || head.x > gameArea.width) {
isGameOver = true;
return;
}
if (head.y < 0 || head.y > gameArea.height) {
isGameOver = true;
return;
}
// Check for snake touching itself
List<Point> cells = snake.getCells();
for (int index = 1; index < cells.size(); index++) {
Point cell = cells.get(index);
if (head.x == cell.x && head.y == cell.y) {
isGameOver = true;
return;
}
}
}
public Point generateRandomAppleLocation() {
int x = random.nextInt(gameArea.width);
int y = random.nextInt(gameArea.height);
this.appleLocation = new Point(x, y);
return getAppleLocation();
}
public void incrementApplesEaten() {
this.applesEaten++;
}
public boolean isRunning() {
return isRunning;
}
public void setRunning(boolean isRunning) {
this.isRunning = isRunning;
}
public boolean isGameOver() {
return isGameOver;
}
public void setGameOver(boolean isGameOver) {
this.isGameOver = isGameOver;
}
public Dimension getGameArea() {
return gameArea;
}
public int getApplesEaten() {
return applesEaten;
}
public Point getAppleLocation() {
return appleLocation;
}
public Snake getSnake() {
return snake;
}
}
public class Snake {
private char direction;
private List<Point> cells;
public Snake() {
this.cells = new ArrayList<>();
initialize();
}
public void initialize() {
this.direction = 'R';
cells.clear();
for (int x = 5; x >= 0; x--) {
cells.add(new Point(x, 0));
}
}
public void addHead(Point head) {
cells.add(0, head);
}
public void addTail(Point tail) {
cells.add(tail);
}
public void removeTail() {
cells.remove(cells.size() - 1);
}
public Point getHead() {
return cells.get(0);
}
public Point getTail() {
return cells.get(cells.size() - 1);
}
public char getDirection() {
return direction;
}
public void setDirection(char direction) {
this.direction = direction;
}
public List<Point> getCells() {
return cells;
}
}
}

How do I paint multiple objetcs that move at different speeds in Java?

I am working on homework for class, and its late because I can't seem to understand the material despite all the research that I am doing. I am a beginner and do not know much in the way of java. Also, this is my first post so please be forgiving when you are reading this.
I am building on source code from my textbook, which I updated recently for past homework, but now I am trying to generate a class that draws multiple squares and moves those objects independently and at different speeds. They will all need to rebound off the walls as well. I followed the instructions and created two arrays that will hold the random x and y values between 1 and 10. However, I struggle with arrays and I am sure that I am not doing it correctly. So, I would love some feedback to see if I have it set up correctly.
I have a the jpanel pulling up and drawing, and as long as there is 1 square it is working fine bouncing off the walls, but things change when I draw more than one. The do not move independently and they also share the same speed. Some even disappear from time to time. This has really thrown me off. I appreciate any help!
In short, I am trying to paint new squares that all travel in different directions and at different speeds. Per the instructions we are suppose create and use a two arrays that handle the x and y values.
Here is what I have so far:
public class DotsPanel extends JPanel
{
private int delay = 15;
private final int SIZE = 7, IMAGE_SIZE = 3; // radius of each dot
private Timer timer;
private int x, y, i;
private ArrayList<Point> pointList;
static int [] xarray = new int [1000];
static int [] yarray = new int [1000];
Random rand = new Random();
//-----------------------------------------------------------------
// Constructor: Sets up this panel to listen for mouse events.
//-----------------------------------------------------------------
public DotsPanel()
{
pointList = new ArrayList<Point>();
int [] xarray = new int [1000];
int [] yarray = new int [1000];
timer = new Timer(delay, new ReboundListener());
addMouseListener (new DotsListener());
addMouseMotionListener (new DotsListener());
setBackground(Color.gray);
setPreferredSize(new Dimension(700, 500));
for(int i = 0; i < xarray.length; i++)
{
xarray[i] = rand.nextInt(7);
yarray[i] = rand.nextInt(7);
}
timer.start();
}
//-----------------------------------------------------------------
// Draws all of the dots stored in the list.
//-----------------------------------------------------------------
public void paintComponent(Graphics page)
{
super.paintComponent(page);
page.setColor(Color.BLUE);
for (Point spot : pointList)
{
page.fillRect(spot.x-SIZE, spot.y-SIZE, 25, 25);
page.drawString("Count: " + pointList.size(), 5, 15);
}
}
//*****************************************************************
// Represents the listener for mouse events.
//*****************************************************************
private class DotsListener implements MouseListener, MouseMotionListener
{
//--------------------------------------------------------------
// Adds the current point to the list of points and redraws
// the panel whenever the mouse button is pressed.
//--------------------------------------------------------------
public void mousePressed(MouseEvent event)
{
pointList.add(event.getPoint());
repaint();
}
public void mouseDragged(MouseEvent event)
{
// initially I had two xarray and yarray in here just like in
// mouseClicked
// but it did not change anything when removed
}
//--------------------------------------------------------------
// Provide empty definitions for unused event methods.
//--------------------------------------------------------------
public void mouseClicked(MouseEvent event)
{
xarray[i] = rand.nextInt(7);
yarray[i] = rand.nextInt(7);
}
public void mouseReleased(MouseEvent event) {}
public void mouseEntered(MouseEvent event) {}
public void mouseExited(MouseEvent event) {}
public void mouseMoved(MouseEvent e) {}
}
private class ReboundListener implements ActionListener
{
//--------------------------------------------------------------
// Updates the position of the image and possibly the direction
// of movement whenever the timer fires an action event.
//--------------------------------------------------------------
public void actionPerformed(ActionEvent event)
{
for (Point spot : pointList)
{
spot.x += xarray[i];
spot.y += yarray[i];
if (spot.x <= 0 || spot.x >= 700)
xarray[i] = xarray[i] * -1;
if (spot.y <= 0 || spot.y >= 500)
yarray[i] = yarray[i] * -1;
repaint();
}
}
}
}
However, I struggle with arrays and I am sure that I am not doing it correctly.
I wouldn't use Arrays.
Instead, have a Ball object manage its own state. Then you can have different color, speed, size etc for each Ball. Then when the Timer fires you just calculate the new position and repaint the Ball.
Here is an example to get you started:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;
public class BallAnimation4
{
private static void createAndShowUI()
{
BallPanel panel = new BallPanel();
JFrame frame = new JFrame("BallAnimation4");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( panel );
frame.setSize(800, 600);
frame.setLocationRelativeTo( null );
//frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.setVisible( true );
panel.addBalls(5);
panel.startAnimation();
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
class BallPanel extends JPanel implements ActionListener
{
private ArrayList<Ball> balls = new ArrayList<Ball>();
public BallPanel()
{
setLayout( null );
setBackground( Color.BLACK );
}
public void addBalls(int ballCount)
{
Random random = new Random();
for (int i = 0; i < ballCount; i++)
{
Ball ball = new Ball();
ball.setRandomColor(true);
ball.setLocation(random.nextInt(getWidth()), random.nextInt(getHeight()));
ball.setMoveRate(32, 32, 1, 1, true);
// ball.setMoveRate(16, 16, 1, 1, true);
ball.setSize(32, 32);
balls.add( ball );
}
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
for (Ball ball: balls)
{
ball.draw(g);
}
}
public void startAnimation()
{
Timer timer = new Timer(75, this);
timer.start();
}
public void actionPerformed(ActionEvent e)
{
move();
repaint();
}
private void move()
{
for (Ball ball : balls)
{
ball.move(this);
}
}
class Ball
{
public Color color = Color.BLACK;
public int x = 0;
public int y = 0;
public int width = 1;
public int height = 1;
private int moveX = 1;
private int moveY = 1;
private int directionX = 1;
private int directionY = 1;
private int xScale = moveX;
private int yScale = moveY;
private boolean randomMove = false;
private boolean randomColor = false;
private Random myRand = null;
public Ball()
{
myRand = new Random();
setRandomColor(randomColor);
}
public void move(JPanel parent)
{
int iRight = parent.getSize().width;
int iBottom = parent.getSize().height;
x += 5 + (xScale * directionX);
y += 5 + (yScale * directionY);
if (x <= 0)
{
x = 0;
directionX *= (-1);
xScale = randomMove ? myRand.nextInt(moveX) : moveX;
if (randomColor) setRandomColor(randomColor);
}
if (x >= iRight - width)
{
x = iRight - width;
directionX *= (-1);
xScale = randomMove ? myRand.nextInt(moveX) : moveX;
if (randomColor) setRandomColor(randomColor);
}
if (y <= 0)
{
y = 0;
directionY *= (-1);
yScale = randomMove ? myRand.nextInt(moveY) : moveY;
if (randomColor) setRandomColor(randomColor);
}
if (y >= iBottom - height)
{
y = iBottom - height;
directionY *= (-1);
yScale = randomMove ? myRand.nextInt(moveY) : moveY;
if (randomColor) setRandomColor(randomColor);
}
}
public void draw(Graphics g)
{
g.setColor(color);
g.fillOval(x, y, width, height);
}
public void setColor(Color c)
{
color = c;
}
public void setLocation(int x, int y)
{
this.x = x;
this.y = y;
}
public void setMoveRate(int xMove, int yMove, int xDir, int yDir, boolean randMove)
{
this.moveX = xMove;
this.moveY = yMove;
directionX = xDir;
directionY = yDir;
randomMove = randMove;
}
public void setRandomColor(boolean randomColor)
{
this.randomColor = randomColor;
switch (myRand.nextInt(3))
{
case 0: color = Color.BLUE;
break;
case 1: color = Color.GREEN;
break;
case 2: color = Color.RED;
break;
default: color = Color.BLACK;
break;
}
}
public void setSize(int width, int height)
{
this.width = width;
this.height = height;
}
}
}
Since your Arrays only contain the Point you want to paint you don't have any information about the speed each point should be moved at. The best you could do is create a random amount each point should be moved each time its location is changed. This would give erratic movement as each time you move a point the distance would be random.
If you want more constant speed then you would need to create a second Array to contain the distance each point should move every time.
This starts to get messy creating a new Array every time you want a new property to be unique for the object you want to paint. That is why the approach to create a custom Object with multiple properties is easier to manage.

Score not Updating in Pong

I'm very confused as to why my scoreboard isn't updating on screen. The scores are increasing (I checked with debugger, also the ball is being centered). But the scoreboard isn't updating at all it constantly says "0 : 0"
Pong Class
package com.dheraxysgames.pong;
import java.awt.Dimension;
import javax.swing.JFrame;
public class Pong extends JFrame{
private static final long serialVersionUID = 1L;
//Set Resolution
public static final int width = 300,
height = width / 4 * 3,
scale = 3;
public Pong() {
Dimension size = new Dimension(width * scale, height * scale);
setSize(size);
setTitle("PONG");
setResizable(false);
setVisible(true);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(new GamePanel());
}
public static int getWindowWidth(){
return width * scale;
}
public static int getWindowHeight(){
return height * scale;
}
public static void main(String[] args) {
new Pong();
}
}
GamePanel Class
package com.dheraxysgames.pong;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JPanel;
import javax.swing.Timer;
public class GamePanel extends JPanel implements ActionListener, KeyListener {
private static final long serialVersionUID = 1L;
Ball ball = new Ball();
Player player = new Player();
AI ai = new AI(this);
public GamePanel(){
Timer time = new Timer(50, this);
time.start();
this.addKeyListener(this);
this.setFocusable(true);
}
private void update(){
player.update();
ai.update();
ball.update();
ball.checkCollision(player);
ball.checkCollision(ai);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillRect(0, 0, Pong.getWindowWidth(), Pong.getWindowHeight());
g.setColor(Color.WHITE);
Font font = new Font("IMMORTAL", Font.BOLD, 50);
g.setFont(font);
g.drawString(player.score + " : " + ai.score, Pong.getWindowWidth() / 2 - 60, 50);
player.paint(g);
ai.paint(g);
ball.paint(g);
}
public Ball getBall(){
return ball;
}
public void actionPerformed(ActionEvent e) {
update();
repaint();
}
//Keyboard
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_UP) player.setYVelocity(-10);
if (e.getKeyCode() == KeyEvent.VK_DOWN) player.setYVelocity(10);
}
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_UP) player.setYVelocity(0);
if (e.getKeyCode() == KeyEvent.VK_DOWN) player.setYVelocity(0);
}
public void keyTyped(KeyEvent e) {
}
}
Player Class
package com.dheraxysgames.pong;
import java.awt.Color;
import java.awt.Graphics;
public class Player {
public int score = 0;
private static int width = 50,
height = 150;
private int x = 800, y = (Pong.getWindowHeight() / 2) - (height / 2),
yV = 0;
public Player() {
}
public void update(){
y += yV;
}
public void paint(Graphics g){
g.setColor(Color.GREEN);
g.fillRect(x, y, width, height);
}
public int getX(){
return x;
}
public int getY(){
return y;
}
public int getWidth(){
return width;
}
public int getHeight(){
return height;
}
public void setYVelocity(int speed){
yV = speed;
}
}
AI Class
package com.dheraxysgames.pong;
import java.awt.Color;
import java.awt.Graphics;
public class AI {
public int score = 0;
private static int width = 50,
height = 150;
private GamePanel field;
private int x = 50, y = (Pong.getWindowHeight() / 2) - (height / 2),
yV = 0;
public AI(GamePanel game) {
this.field = game;
}
public AI(){
}
public void update(){
if(field.getBall().getY() < this.y) yV = -8;
if(field.getBall().getY() > this.y) yV = 8;
y += yV;
}
public void paint(Graphics g){
g.setColor(Color.GREEN);
g.fillRect(x, y, width, height);
}
public int getX(){
return x;
}
public int getY(){
return y;
}
public int getWidth(){
return width;
}
public int getHeight(){
return height;
}
public void setYVelocity(int speed){
yV = speed;
}
}
Ball Class
package com.dheraxysgames.pong;
import java.awt.Color;
import java.awt.Graphics;
public class Ball {
private int x = Pong.getWindowWidth() / 2, y = Pong.getWindowHeight() / 2,
xV = 10, yV = 10;
private static int size = 40;
Player player = new Player();
AI ai = new AI();
public void update() {
x += xV;
y += yV;
if (x < 0){
reverseXDirection();
player.score++;
x = Pong.getWindowWidth() / 2;
y = Pong.getWindowHeight() / 2;
}
if (x > Pong.getWindowWidth() - size){
reverseXDirection();
ai.score++;
x = Pong.getWindowWidth() / 2;
y = Pong.getWindowHeight() / 2;
}
if (y < 0 || y > Pong.getWindowHeight() - size - 38) reverseYDirection();
}
public void paint(Graphics g){
g.setColor(Color.GREEN);
g.fillOval(x, y, size, size);
}
private void reverseXDirection(){
xV = -xV;
}
private void reverseYDirection(){
yV = -yV;
}
public void checkCollision(Player p) {
if (this.x + size > p.getX() && this.x + size < p.getX() + p.getWidth()){
if (this.y + size > p.getY() && this.y + size < p.getY() + p.getHeight()){
reverseXDirection();
}
}
}
public void checkCollision(AI ai) {
if (this.x > ai.getX() && this.x < ai.getX() + ai.getWidth()){
if (this.y > ai.getY() && this.y < ai.getY() + ai.getHeight()){
reverseXDirection();
}
}
}
public int getX(){
return x;
}
public int getY(){
return y;
}
}
In you're Ball class the score you're updating is not the same score that is being checked in the GamePanel class.
Notice how in both classes you are calling
Player player = new Player();
AI ai = new AI();
player and ai in Ball are seperate instances from the player and ai in GamePanel.
You will need to get the player and ai from the GamePanel class and pass them to the Ball class. The easiest way to do this would probably be to give Ball a constructor like so
Player player;
AI ai;
public Ball(Player player, AI ai) {
this.player = player;
this.ai = ai;
}
And in your GamePanel class change:
Ball ball = new Ball();
Player player = new Player();
AI ai = new AI(this);
To this:
Player player = new Player();
AI ai = new AI(this);
Ball ball = new Ball(player, ai);
It's been a while since I've used java so I'm probably forgetting a simpler way to do this, but this should solve the problem.

Move object from point A to the nearest object

Basically, I'm developing a Java game where I have to make the troops move to the nearest hall inside the panel (like in Clash of Clans).
I have a BasePanel class that initializes 5 objects (Halls) that are randomly placed anywhere in the panel. The BasePanel has a mouselistener that when clicked, drops another object (Troop) to the x and y coordinates. The Troop object has an attribute that moves in a linear pattern. My problem is that the troop object just moves in a downward direction.
The question is how do I get the Troop object move to the closest Hall object in the BasePanel?
Here is my code:
import javax.swing.*;
import java.util.*;
import java.util.List;
import java.awt.*;
import java.awt.event.*;
import javax.imageio.*;
import java.io.*;
import java.awt.image.*;
public class Test extends JFrame{
public static BasePanel panel1;
public static JButton baseClick, buttonClick, barbarian, archer, goblin;
public static int clicks=0, count1=10, count2=5, count3=7, hallCount=5, basePanelW=600, basePanelH=420;
public List<Troop> troops = new ArrayList<Troop>(20);
public List<Hall> halls = new ArrayList<Hall>(5);
public Thread gameThread;
Random rand = new Random();
float mouseX, mouseY;
float x = mouseX; // troop's center (x, y)
float y = mouseY;
double troopSpeedX = 1;
double troopSpeedY = 1;
float troopRadius = 5;
public static void main(String[] args){
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
Test Test = new Test();
}
});
}
public void update() {
for (Troop troop : troops) {
troop.move(panel1);
}
}
class Hall{
int xVal = rand.nextInt(basePanelW);
int yVal = rand.nextInt(basePanelH);
public Hall(){}
public void paint(Graphics g){
g.setColor(Color.BLACK);
g.fillOval(xVal, yVal, 50, 50);
}
public int getX(){
System.out.println("xVal: "+xVal);
return xVal;
}
public int getY(){
System.out.println("yVal: "+yVal);
return yVal;
}
}
class Troop{
float x = mouseX;
float y = mouseY;
double troopSpeedX = 1;
double troopSpeedY = 1;
float troopRadius = 5;
Hall h = new Hall();
public Troop(){}
public void paint(Graphics g){
g.setColor(Color.BLUE);
g.fillOval((int) (x - troopRadius), (int) (y - troopRadius), (int)(2 * troopRadius), (int)(2 * troopRadius));
}
public void move(JPanel panel){
x += troopSpeedX;
y += troopSpeedY;
if (x - troopRadius < 0) {
troopSpeedX = -troopSpeedX;
x = troopRadius;
}else if (x + troopRadius > basePanelW) {
troopSpeedX = -troopSpeedX;
x = basePanelW - troopRadius;
}else if (y - troopRadius < 0) {
troopSpeedY = -troopSpeedY;
y = troopRadius;
}else if (y + troopRadius > basePanelH) {
troopSpeedY = -troopSpeedY;
y = basePanelH - troopRadius;
}repaint();
}
}
public void start(){
gameThread = new Thread() {
public void run() {
while (true) {
update();
repaint();
try {Thread.sleep(100/3);}catch(InterruptedException ex){}
}
}
}; gameThread.start();
}
class BasePanel extends JPanel{
private BufferedImage image;
BasePanel(){
setPreferredSize(new Dimension(basePanelW,basePanelH));
setBackground(Color.green);
while(hallCount>0){
halls.add(new Hall());
hallCount--;
}
addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent e){
mouseX = e.getX();
mouseY = e.getY();
clicks++;
}
public void mouseReleased(MouseEvent e){
troops.add(new Troop());
}
});
}
private void msgbox(String s){
JOptionPane.showMessageDialog(null, s);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
for (Troop troop : troops){
troop.paint(g);
}
}
public void paint(Graphics g){
super.paint(g);
for (Hall hall : halls){
hall.paint(g);
}
}
}
public Test(){
panel1 = new BasePanel();
add(panel1, BorderLayout.CENTER);
setVisible(true);
setSize(700,420);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
start();
}
}

Loop in a JApplet animation Java

import java.awt.Graphics;
import javax.swing.JApplet;
import javax.swing.JPanel;
public class Circle extends JPanel {
int x = 75;
int y = 100;
int diameter = 50;
public void setAnimationY(int y) {
this.y = y;
}
public int getAnimationY() {
return y;
}
public int getDiameter() {
return diameter;
}
public void setDiameter(int startDiameter) {
diameter = startDiameter;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawOval(x, y, diameter, diameter);
}
}
import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JApplet;
import javax.swing.JPanel;
import javax.swing.Timer;
public class BouncingBall extends JApplet {
private int speed = 5;
private Timer timer;
private Circle draw;
#Override
public void init() {
super.init();
setLayout(new BorderLayout());
draw = new Circle();
add(draw);
timer = new Timer(30, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
int y = draw.getAnimationY();
int diameter = draw.getDiameter();
int roof = getHeight();
y += speed;
if (y < 0) {
y = 0;
speed *= -1;
} else if (y + diameter > roof) {
y = roof - diameter;
speed *= -1;
}
draw.setAnimationY(y);
repaint();
}
});
}
#Override
public void start() {
super.start();
timer.start();
}
#Override
public void stop() {
timer.stop();
super.stop();
}
}
I am trying to create a JApplet that contains a ball that is bouncing up and down. So far I have been able to get the ball to go up and down but now I am trying to make the ball more "life-like" so I want the height of the ball to decrease each time the ball bounces until eventually it stops.
I have attempted to do a while loop using the roof variable that I created for the getHeight() method but for some reason when I tried to use it either the ball didn't move at all or the loop had no affect on the ball.
I have also tried a for loop but I ran into the same problem that I got into with the while loop. I believe the problem is that I am not placing this for loop in the correct spot for it to work correctly.
thanks in advance.
Little modifications to your code that can give you some trails:
#Override
public void actionPerformed(ActionEvent e) {
int y = draw.getAnimationY();
int diameter = draw.getDiameter();
int roof = getHeight();
y += speed;
//
// Reduce the ball size at the bottom of the screen
//
if(y + diameter > roof) {
if(diameter > minDiameter) {
diameter -= (roof - y);
} else {
diameter = minDiameter;
}
} else if (diameter < maxDiameter) {
diameter++;
}
draw.setDiameter(diameter);
if (y < 0) {
y = 0;
speed *= -1;
} else if (y + diameter > roof) {
y = roof - diameter;
speed *= -1;
}
// Simulates a little gravity
speed += 0.5;
draw.setAnimationY(y);
repaint();
}
For more realism, the best way would to find an equation that is function of the ball position and a coefficient of hardness for the ball and would give you the ball size.
Well let use continue with #MadProgrammer's solution from your other related question:
In your class of DrawPane we can easily define the height, getAnimationHeight() and setAnimationHeight(int) to control the height decrease as soon as it touches the ground. Please remember that in java left-top co-ordinate is (0, 0) and right-bottom co-ordinate is (getWidth(), getHeight()). Suppose that it starts from height = 0(top). Then it will start from y = height(top) and eventually move to the getHeight()(bottom) of your container. We will increase the height(top y) using setAnimationHeight() by adding an amount(say 30) to current height(which getAnimationHeight() will return) .
So, this little tweak made to #MadeProgrammer's solution in your other question will be the following demo.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.Timer;
public class Circle extends JApplet {
private int delta = 8;
private Timer timer;
private DrawPane drawPane;
#Override
public void init() {
super.init();
setLayout(new BorderLayout());
drawPane = new DrawPane();
add(drawPane);
timer = new Timer(100, new ActionListener() {
int frameCount = 0;
#Override
public void actionPerformed(ActionEvent e) {
int y = drawPane.getAnimationY();
int diameter = drawPane.getDiameter();
y += delta;
if (y < drawPane.getAnimationHeight()) {
y = drawPane.getAnimationHeight();
delta *= -1;
} else if (y + diameter > getHeight()) {
y = getHeight()- diameter;
delta *= -1;
int animationHeight = drawPane.getAnimationHeight();
animationHeight = animationHeight + (getHeight() - diameter - animationHeight)/2;
drawPane.setAnimationHeight(animationHeight);
if(animationHeight + diameter + 2 >= getHeight())
{
System.out.println("true");
drawPane.setAnimationY(getHeight() - diameter);
repaint();
timer.stop();
return;
}
}
drawPane.setAnimationY(y);
repaint();
}
});
}
#Override
public void start() {
super.start();
timer.start();
}
#Override
public void stop() {
timer.stop();
super.stop();
}
public class DrawPane extends JPanel {
int x = 100;
int y = 0;
int diameter = 50;
int height = 0;
public void setAnimationX(int x) {
this.x = x;
}
public void setAnimationY(int y) {
this.y = y;
}
public void setAnimationHeight(int h)
{
height = h;
}
public int getAnimationHeight()
{
return height;
}
public int getAnimationX() {
return x;
}
public int getAnimationY() {
return y;
}
public int getDiameter() {
return diameter;
}
public void setDiameter(int startDiameter) {
diameter = startDiameter;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawOval(x, y, diameter, diameter);
}
}
}
NOTE: As soon as it touches the bottom finally, you should stop the Timer to get rid of the flickering of the ball. This task is left as an exercise for you.

Categories