Why is my Game Of Life simulation so inaccurate? - java

I've had that project in my head for a few weeks so I made a game of life simulation program
but after comparing it to this simulation (https://playgameoflife.com/), none of what i've codded is accurate even tho I respected the rules in my code ...
is my code not correct ? what's wrong ?
(https://pastebin.com/SpMFQGJL)
public class GameOfLife {
/*
* STATIC PART - ENTRY POINT
*/
public static void main(String[] args) {
GameOfLife gol = new GameOfLife(new Dimension(480,270),4, true, new Random().nextInt(), new Random().nextInt(25)).init();
gol.start();
}
/*
* GameOfLife Class content
*/
/*
* GameGrid Vars (requiered for game grid)
* dimX = game of life grid size on X axis
* dimY = game of life grid size on Y axis
* cellScale = size of cells on X and Y axis on screen display
* fillperc = chances of a cell to be alive on grid generation
* borderTeleporter = is wall teleporter (on other side of map) !!useless at the moment!!
*/
public int dimX;
public int dimY;
public int cellScale;
public int fillperc;
public boolean borderTeleporter;
/*
* Generation vars
*/
public int currentSeed = 0;
public int generation = 0;
//Thread clock = the thread the game of life clock is on
public Thread clock;
/*
* frame = the frame the game of life grid is drawn on
* game_grid = component of game of life
*/
public JFrame frame;
public GameGrid game_grid;
/*
* GameOfLife
* #params args
*/
public GameOfLife(Dimension boardDimension, int cellScale, boolean borderTeleporter,int seed,int fillperc) {
if(seed != 0) currentSeed = seed;
this.dimX = (int) boardDimension.getWidth();
this.dimY = (int) boardDimension.getHeight();
this.cellScale = cellScale;
this.borderTeleporter = borderTeleporter;
this.fillperc = fillperc;
}
public GameOfLife init() {
if(currentSeed == 0) currentSeed = new Random().nextInt();
this.game_grid = new GameGrid(this).genBoard();
frame = new JFrame("Map: " + currentSeed + " Generation: " + generation);
frame.setUndecorated(true);
frame.addWindowListener(new WindowAdapter() {#Override public void windowClosing(WindowEvent e) {running = false;try{clock.join();}catch(Exception exx) {System.out.println("impossible de tuer le thread clock");};System.exit(0);}});
frame.addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {}
#Override
public void keyReleased(KeyEvent e) {if(e.getKeyCode() == 32) {if(running)stop();else start();}else if(e.getKeyCode() == 116) {stop();fillperc = new Random().nextInt(25);currentSeed = new Random().nextInt();game_grid.genBoard();start();}}
#Override
public void keyPressed(KeyEvent e) {}
});
frame.setPreferredSize(new Dimension(dimX*cellScale,dimY*cellScale));
frame.pack();
frame.add(game_grid);
frame.pack();
frame.setVisible(true);
return this;
}
public boolean running = false;
public void start() {
running = true;
clock = new Thread(new Runnable() {
#Override
public void run() {
while(running) {
frame.setTitle("Map: " + currentSeed + " Generation: " + generation);
game_grid.calcBoard();
frame.repaint();
generation++;
try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}
}
}
});
clock.start();
}
public void stop() {
running = false;try{clock.join();}catch(Exception exx) {System.out.println("impossible de tuer le thread clock");}
}
}
and then the game grid class:
public class GameGrid extends JPanel{
/**
*
*/
private GameOfLife gol;
private BufferedImage img;
boolean first = true;
public GameGrid(GameOfLife gol) {
this.gol = gol;
}
public boolean[][] board;
public boolean[][] new_board;
public GameGrid genBoard() {
board = new boolean[gol.dimX][gol.dimY];
Random seed = new Random(gol.currentSeed);
for(int x = 0; x < gol.dimX; x++) {
for(int y = 0; y < gol.dimY; y++) {
board[x][y] = seed.nextInt(100) < gol.fillperc;
}
}
return this;
}
public void calcBoard() {
Thread t = new Thread(new Runnable() {
#Override
public void run() {
new_board = board;
for(int x = 0; x < gol.dimX; x++) {
for(int y = 0; y < gol.dimY; y++) {
int n = getAliveNeighbours(x,y);
if(board[x][y]) {
if(n > 3 || n < 2) new_board[x][y] = false;
else new_board[x][y] = true;
} else {
if(n == 3) new_board[x][y] = true;
else new_board[x][y] = false;
}
}
}
}
});
t.start();
try {t.join();} catch (InterruptedException e) {e.printStackTrace();}
}
public int getAliveNeighbours(int x, int y) {
int n = 0;
if(isIn(x+1,y+1)) {if(board[x+1][y+1]) {n+=1;}}
if(isIn(x,y+1)) {if(board[x][y+1]) {n+=1;}}
if(isIn(x-1,y+1)) {if(board[x-1][y+1]) {n+=1;}}
if(isIn(x+1,y)) {if(board[x+1][y]) {n+=1;}}
if(isIn(x-1,y)) {if(board[x-1][y]) {n+=1;}}
if(isIn(x+1,y-1)) {if(board[x+1][y-1]) {n+=1;}}
if(isIn(x,y-1)) {if(board[x][y-1]) {n+=1;}}
if(isIn(x-1,y-1)) {if(board[x-1][y-1]) {n+=1;}}
return n;
}
public boolean isIn(int x, int y) {
if(x < 0|| x > gol.dimX-1) return false;
if(y < 0|| y > gol.dimY-1) return false;
return true;
}
public void genImage() {
Thread t = new Thread(new Runnable() {
#Override
public void run() {
BufferedImage imag = new BufferedImage(gol.dimX*gol.cellScale,gol.dimX*gol.cellScale,BufferedImage.TYPE_INT_RGB);
for(int x = 0; x < gol.dimX; x++) {
for(int y = 0; y < gol.dimY; y++) {
int c = 0x000000;
if(board[x][y]) c = 0xCCCCCC;
for(int i = 0; i < gol.cellScale; i++) {
for(int j = 0; j < gol.cellScale; j++) {
imag.setRGB(x*gol.cellScale+i, y*gol.cellScale+j, c);
}
}
}
}
img = imag;
}
});
t.start();
try {t.join();} catch (InterruptedException e) {System.out.println("wesh");}
}
public void paint(Graphics g) {
super.paint(g);
genImage();
g.drawImage(img, 0, 0, img.getWidth(), img.getHeight(), null);
}
}
thing that triggers me the most is that everything works fine and the after looking at my code I can't find what can cause the simulation to be inaccurate

new_board = board doesn't do what you want it to. It just sets one the value of the new_board variable to the current value of the board variable... both are references. That means you're modifying the same board that you're examining... whereas you want to construct a new board based solely on the contents of the old board.
Remove the new_board field (which you don't need), then change this:
new_board = board;
to
boolean[][] new_board = new boolean[gol.dimX][gol.dimY];
and then at the end of the method, add:
board = new_board;
... so that the old board is replaced with the new board. It's possible that there are other problems with your logic, but that's the most obvious one.
I'd also strongly advise you to get rid of all your threading code in calcBoard and genImage. All you're doing is creating a new thread to do something, then waiting for that thread to finish... that doesn't do any good (it still blocks until the work is complete) but it makes the code more complex.
(I'd also suggest reformatting the stop method so that it's not all on one line, which makes it really hard to read. In general, I'd suggest reformatting the whole code, in fact. Just tell your IDE to reformat the whole thing.)

Related

Swing draws 3 JLabels instead of one

I don't really understand why this program draws three pawns instead of one and two of which seem to have a random(probably not so random) positions. enter image description here
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class GamePanel extends JPanel implements MouseListener {
static final int SCREEN_EDGE = 800;
static final int GAME_UNITS = 64;
static final int UNIT_SIZE = 100;
final int[] x = new int[GAME_UNITS];
final int[] y = new int[GAME_UNITS];
boolean running = false;
public GamePanel() {
this.setPreferredSize(new Dimension(SCREEN_EDGE, SCREEN_EDGE));
this.setFocusable(true);
this.addKeyListener(new MyKeyAdapter());
startGame();
int[] position = {0,0};
int[] position1 = {1, 0};
new Pawn(position,-1);
}
private void startGame() {
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
draw(g);
}
public void draw(Graphics g){
int counter = 1;
for (int y = 0; y < SCREEN_EDGE/UNIT_SIZE; y++) {
// 1 == "white" 2 == "black"
int color = (y % 2 == 0) ? 1 : 2;
for (int x = 0; x < SCREEN_EDGE/UNIT_SIZE; x++) {
g.setColor(color == 1 ? new Color(239,217, 181) : new Color(180, 136,98));
g.fillRect(x*UNIT_SIZE, y*UNIT_SIZE, UNIT_SIZE, UNIT_SIZE);
color = color == 1 ? 2 : 1;
}
}
for (int i = 0; i < Figure.figures.length; i++) {
JLabel figureSprite = new JLabel(Figure.figures[i].image, JLabel.CENTER);
figureSprite.setSize(90,90);
figureSprite.setLocation(Figure.figures[i].position[0] + 5,Figure.figures[i].position[1] + 5);
this.add(figureSprite);
}
}
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
public class MyKeyAdapter extends KeyAdapter {
#Override
public void keyPressed(KeyEvent e) {
}
}
}
import javax.swing.*;
public abstract class Figure{
protected int value;
protected ImageIcon image;
protected int[][] possibleMoves;
public int[] position;
// white = -1 black = 1
protected int whiteOrBlack;
protected static int figureCount = 1;
// int[figureCount][0 = x][1 = y][2 = color]
public static Figure[] figures = new Figure[figureCount];
public Figure(int value, int[] position, int[][] possibleMoves , int whiteOrBlack) {
this.value = value;
this.position = position;
this.possibleMoves = possibleMoves;
this.whiteOrBlack = whiteOrBlack;
Figure[] oldFigures = figures;
figures = new Figure[figureCount];
for (int i = 0; i < oldFigures.length; i++) {
figures[i] = oldFigures[i];
}
figures[figureCount - 1] = this;
figureCount++;
}
public abstract void move(int[] coordinates);
}
import javax.swing.*;
import java.awt.*;
import java.io.*;
public class Pawn extends Figure{
public Pawn(int[] position, int whiteOrBlack) {
super(1, position, new int[3][2], whiteOrBlack);
super.image = new ImageIcon(getClass().getClassLoader().getResource("graphics/" + (whiteOrBlack == -1 ? "whitePawn.png" : "blackPawn.png")));
Image newImage = super.image.getImage().getScaledInstance(90,90, Image.SCALE_AREA_AVERAGING);
super.image = new ImageIcon(newImage);
}
public void checkMoves(){
for (int i = 0; i < figures.length; i++) {
if((position[0] - 1) == figures[i].position[0] && (position[1] + this.whiteOrBlack) == figures[i].position[1] && figures[i].whiteOrBlack != this.whiteOrBlack) {
possibleMoves[0][0] = position[0] - 1;
possibleMoves[0][1] = position[1] + this.whiteOrBlack;
}else possibleMoves[0] = new int[2];
if((position[0]) != figures[i].position[0] && (position[1]) != figures[i].position[1]){
possibleMoves[1][0] = position[0];
possibleMoves[1][1] = position[1] + 1;
}else possibleMoves[1] = new int[2];
if((position[0] + 1) == figures[i].position[0] && (position[1] + this.whiteOrBlack) == figures[i].position[1] && figures[i].whiteOrBlack != this.whiteOrBlack) {
possibleMoves[2][0] = position[0] + 1;
possibleMoves[2][1] = position[1] + this.whiteOrBlack;
}else possibleMoves[2] = new int[2];
}
}
#Override
public void move(int[] coordinates) {
}
}
import javax.swing.*;
public class GameFrame extends JFrame {
public GameFrame(){
this.add(new GamePanel());
this.setTitle("Chess");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.pack();
this.setResizable(false);
this.setVisible(true);
this.setLocationRelativeTo(null);
}
}
public class ChessGame {
public static void main(String[] args) {
new GameFrame();
}
}
I tried few thing like changing JLabel to BufferedImage but it would've generate other problems down the line like not being able to use MouseListener so i feel stuck. I would love to know why this code generates 3 textures too.
It looks like you are adding FigureSprites from within the drawComponent() method. The more often you draw the window, the more figures you have to draw.
Instead within drawComponent() just draw the current state but do not modify it. Adding figures has to come from somewhere else. For example, you could create the necessary pawns in the constructor. Or in extra methods that might get triggered based on user input.
In case you modify the GamePanel's state enough so that it should get painted freshly, just invoke repaint(). Swing will mark this component for repainting and decide on it's own when to run the paint() method, which in turn will run paintComponent().

Issue in the update step of Conway's Game of Life

I'm currently trying to implement Conway's Game of Life in Java. On a first glance the application seems to work as it either stalls in the states that are expected as stalling states (e.g. block, bee-hive and tub) or changes infinitely.
However, as I took a closer look I never actually saw a Blinker or Beacon nor a Loaf (for reference images of these states see here). So I decided to implement a drawing mode and a one-step-update method that gives me the possibility to analyze exactly what's going on.
The strange thing that I found was the following:
When entering Drawing Mode I reset all cells and all buffered cells on the board to dead. Then, I drew a vertical Blinker somewhere in the middle of the board and did one update step and checked for the update mechanism of all cells that are alive (which are only the 3 cells belonging to the Blinker). In the output however, all of them returned that the value to their bottom left was alive (which actually could not be the case because nothing is being logged for these cells).
I'm really confused about where I went wrong in my code because this just doesn't make any sense for me. The values for the next generation are being stored in a buffer and only written back after the next generation has been calculated entirely, so I don't think that this should be the issue.
For reproducibility, here is the code:
Main.java
import java.awt.EventQueue;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
public class Main {
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
createAndShowUI();
});
}
private static void createAndShowUI() {
AnimationPanel panel = new AnimationPanel();
JFrame frame = new JFrame("Game of Life");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel);
frame.setResizable(false);
frame.pack();
frame.setLocationRelativeTo(null);
frame.addKeyListener(new KeyAdapter() {
#Override
public void keyReleased(KeyEvent e) {
super.keyReleased(e);
if(e.getKeyCode() == e.VK_R) {
panel.reset();
panel.startAnimation();
}
if(e.getKeyCode() == e.VK_C) {
panel.clear();
}
if(e.getKeyCode() == e.VK_S) {
panel.startAnimation();
}
if(e.getKeyCode() == e.VK_SPACE) {
panel.stepAnimation();
}
}
});
frame.setVisible(true);
panel.populate();
panel.startAnimation();
}
}
AnimationPanel.java:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Random;
import javax.swing.JPanel;
import javax.swing.Timer;
public class AnimationPanel extends JPanel implements ActionListener {
private static final long serialVersionUID = 4929258196354814855L;
private int width = 1920;
private int height = 1080;
private int gridSize = 10;
private Cell[][] cells;
private Cell[][] buffer;
private Timer timer;
private boolean drawingMode = false;
public AnimationPanel() {
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
if(drawingMode) {
int x = e.getX();
int y = e.getY();
int xIndex = (int)x / gridSize;
int yIndex = (int)y / gridSize;
cells[xIndex][yIndex].live = !cells[xIndex][yIndex].live;
repaint();
}
}
});
}
public void reset() {
timer.stop();
cells = null;
buffer = null;
populate();
}
public void clear() {
timer.stop();
for(Cell[] row : cells) {
for(Cell cell: row) {
cell.live = false;
}
}
for(Cell[] row : buffer) {
for(Cell cell: row) {
cell.live = false;
}
}
repaint();
drawingMode = true;
}
public void populate() {
Random random = new Random();
int horizontalCellCount = width / gridSize;
int verticalCellCount = height / gridSize;
cells = new Cell[horizontalCellCount][verticalCellCount];
buffer = new Cell[horizontalCellCount][verticalCellCount];
for(int i = 0; i < horizontalCellCount; i++) {
for(int j = 0; j < verticalCellCount; j++) {
double randVal = random.nextDouble();
boolean live = (randVal < 0.05) ? true : false;
int x = i * gridSize;
int y = j * gridSize;
cells[i][j] = new Cell(live, x, y, gridSize);
buffer[i][j] = new Cell(live, x, y, gridSize);
}
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for(Cell[] row : cells) {
for(Cell cell : row) {
cell.draw(g);
}
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(width, height);
}
public void startAnimation() {
drawingMode = false;
timer = new Timer(250, this);
timer.start();
}
public void stepAnimation() {
update();
repaint();
}
#Override
public void actionPerformed(ActionEvent e) {
update();
repaint();
}
private void update() {
int horizontalCellCount = width / gridSize;
int verticalCellCount = height / gridSize;
boolean log = false;
for(int i = 0; i < horizontalCellCount; i++) {
for(int j = 0; j < verticalCellCount; j++) {
if(cells[i][j].live) {
log = true;
System.out.println("Cell: (" + i + "|" + j + ") " + cells[i][j].toString());
System.out.println("Row length: " + cells[i].length);
System.out.println("Column length: " + cells.length);
} else {
log = false;
}
int liveNeighours = 0;
for(int x = i - 1; x <= i + 1; x++) {
if(x < 0 || x == horizontalCellCount) {
continue;
}
for(int y = j - 1; y <= j + 1; y++) {
if(y < 0 || y == verticalCellCount || y == j && x == i) {
continue;
}
if(cells[x][y].live) {
liveNeighours++;
}
if(log) {
System.out.println("Cell at " + x + "|" + y + ": " + cells[x][y].live + " - liveNeighbours: " + liveNeighours);
}
}
}
Cell cell = cells[i][j];
if(liveNeighours > 3 || liveNeighours < 2) {
buffer[i][j].live = false;
} else if(!cell.live && liveNeighours == 3) {
buffer[i][j].live = true;
}
}
}
for(int i = 0; i < horizontalCellCount; i++) {
for(int j = 0; j < verticalCellCount; j++) {
cells[i][j] = buffer[i][j];
}
}
}
}
Cell.java
import java.awt.Color;
import java.awt.Graphics;
public class Cell {
public boolean live;
public int x;
public int y;
public int size;
public Cell(boolean live, int x, int y, int size) {
this.live = live;
this.x = x;
this.y = y;
this.size = size;
}
public void draw(Graphics g) {
if(live) {
g.setColor(Color.BLACK);
} else {
g.setColor(Color.WHITE);
}
g.fillRect(x, y, size, size);
}
#Override
public String toString() {
return "(" + x + " " + y + ") - Size: " + size;
}
}
Any help is really appreciated on this as I'm really confused what is wrong with the code.

Java, program does only repaint() in the end

I have to do an assignment at school, it's a car park simulator, calling run(); from the main gives a perfectly normal simulation where the carspots get repainted every step but when calling it from an actionListener it only paints the result and not the steps inbetween. There are 6 classes down below where the main is in the Simulator class, and SimulatorView has the repaint(); in it.
So could anyone explain to me why it simulates every step when run() is called from the main, and why it just paints the output when run() is called from an ActionListener behind a JButton?
Simulator.java:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
public class Simulator implements ActionListener{
private CarQueue entranceCarQueue;
private CarQueue paymentCarQueue;
private CarQueue exitCarQueue;
private SimulatorView simulatorView;
private int day = 0;
private int hour = 0;
private int minute = 0;
private int tickPause = 100;
int weekDayArrivals= 50; // average number of arriving cars per hour
int weekendArrivals = 90; // average number of arriving cars per hour
int enterSpeed = 3; // number of cars that can enter per minute
int paymentSpeed = 10; // number of cars that can pay per minute
int exitSpeed = 9; // number of cars that can leave per minute
public Simulator() {
entranceCarQueue = new CarQueue();
paymentCarQueue = new CarQueue();
exitCarQueue = new CarQueue();
simulatorView = new SimulatorView(3, 6, 30, this);
}
public void run() {
for (int i = 0; i < 10000; i++) {
tick();
}
}
public void run(int steps) {
System.out.println("run " + steps + " steps");
for (int i = 0; i < steps; i++) {
tick();
}
}
private void tick() {
System.out.println("simulator-tick");
// Advance the time by one minute.
minute++;
while (minute > 59) {
minute -= 60;
hour++;
}
while (hour > 23) {
hour -= 24;
day++;
}
while (day > 6) {
day -= 7;
}
Random random = new Random();
// Get the average number of cars that arrive per hour.
int averageNumberOfCarsPerHour = day < 5
? weekDayArrivals
: weekendArrivals;
// Calculate the number of cars that arrive this minute.
double standardDeviation = averageNumberOfCarsPerHour * 0.1;
double numberOfCarsPerHour = averageNumberOfCarsPerHour + random.nextGaussian() * standardDeviation;
int numberOfCarsPerMinute = (int)Math.round(numberOfCarsPerHour / 60);
// Add the cars to the back of the queue.
for (int i = 0; i < numberOfCarsPerMinute; i++) {
Car car = new AdHocCar();
entranceCarQueue.addCar(car);
}
// Remove car from the front of the queue and assign to a parking space.
for (int i = 0; i < enterSpeed; i++) {
Car car = entranceCarQueue.removeCar();
if (car == null) {
break;
}
// Find a space for this car.
Location freeLocation = simulatorView.getFirstFreeLocation();
if (freeLocation != null) {
simulatorView.setCarAt(freeLocation, car);
int stayMinutes = (int) (15 + random.nextFloat() * 10 * 60);
car.setMinutesLeft(stayMinutes);
}
}
// Perform car park tick.
simulatorView.tick();
// Add leaving cars to the exit queue.
while (true) {
Car car = simulatorView.getFirstLeavingCar();
if (car == null) {
break;
}
car.setIsPaying(true);
paymentCarQueue.addCar(car);
}
// Let cars pay.
for (int i = 0; i < paymentSpeed; i++) {
Car car = paymentCarQueue.removeCar();
if (car == null) {
break;
}
// TODO Handle payment.
simulatorView.removeCarAt(car.getLocation());
exitCarQueue.addCar(car);
}
// Let cars leave.
for (int i = 0; i < exitSpeed; i++) {
Car car = exitCarQueue.removeCar();
if (car == null) {
break;
}
// Bye!
}
// Update the car park view.
simulatorView.updateView();
// Pause.
try {
Thread.sleep(tickPause);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
#Override
public void actionPerformed(ActionEvent e)
{
String command = e.getActionCommand();
switch (command) {
case "oneStep":
run(1);
break;
case "hundredSteps":
run(10);
break;
default:
break;
}
}
public static void main (String[] args) {
Simulator start = new Simulator();
}
}
SimulatorView.java:
import javax.swing.*;
import java.awt.*;
public class SimulatorView extends JFrame {
private CarParkView carParkView;
private int numberOfFloors;
private int numberOfRows;
private int numberOfPlaces;
private Car[][][] cars;
public SimulatorView(int numberOfFloors, int numberOfRows, int numberOfPlaces, Simulator parent) {
this.numberOfFloors = numberOfFloors;
this.numberOfRows = numberOfRows;
this.numberOfPlaces = numberOfPlaces;
cars = new Car[numberOfFloors][numberOfRows][numberOfPlaces];
carParkView = new CarParkView();
Container contentPane = getContentPane();
//contentPane.add(stepLabel, BorderLayout.NORTH);
contentPane.add(carParkView, BorderLayout.CENTER);
//contentPane.add(population, BorderLayout.SOUTH);
JButton stepForward = new JButton("oneStep");
stepForward.addActionListener(parent);
JButton stepHundredForward = new JButton("hundredSteps");
stepHundredForward.addActionListener(parent);
JMenuBar stepBar = new JMenuBar();
stepBar.add(stepForward);
stepBar.add(stepHundredForward);
contentPane.add(stepBar, BorderLayout.SOUTH);
pack();
setVisible(true);
updateView();
}
public void updateView() {
carParkView.updateView();
}
public int getNumberOfFloors() {
return numberOfFloors;
}
public int getNumberOfRows() {
return numberOfRows;
}
public int getNumberOfPlaces() {
return numberOfPlaces;
}
public Car getCarAt(Location location) {
if (!locationIsValid(location)) {
return null;
}
return cars[location.getFloor()][location.getRow()][location.getPlace()];
}
public boolean setCarAt(Location location, Car car) {
if (!locationIsValid(location)) {
return false;
}
Car oldCar = getCarAt(location);
if (oldCar == null) {
cars[location.getFloor()][location.getRow()][location.getPlace()] = car;
car.setLocation(location);
return true;
}
return false;
}
public Car removeCarAt(Location location) {
if (!locationIsValid(location)) {
return null;
}
Car car = getCarAt(location);
if (car == null) {
return null;
}
cars[location.getFloor()][location.getRow()][location.getPlace()] = null;
car.setLocation(null);
return car;
}
public Location getFirstFreeLocation() {
for (int floor = 0; floor < getNumberOfFloors(); floor++) {
for (int row = 0; row < getNumberOfRows(); row++) {
for (int place = 0; place < getNumberOfPlaces(); place++) {
Location location = new Location(floor, row, place);
if (getCarAt(location) == null) {
return location;
}
}
}
}
return null;
}
public Car getFirstLeavingCar() {
for (int floor = 0; floor < getNumberOfFloors(); floor++) {
for (int row = 0; row < getNumberOfRows(); row++) {
for (int place = 0; place < getNumberOfPlaces(); place++) {
Location location = new Location(floor, row, place);
Car car = getCarAt(location);
if (car != null && car.getMinutesLeft() <= 0 && !car.getIsPaying()) {
return car;
}
}
}
}
return null;
}
public void tick() {
System.out.println("simulatorview-tick");
for (int floor = 0; floor < getNumberOfFloors(); floor++) {
for (int row = 0; row < getNumberOfRows(); row++) {
for (int place = 0; place < getNumberOfPlaces(); place++) {
Location location = new Location(floor, row, place);
Car car = getCarAt(location);
if (car != null) {
car.tick();
}
}
}
}
}
private boolean locationIsValid(Location location) {
int floor = location.getFloor();
int row = location.getRow();
int place = location.getPlace();
if (floor < 0 || floor >= numberOfFloors || row < 0 || row > numberOfRows || place < 0 || place > numberOfPlaces) {
return false;
}
return true;
}
private class CarParkView extends JPanel {
private Dimension size;
private Image carParkImage;
/**
* Constructor for objects of class CarPark
*/
public CarParkView() {
size = new Dimension(0, 0);
}
/**
* Overridden. Tell the GUI manager how big we would like to be.
*/
public Dimension getPreferredSize() {
return new Dimension(800, 500);
}
/**
* Overriden. The car park view component needs to be redisplayed. Copy the
* internal image to screen.
*/
public void paintComponent(Graphics g) {
if (carParkImage == null) {
return;
}
Dimension currentSize = getSize();
if (size.equals(currentSize)) {
g.drawImage(carParkImage, 0, 0, null);
}
else {
// Rescale the previous image.
g.drawImage(carParkImage, 0, 0, currentSize.width, currentSize.height, null);
}
}
public void updateView() {
// Create a new car park image if the size has changed.
if (!size.equals(getSize())) {
size = getSize();
carParkImage = createImage(size.width, size.height);
}
Graphics graphics = carParkImage.getGraphics();
for(int floor = 0; floor < getNumberOfFloors(); floor++) {
for(int row = 0; row < getNumberOfRows(); row++) {
for(int place = 0; place < getNumberOfPlaces(); place++) {
Location location = new Location(floor, row, place);
Car car = getCarAt(location);
Color color = car == null ? Color.white : Color.red;
drawPlace(graphics, location, color);
}
}
}
repaint();
}
/**
* Paint a place on this car park view in a given color.
*/
private void drawPlace(Graphics graphics, Location location, Color color) {
graphics.setColor(color);
graphics.fillRect(
location.getFloor() * 260 + (1 + (int)Math.floor(location.getRow() * 0.5)) * 75 + (location.getRow() % 2) * 20,
60 + location.getPlace() * 10,
20 - 1,
10 - 1); // TODO use dynamic size or constants
}
}
}
Car.java:
public abstract class Car {
private
Location location;
private int minutesLeft;
private boolean isPaying;
/**
* Constructor for objects of class Car
*/
public Car() {
}
public Location getLocation() {
return location;
}
public void setLocation(Location location) {
this.location = location;
}
public int getMinutesLeft() {
return minutesLeft;
}
public void setMinutesLeft(int minutesLeft) {
this.minutesLeft = minutesLeft;
}
public boolean getIsPaying() {
return isPaying;
}
public void setIsPaying(boolean isPaying) {
this.isPaying = isPaying;
}
public void tick() {
System.out.println("car-tick");
minutesLeft--;
}
}
Location.java:
public class Location {
private int floor;
private int row;
private int place;
/**
* Constructor for objects of class Location
*/
public Location(int floor, int row, int place) {
this.floor = floor;
this.row = row;
this.place = place;
}
/**
* Implement content equality.
*/
public boolean equals(Object obj) {
if(obj instanceof Location) {
Location other = (Location) obj;
return floor == other.getFloor() && row == other.getRow() && place == other.getPlace();
}
else {
return false;
}
}
/**
* Return a string of the form floor,row,place.
* #return A string representation of the location.
*/
public String toString() {
return floor + "," + row + "," + place;
}
/**
* Use the 10 bits for each of the floor, row and place
* values. Except for very big car parks, this should give
* a unique hash code for each (floor, row, place) tupel.
* #return A hashcode for the location.
*/
public int hashCode() {
return (floor << 20) + (row << 10) + place;
}
/**
* #return The floor.
*/
public int getFloor() {
return floor;
}
/**
* #return The row.
*/
public int getRow() {
return row;
}
/**
* #return The place.
*/
public int getPlace() {
return place;
}
}
CarQueue.java:
import java.util.LinkedList;
import java.util.Queue;
public class CarQueue {
private Queue<Car> queue = new LinkedList<>();
public boolean addCar(Car car) {
return queue.add(car);
}
public Car removeCar() {
return queue.poll();
}
}
AdHocCar.java:
public class AdHocCar extends Car {
public AdHocCar() {
}
}
When you call run() from the actionListener, it is being executed on the EventDispatchTread, blocking that thread until the run() method terminates. Only after that point can the EDT act on the repaint request.
"In general Swing is not thread safe. All Swing components and related classes, unless otherwise documented, must be accessed on the event dispatching thread." see here
So when an action is performed, actionListener pick up that event and execute run() in the event dispatching thread.
At step 0, when it comes to repaint(), it doesn't draw anything because "The component will be repainted after all of the currently pending events have been dispatched." see here
The repaint() is waiting for the release of ActionListener.actionPerformed.
So it comes to step 1, the same thing happens. Only at the end, after the action event is released, the repaint() will be execute once. So that's why only results are there not steps in between.
My suggestion is put the painting logic in the main thread instead of the actionListener.

Keybind action being run at start but not from actual key press

I'm coding a game for my final project in Java, our teacher provided us with a Board class that is a component that allows us to place and remove pegs on a virtual game board instead of having to code one ourselves. I'm trying to add Key Binding to the Board component but the action I want performed on key press is happening when I run the program but It won't run when I type a Key.
The board class already has a method for getting the position clicked on the component and I think this might be interfering with my Code but I'm not sure.
This is my game class where I tried to add keybinding
package rpgGame;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.KeyStroke;
public class RPGGame
{
public static final GameWorld WORLD_MAP = new GameWorld();
public static Board LOCAL_MAP = new Board(20,50);
public static List<Mobile> allMobs = new ArrayList<Mobile>();
public static final Player PLAYER = new Player();
public static int xIndex = ((GameWorld.WORLD_SIZE-1)/2) - (50/2);
public static int yIndex = ((GameWorld.WORLD_SIZE-1)/2) - (20/2);
public static boolean boardUpdate = true;
public enum Direction {RIGHT,LEFT,UP,DOWN}
private static final String MOVE_PLAYER_UP = "move up";
private static final String MOVE_PLAYER_LEFT = "move left";
private static final String MOVE_PLAYER_RIGHT = "move right";
private static final String MOVE_PLAYER_DOWN = "move down";
public static final Thread SYNC_BOARD = new Thread()
{
public synchronized void run()
{
while (boardUpdate)
{
for (int i = 0; i < 50; i++)
{
for (int j = 0; j < 20; j++)
{
if (WORLD_MAP.isOccupied(i+xIndex, j+yIndex))
{
LOCAL_MAP.putPeg(Color.RED, j, i);
System.out.println("Successfully Updated");
}
else
{
LOCAL_MAP.putPeg(Color.GRAY, j,i);
}
}
}
boardUpdate = false;
}
}
};
public RPGGame()
{
generateMobs(200);
placeMobs();
placePlayer();
SYNC_BOARD.run();
LOCAL_MAP.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), MOVE_PLAYER_UP);
LOCAL_MAP.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0), MOVE_PLAYER_UP);
LOCAL_MAP.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), MOVE_PLAYER_LEFT);
LOCAL_MAP.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0), MOVE_PLAYER_LEFT);
LOCAL_MAP.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), MOVE_PLAYER_RIGHT);
LOCAL_MAP.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0), MOVE_PLAYER_RIGHT);
LOCAL_MAP.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), MOVE_PLAYER_DOWN);
LOCAL_MAP.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0), MOVE_PLAYER_DOWN);
LOCAL_MAP.getActionMap().put(MOVE_PLAYER_UP, new MoveAction(Direction.UP));
LOCAL_MAP.getActionMap().put(MOVE_PLAYER_LEFT, new MoveAction(Direction.LEFT));
LOCAL_MAP.getActionMap().put(MOVE_PLAYER_RIGHT, new MoveAction(Direction.RIGHT));
LOCAL_MAP.getActionMap().put(MOVE_PLAYER_DOWN, new MoveAction(Direction.DOWN));
}
public static void main(String[] args)
{
new RPGGame();
}
public static void generateMobs(int numOfMobs)
{
for (int i=0; i<numOfMobs; i++)
{
allMobs.add(new Mobile());
}
}
public static void generateMobs()
{
int numOfMobs = (int)(Math.random()*500);
for (int i=0;i<numOfMobs; i++)
{
allMobs.add(new Mobile());
}
}
public static void placeMobs()
{
for (int i=0; i<allMobs.size(); i++)
{
//i is used as a placeholder value for points until I create a random number generator.
WORLD_MAP.placeCharacter(i, i,allMobs.get(i));
allMobs.get(i).setLocation(i, i);
}
}
public static void placePlayer()
{
WORLD_MAP.placeCharacter(249, 249, PLAYER);
PLAYER.setLocation(249, 249);
}
#SuppressWarnings("serial")
public class MoveAction extends AbstractAction
{
Direction direction;
public MoveAction(Direction direction)
{
if (direction.equals(Direction.RIGHT))
{
int x = PLAYER.getX();
int y = PLAYER.getY();
WORLD_MAP.moveCharacter(x+1, y, x, y);
PLAYER.move(1, 0);
boardUpdate = true;
System.out.println("MOVE RIGHT");
}
if (direction.equals(Direction.LEFT))
{
int x = PLAYER.getX();
int y = PLAYER.getY();
WORLD_MAP.moveCharacter(x, y, x-1, y);
PLAYER.move(-1, 0);
boardUpdate = true;
System.out.println("MOVE LEFT");
}
if (direction.equals(Direction.UP))
{
int x = PLAYER.getX();
int y = PLAYER.getY();
WORLD_MAP.moveCharacter(x, y, x, y+1);
PLAYER.move(0, 1);
boardUpdate = true;
System.out.println("MOVE UP");
}
if (direction.equals(Direction.DOWN))
{
int x = PLAYER.getX();
int y = PLAYER.getY();
WORLD_MAP.moveCharacter(x, y, x, y-1);
PLAYER.move(0, -1);
boardUpdate = true;
System.out.println("MOVE DOWN");
}
}
#Override
public void actionPerformed(ActionEvent e)
{
}
}
}
This is the Board class
package rpgGame;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
/** Board GUI for implementation with various games
* Author: Kirill Levin, Troy Vasiga, Chris Ingram
*/
#SuppressWarnings("serial")
public class Board extends JPanel
{
private static final int X_DIM = 60;
private static final int Y_DIM = 60;
private static final int X_OFFSET = 30;
private static final int Y_OFFSET = 30;
private static final double MIN_SCALE = 0.25;
private static final int GAP = 10;
private static final int FONT_SIZE = 16;
// Grid colours
private static final Color GRID_COLOR_A = new Color(153,255,102);
private static final Color GRID_COLOR_B = new Color(136,255,77);
private Color[][] grid;
private Point lastClick; // How the mouse handling thread communicates
// to the board where the last click occurred
private String message = "";
private int numLines = 0;
private double[][] line = new double[4][100]; // maximum number of lines is 100
private int columns, rows;
private int originalWidth;
private int originalHeight;
private double scale;
/** A constructor to build a 2D board.
*/
public Board (int rows, int columns)
{
super( true );
JFrame boardFrame = new JFrame( "Board game" );
this.columns = columns;
this.rows = rows;
originalWidth = 2*X_OFFSET+X_DIM*columns;
originalHeight = 2*Y_OFFSET+Y_DIM*rows+GAP+FONT_SIZE;
this.setPreferredSize( new Dimension( originalWidth, originalHeight ) );
boardFrame.setResizable(true);
this.grid = new Color[columns][rows];
this.addMouseListener(
new MouseInputAdapter()
{
/** A method that is called when the mouse is clicked
*/
public void mouseClicked(MouseEvent e)
{
int x = (int)e.getPoint().getX();
int y = (int)e.getPoint().getY();
// We need to by synchronized to the parent class so we can wake
// up any threads that might be waiting for us
synchronized(Board.this)
{
int curX = (int)Math.round(X_OFFSET*scale);
int curY = (int)Math.round(Y_OFFSET*scale);
int nextX = (int)Math.round((X_OFFSET+X_DIM*grid.length)*scale);
int nextY = (int)Math.round((Y_OFFSET+Y_DIM*grid[0].length)*scale);
// Subtract one from high end so clicks on the black edge
// don't yield a row or column outside of board because of
// the way the coordinate is calculated.
if (x >= curX && y >= curY && x < nextX && y < nextY)
{
lastClick = new Point(y,x);
// Notify any threads that would be waiting for a mouse click
Board.this.notifyAll() ;
} /* if */
} /* synchronized */
} /* mouseClicked */
} /* anonymous MouseInputAdapater */
);
boardFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
boardFrame.setContentPane( this );
boardFrame.pack();
boardFrame.setVisible(true);
}
/** A constructor to build a 1D board.
*/
public Board (int cols)
{
this(1, cols);
}
private void paintText(Graphics g)
{
g.setColor( this.getBackground() );
g.setFont(new Font(g.getFont().getFontName(), Font.ITALIC+Font.BOLD, (int)(Math.round(FONT_SIZE*scale))));
int x = (int)Math.round(X_OFFSET*scale);
int y = (int)Math.round((Y_OFFSET+Y_DIM*grid[0].length)*scale + GAP ) ;
g.fillRect(x,y, this.getSize().width, (int)Math.round(GAP+FONT_SIZE*scale) );
g.setColor( Color.black );
g.drawString(message, x, y + (int)Math.round(FONT_SIZE*scale));
}
private void paintGrid(Graphics g)
{
for (int i = 0; i < this.grid.length; i++)
{
for (int j = 0; j < this.grid[i].length; j++)
{
if ((i%2 == 0 && j%2 != 0) || (i%2 != 0 && j%2 == 0))
g.setColor(GRID_COLOR_A);
else
g.setColor(GRID_COLOR_B);
int curX = (int)Math.round((X_OFFSET+X_DIM*i)*scale);
int curY = (int)Math.round((Y_OFFSET+Y_DIM*j)*scale);
int nextX = (int)Math.round((X_OFFSET+X_DIM*(i+1))*scale);
int nextY = (int)Math.round((Y_OFFSET+Y_DIM*(j+1))*scale);
int deltaX = nextX-curX;
int deltaY = nextY-curY;
g.fillRect( curX, curY, deltaX, deltaY );
Color curColour = this.grid[i][j];
if (curColour != null) // Draw pegs if they exist
{
g.setColor(curColour);
g.fillOval(curX+deltaX/4, curY+deltaY/4, deltaX/2, deltaY/2);
}
}
}
((Graphics2D) g).setStroke( new BasicStroke(0.5f) );
g.setColor(Color.BLACK);
int curX = (int)Math.round(X_OFFSET*scale);
int curY = (int)Math.round(Y_OFFSET*scale);
int nextX = (int)Math.round((X_OFFSET+X_DIM*grid.length)*scale);
int nextY = (int)Math.round((Y_OFFSET+Y_DIM*grid[0].length)*scale);
g.drawRect(curX, curY, nextX-curX, nextY-curY);
}
private void drawLine(Graphics g)
{
for (int i =0; i < numLines; i++ )
{
((Graphics2D) g).setStroke( new BasicStroke( 5.0f*(float)scale) );
g.drawLine( (int)Math.round((X_OFFSET+X_DIM/2.0+line[0][i]*X_DIM)*scale),
(int)Math.round((Y_OFFSET+Y_DIM/2.0+line[1][i]*Y_DIM)*scale),
(int)Math.round((X_OFFSET+X_DIM/2.0+line[2][i]*X_DIM)*scale),
(int)Math.round((Y_OFFSET+Y_DIM/2.0+line[3][i]*Y_DIM)*scale) );
}
}
/**
* Convert a String to the corresponding Color defaulting to Black
* with an invald input
*/
/*private Color convertColour( String theColour )
{
for( int i=0; i<COLOUR_NAMES.length; i++ )
{
if( COLOUR_NAMES[i].equalsIgnoreCase( theColour ) )
return COLOURS[i];
}
return DEFAULT_COLOUR;
}*/
/** The method that draws everything
*/
public void paintComponent( Graphics g )
{
this.setScale();
this.paintGrid(g);
this.drawLine(g);
this.paintText(g);
}
public void setScale()
{
double width = (0.0+this.getSize().width) / this.originalWidth;
double height = (0.0+this.getSize().height) / this.originalHeight;
this.scale = Math.max( Math.min(width,height), MIN_SCALE );
}
/** Sets the message to be displayed under the board
*/
public void displayMessage(String theMessage)
{
message = theMessage;
this.repaint();
}
/** This method will save the value of the colour of the peg in a specific
* spot. theColour is restricted to
* "yellow", "blue", "cyan", "green", "pink", "white", "red", "orange"
* Otherwise the colour black will be used.
*/
public void putPeg(Color colour, int row, int col)
{
this.grid[col][row] = colour;
this.repaint();
}
/** Same as putPeg above but for 1D boards
*/
public void putPeg(Color colour, int col)
{
this.putPeg(colour, 0, col );
}
/** Remove a peg from the gameboard.
*/
public void removePeg(int row, int col)
{
this.grid[col][row] = null;
repaint();
}
/** Same as removePeg above but for 1D boards
*/
public void removePeg(int col)
{
this.grid[col][0] = null;
repaint();
}
/** Draws a line on the board using the given co-ordinates as endpoints
*/
public void drawLine(double row1, double col1, double row2, double col2)
{
this.line[0][numLines]=col1;
this.line[1][numLines]=row1;
this.line[2][numLines]=col2;
this.line[3][numLines]=row2;
this.numLines++;
repaint();
}
/** Removes one line from a board given the co-ordinates as endpoints
* If there is no such line, nothing happens
* If multiple lines, all copies are removed
*/
public void removeLine(int row1, int col1, int row2, int col2)
{
int curLine = 0;
while (curLine < this.numLines)
{
// Check for either endpoint being specified first in our line table
if ( (line[0][curLine] == col1 && line[1][curLine] == row1 &&
line[2][curLine] == col2 && line[3][curLine] == row2) ||
(line[2][curLine] == col1 && line[3][curLine] == row1 &&
line[0][curLine] == col2 && line[1][curLine] == row2) )
{
// found a matching line: overwrite with the last one
numLines--;
line[0][curLine] = line[0][numLines];
line[1][curLine] = line[1][numLines];
line[2][curLine] = line[2][numLines];
line[3][curLine] = line[3][numLines];
curLine--; // perhaps the one we copied is also a match
}
curLine++;
}
repaint();
}
/** Waits for user to click somewhere and then returns the click.
*/
public Point getClick()
{
Point returnedClick = null;
synchronized(this) {
lastClick = null;
while (lastClick == null)
{
try {
this.wait();
} catch(Exception e) {
// We'll never call Thread.interrupt(), so just consider
// this an error.
e.printStackTrace();
System.exit(-1) ;
} /* try */
}
int x = (int)Math.floor((lastClick.getY()-X_OFFSET*scale)/X_DIM/scale);
int y = (int)Math.floor((lastClick.getX()-Y_OFFSET*scale)/Y_DIM/scale);
// Put this into a new object to avoid a possible race.
returnedClick = new Point(x,y);
}
return returnedClick;
}
/** Same as getClick above but for 1D boards
*/
public double getPosition()
{
return this.getClick().getY();
}
public int getColumns()
{
return this.columns;
}
public int getRows()
{
return this.rows;
}
}
You're shooting yourself in the foot with that thread code -- you're calling run() not start() on it
SYNC_BOARD.run();
This will run on the Swing event thread and risks completely freezing your GUI.
As a general rule, you should almost never extend Thread but rather implement Runnable, but regardless, don't use that Thread code -- Instead use a Swing Timer since your code has no breaks in it, no Thread.sleeps and it will make your CPU awfully busy, and the Swing Timer will help make sure that your code obeys Swing threading rules.
Also your MoveAction is wrong. Most of that code should go in the actionPerformed method. The constructor should just set the direction field and that's it.
Something like:
#SuppressWarnings("serial")
public class MoveAction extends AbstractAction {
Direction direction;
public MoveAction(Direction direction) {
// this is the only code the constructor should have!
this.direction = direction;
}
#Override
public void actionPerformed(ActionEvent e) {
// use direction to help make move in here
}
}
Understand that this is likely causing some major problems, since the constructor is called on program creation (hence your key bindings "work" when the program starts), but it's the actionPerformed that actually gets called when the right key is pressed.

Why is my ArrayList not looping through all values?

I'm trying to make a small app that bounces balls around the frame of the window. When I add more than 1 ball to the list; it doesn't loop through them as expected.
Here's my code:
The Main Class:
public class Main extends JFrame {
private static final long serialVersionUID = 1L;
private List<Ball> balls = new ArrayList<Ball>();
private List<Ball> tempballs = new ArrayList<Ball>();
private static Timer t;
public void addBall(Ball b) {
tempballs.add(b);
}
public void initUI() {
this.setSize(500, 500);
this.setDefaultCloseOperation(3);
this.setVisible(true);
}
private BufferStrategy bs;
private Random r = new Random();
public void paint() {
if (!tempballs.isEmpty()) {
balls.addAll(tempballs);
tempballs = new ArrayList<Ball>();
}
int i = 0;
System.out.println(balls.size());
for (Ball b : new ArrayList<Ball>(balls)) {
i++;
System.out.println(i);
if ((bs = this.getBufferStrategy()) == null) {
this.createBufferStrategy(2);
return;
}
if (bs.contentsLost() || bs.contentsRestored()) {
return;
}
if (b.y >= this.getHeight() - 100) {
b.ydirection = -r.nextDouble() * 5;
}
if (b.y < 20) {
b.ydirection = r.nextDouble() * 5;
}
if (b.x >= this.getWidth() - 100) {
b.xdirection = -r.nextDouble() * 5;
}
if (b.x < 0) {
b.xdirection = r.nextDouble() * 5;
}
b.x += b.xdirection;
b.y += b.ydirection;
if (b.xdirection > 0)
b.xdirection += 0.1;
else
b.xdirection += -0.1;
if (b.ydirection > 0)
b.ydirection += 0.1;
else
b.ydirection += -0.1;
Graphics g = bs.getDrawGraphics();
g.fillOval((int) b.x, (int) b.y, 100, 100);
bs.show();
g.dispose();
bs.dispose();
}
i = 0;
}
public static void main(String[] args) {
try {
final Main m = new Main();
m.addMouseListener(new Mouse(m));
m.initUI();
t = new Timer();
TimerTask tt = new TimerTask() {
#Override
public void run() {
m.paint();
}
};
t.schedule(tt, Calendar.getInstance().getTime(), 20);
} catch (ConcurrentModificationException e) {
e.printStackTrace();
}
}
}
Here's the Ball class:
public class Ball {
private Random r = new Random();
public double y, x, ydirection, xdirection;
public Ball(int x, int y) {
this.y = y;
this.x = x;
ydirection = r.nextGaussian() * 5;
xdirection = r.nextGaussian() * 5;
}
}
and the mouse listener:
public class Mouse implements MouseListener {
Main m;
public Mouse(Main m) {
this.m = m;
}
#Override
public void mouseClicked(MouseEvent e) {
m.addBall(new Ball(e.getX(), e.getY()));
System.out.println("cl");
}
#Override
public void mouseEntered(MouseEvent arg0) {
}
#Override
public void mouseExited(MouseEvent arg0) {
}
#Override
public void mousePressed(MouseEvent arg0) {
}
#Override
public void mouseReleased(MouseEvent arg0) {
}
}
Additional details:
It only loops through the first item in the list, but the list size grows.
I'm using java 6, but I will change versions if needed.
If your loop does not behave as intended, try to find out why it is cancelled. You have two return statements in your loop which could cause this behavior. Make sysouts before those returns to figure out which one is the cause. Then find out why your if around the return is true. To digg deeper you can use your IDE's debugging mode and place breakpoints at interesting lines or use the step mode to run one line of code at a time.
Besides this you can place both if before the loop, the values they are checking should not change while you are in the paint() function (you are using the UI thread which might change them).
You have below return statements inside the loop. If the execution reaches any of the return statements, the loop ends.
Figure out, if you are entering these conditions and returning for the first value in the list.
if ((bs = this.getBufferStrategy()) == null) {
this.createBufferStrategy(2);
return;
}
if (bs.contentsLost() || bs.contentsRestored()) {
return;
}

Categories

Resources