So I have been coding this little UI for a while and for some reason the repaint() method seems to cause the image generated on the screen to flicker and flash. Is there a way to prevent this?
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;
import java.util.Timer;
public class userInterfaceSimple extends JFrame implements ActionListener
{
static ArrayList<Creature> list = new ArrayList<Creature>();
static JButton next = new JButton("Begin");
static Timer timer = new Timer();
static JFrame frame = new JFrame();
final static JPanel pane = new JPanel();
public static void main(String[] args)
{
new userInterfaceSimple();
}
public userInterfaceSimple()
{
super("Toy Planet: Life, a Simulation");
setSize(1000,1000);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new BorderLayout());
add(pane, BorderLayout.CENTER);
next.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
if(next.getText().equals("Begin"))
{
next.setText("Stop");
timer.scheduleAtFixedRate(new TimerTask()
{
#Override
public void run()
{
checkRules();
repaint();
}
}, 250, 250);
}
else
{
next.setText("Begin");
timer.cancel();
timer = new Timer();
}
}
});
add(next, BorderLayout.SOUTH);
for(int j = 0; j < 3; j ++)
{
Creature orgArc = new Creature();
for(int i = 0; i < 20; i++)
{
list.add(new Creature(orgArc.gene));
}
}
sortList();
}
public void paint(Graphics g)
{
super.paint(g);
for(int i = 0; i < list.size(); i++)
{
g.setColor(list.get(i).getColor());
int size = list.get(i).getSize();
g.fillOval(list.get(i).getX() - size/2, list.get(i).getY() - size/2,size,size);
}
}
public static void checkRules()
{
int size = list.size();
for(int i = 0; i < size; i++)
{
try
{
for(int j = 0; j < size; j++)
{
boolean alreadyMoved = false;
if(!(list.get(i)).equals(list.get(j)))
{
int x = list.get(j).getX() - (list.get(i)).getX();
int y = list.get(j).getY() - (list.get(i)).getY();
double z = Math.sqrt(x*x + y*y);
//Passive Interaction Rules
//Sight Check
if(list.get(j).getSense() > z)
{
list.get(j).moveTo(list.get(i));
alreadyMoved = true;
}
if(z <= 1.5)
{
//Active Interaction Rules
//Breeding Check
if(list.get(j).isCompatible(list.get(i)))
{
list.add(list.get(j).breed(list.get(i)));
sortList();
}
//Eating Check
else if(list.get(j).getAggro() > 15 || list.get(j).getDiet() > 3)
{
list.get(j).eat(list.get(i));
}
}
}
//If no checks fire then move randomly
if(alreadyMoved == false)
{
Organism temp = new Organism();
list.get(j).moveTo(temp);
}
}
//Hunger Check
list.get(i).hungerCheck();
}
catch(Exception e)
{
}
list.get(i).validate();
size = size - validateList();
}
/*for(int i = 0; i < list.size(); i ++) //for debugging.
{
System.out.println(list.get(i).getInfo());
}
System.out.println("----");*/
}
public static int getClosestCreature(Organism org)
{
int x,y;
double z;
double tempZ = 100000;
int closest = 0;
for(int i = 0; i < list.size(); i++)
{
if(!(list.get(i)).equals(org))
{
x = org.getX() - (list.get(i)).getX();
y = org.getY() - (list.get(i)).getY();
z = Math.sqrt(x*x + y*y);
if(z < tempZ)
{
closest = i;
tempZ = z;
}
}
}
return (closest);
}
public static int validateList()
{
int netLoss = 0;
for(int i = 0; i < list.size(); i++)
{
if((list.get(i)).getAlive() == false)
{
list.remove(i);
netLoss = netLoss + 1;
}
}
return netLoss;
}
public static void sortList()
{
Creature temp;
for(int i = 0; i < list.size(); i ++)
{
for(int j = 1; j < list.size() - i; j++)
{
if(list.get(j - 1).getSpeed() > list.get(j).getSpeed())
{
temp = list.get(j - 1);
list.set(j - 1, list.get(j));
list.set(j, temp);
}
}
}
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
}
}
Thanks for you help!
Yes, it is simple: Double Buffering should fix it. For more reference, visit http://docs.oracle.com/javase/tutorial/extra/fullscreen/doublebuf.html
First you should create private BufferedImage bufferImage;
In your public void paint(Graphics g) you should paint stuff into this bufferImage, and then, when bufferImage is filled, paint it g.drawImage(bufferImage, 0, 0, null);
public void paint(Graphics g) {
Graphics2D gr = bufferImage.createGraphics();
for(int i = 0; i < list.size(); i++)
{
gr.setColor(list.get(i).getColor());
int size = list.get(i).getSize();
gr.fillOval(list.get(i).getX() - size/2, list.get(i).getY() - size/2,size,size);
}
super(g);
g.drawImage(bufferImage, 0, 0, null);
}
Not compiled, but something like this should work.
Related
I have been making a sorting algorithm visualizer in Java using Java Swing and AWT and coded insertion sort to see if it would be able to work. The algorithm works in the sense that when this program is run you can shuffle and sort the algorithm using insertion short but you don't actually see the algorithm in action. It just happens instantly and have been looking for ways to add some sort of delay but I can't find any sources to help me.
I set it up with 4 java classes, the main which just init's the window, the window class, array visualizer class, and the insertion sort class.
Window.java:
public class Window implements ActionListener {
//Window width
protected static int WINDOW_WIDTH = 1980;
//Window height
protected static int WINDOW_HEIGHT = 1080;
private int delay = 100;
//This will draw all our rectangles
protected static ArrayVisualizer arrayVisualizer;
//Make a new JFrame
JFrame window = new JFrame();
JButton shuffleBut = new JButton("Shuffle");
JButton startSort = new JButton("Start Sorting");
public Window() {
initWindow();
}
private void addButtons() {
//Shuffle Button
shuffleBut.setBounds(100, 50, 100, 100);
shuffleBut.setBackground(Color.white);
shuffleBut.addActionListener(this);
startSort.setBounds(500, 50, 100, 100);;
startSort.setBackground(Color.white);
startSort.addActionListener(taskPerformer);
window.add(shuffleBut);
window.add(startSort);
}
private void initWindow() {
ImageIcon logo = new ImageIcon();
window = new JFrame();
window.setTitle("JAlgorithms");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
window.setLocationRelativeTo(null);
window.setResizable(false);
addButtons();
arrayVisualizer = new ArrayVisualizer();
window.add(arrayVisualizer);
arrayVisualizer.repaint(); //Will call paint component method
window.pack();
window.setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
if(e.getSource() == shuffleBut) {
arrayVisualizer.shuffle(ArrayVisualizer.array);
arrayVisualizer.repaint();
}
}
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent event) {
if(event.getSource() == startSort) {
arrayVisualizer.sort(ArrayVisualizer.array);
arrayVisualizer.repaint();
}
}
};
}
ArrayVisualizer.java:
public class ArrayVisualizer extends JPanel {
protected static int[] array;
private final int REC_WIDTH = 1; //1980 rectangles
private final int NUMBER_OF_RECS = Window.WINDOW_WIDTH / REC_WIDTH;
public ArrayVisualizer() {
array = new int[NUMBER_OF_RECS];
generateRandom(array);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D graphics = (Graphics2D) g.create();
graphics.setColor(Color.orange);
for(int i = 0; i < NUMBER_OF_RECS; i++) {
int height = array[i] * 4; //Done for scaling
int recX = i + (REC_WIDTH - 1) * i; //Read fillRect documentation
int recY = Window.WINDOW_HEIGHT - height; //Read fillRect documentation
graphics.fillRect(recX, recY, REC_WIDTH, height);
}
}
//This will return the Dimension of the actual rectangles. i.e the rectangles will only exist when this exists, almost like a canvas in javascript
#Override
public Dimension getPreferredSize() {
return new Dimension(Window.WINDOW_WIDTH, Window.WINDOW_HEIGHT);
}
//Creates a random unsorted array with numbers 1-200
protected void generateRandom(int[] array) {
Random number = new Random();
for(int i = 0; i < NUMBER_OF_RECS; i++) {
array[i] = number.nextInt(200);
}
}
protected void shuffle(int[] array) {
generateRandom(array);
}
protected void sort(int[] array) {
InsertionSort.insertionSort(array);
}
}
InsertionSort.java:
public class InsertionSort {
public static void insertionSort(int arr[])
{
int n = arr.length;
for (int i = 1; i < n; ++i) {
int key = arr[i];
int j = i - 1;
/* Move elements of arr[0..i-1], that are
greater than key, to one position ahead
of their current position */
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j = j - 1;
}
arr[j + 1] = key;
}
}
}
UPDATE (Here is the modified code, I added selection sort as well and heavily modified the suggestion I checkmarked):
Main.java:
public class Main {
public static void main(String[] args) {
//Opens window
Window window = new Window();
}
}
Window.java:
import java.awt.Color;
import java.awt.Image;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.EventQueue;
import javax.swing.ImageIcon;
import java.awt.Graphics2D;
import java.awt.Graphics;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Window implements ActionListener {
//Window width
protected static int WINDOW_WIDTH = 1440;
//Window height
protected static int WINDOW_HEIGHT = 1080;
//This will draw all our rectangles
protected static ArrayVisualizer arrayVisualizer;
//Make a new JFrame
JFrame window = new JFrame();
JButton shuffleBut = new JButton("Shuffle");
JButton startSort = new JButton("Start Sorting");
public Window() {
initWindow();
}
private void addButtons() {
//Shuffle Button
shuffleBut.setBounds(100, 50, 100, 100);
shuffleBut.setBackground(Color.white);
shuffleBut.addActionListener(this);
startSort.setBounds(500, 50, 100, 100);;
startSort.setBackground(Color.white);
window.add(shuffleBut);
window.add(startSort);
}
private void initWindow() {
ImageIcon logo = new ImageIcon();
window = new JFrame();
window.setTitle("JAlgorithms");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
window.setLocationRelativeTo(null);
window.setResizable(false);
addButtons();
startSort.addActionListener(taskPerformer);
arrayVisualizer = new ArrayVisualizer();
window.add(arrayVisualizer);
arrayVisualizer.repaint(); //Will call paint component method
window.pack();
window.setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
if(e.getSource() == shuffleBut) {
arrayVisualizer.shuffle(ArrayVisualizer.array);
arrayVisualizer.repaint();
}
}
ActionListener taskPerformer = new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
if(event.getSource() == startSort) {
if(!timer.isRunning()) { //If timer is not running
timer.setInitialDelay(0); //Set initial delay
timer.start(); //Start the timer
}
}
}
};
ActionListener sortWithDelay = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(sorted(ArrayVisualizer.array)) { //If it is sorted
timer.stop(); //Stop the timer
return;
} else {
arrayVisualizer.sort(ArrayVisualizer.array); //If it is not sorted continue the sort
arrayVisualizer.repaint(); //Called after each swap
}
}
};
private int delay = 10; //Milliseconds
private Timer timer = new Timer(delay, sortWithDelay);
private boolean sorted(int[] array) {
for(int i = 0; i < array.length - 1; i++) {
if(array[i] > array[i+1]) {
return false;
}
}
return true;
}
}
ArrayVisualizer.java:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.Array;
import javax.swing.*;
import java.util.*;
public class ArrayVisualizer extends JPanel {
private static final long serialVersionUID = 1L;
private InsertionSort insertionSort = new InsertionSort();
private SelectionSort selectionSort = new SelectionSort();
protected static int[] array;
private final int REC_WIDTH = 1; //1980 rectangles
private final int NUMBER_OF_RECS = Window.WINDOW_WIDTH / REC_WIDTH;
private final int[] barColors;
public ArrayVisualizer() {
setBackground(Color.black);
array = new int[NUMBER_OF_RECS];
barColors = new int[NUMBER_OF_RECS];
generateRandom(array);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D graphics = (Graphics2D) g.create();
graphics.setColor(Color.white);
for(int i = 0; i < NUMBER_OF_RECS; i++) {
int height = array[i] * 4; //Done for scaling
int recX = i + (REC_WIDTH - 1) * i; //Read fillRect documentation
int recY = Window.WINDOW_HEIGHT - height; //Read fillRect documentation
graphics.fillRect(recX, recY, REC_WIDTH, height);
}
}
//This will return the Dimension of the actual rectangles. i.e the rectangles will only exist when this exists, almost like a canvas in javascript
#Override
public Dimension getPreferredSize() {
return new Dimension(Window.WINDOW_WIDTH, Window.WINDOW_HEIGHT);
}
//Creates a random unsorted array with numbers 1-200
protected void generateRandom(int[] array) {
Random number = new Random();
for(int i = 0; i < NUMBER_OF_RECS; i++) {
array[i] = number.nextInt(200);
}
}
protected void shuffle(int[] array) {
generateRandom(array);
}
protected void sort(int[] array) {
selectionSort.sortWithDelay(array);
}
}
InsertionSort.java:
public class InsertionSort {
private int i = 1;
private int j = 0;
public void sortWithDelay(int arr[])
{
int n = arr.length;
if(i < n) {
int key = arr[i];
j = i - 1;
/* Move elements of arr[0..i-1], that are
greater than key, to one position ahead
of their current position */
while(j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j = j - 1;
}
arr[j + 1] = key;
++i;
}
}
}
SelectionSort.java:
public class SelectionSort {
private int i = 1;
private int j = 0;
public void sortWithDelay(int arr[])
{
int n = arr.length - 1;
if(i < n) {
int min_index = i;
for(int j = i + 1; j < n; j++) {
if(arr[j] < arr[min_index]) {
min_index = j;
}
}
int temp = arr[min_index];
arr[min_index] = arr[i];
arr[i] = temp;
++i;
}
}
}
Call an action with a swing timer every second that will return an intermediate array. Then paint this intermediate array. Repeat until the array is sorted.
To obtain an intermediate array, modify the InsertionSort from a static method to a class method that will store the variable i so that we can resume sorting when called again.
public class Window implements ActionListener {
//Window width
protected static int WINDOW_WIDTH = 1980;
//Window height
protected static int WINDOW_HEIGHT = 1080;
//This will draw all our rectangles
protected static ArrayVisualizer arrayVisualizer;
//Make a new JFrame
JFrame window = new JFrame();
JButton shuffleBut = new JButton("Shuffle");
JButton startSort = new JButton("Start Sorting");
public Window() {
initWindow();
}
private void addButtons() {
//Shuffle Button
shuffleBut.setBounds(100, 50, 100, 100);
shuffleBut.setBackground(Color.white);
shuffleBut.addActionListener(this);
startSort.setBounds(500, 50, 100, 100);;
startSort.setBackground(Color.white);
startSort.addActionListener(taskPerformer);
window.add(shuffleBut);
window.add(startSort);
}
private void initWindow() {
ImageIcon logo = new ImageIcon();
window = new JFrame();
window.setTitle("JAlgorithms");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
window.setLocationRelativeTo(null);
window.setResizable(false);
addButtons();
arrayVisualizer = new ArrayVisualizer();
window.add(arrayVisualizer);
arrayVisualizer.repaint(); //Will call paint component method
window.pack();
window.setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
if(e.getSource() == shuffleBut) {
arrayVisualizer.shuffle(ArrayVisualizer.array);
arrayVisualizer.repaint();
}
}
private int delay = 1000; // delay is 1s
private Timer timer = new Timer(delay, sortWithDelay);
ActionListener taskPerformer = new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
if(event.getSource() == startSort) {
// Start the timer that shows a swap every second
if(!timer.isRunning()) {
timer.setInitialDelay(0);
timer.start();
}
}
}
};
ActionListener sortWithDelay = new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
if(isSorted(ArrayVisualizer.array)) {
timer.stop();
return;
}
arrayVisualizer.sort(ArrayVisualizer.array);
arrayVisualizer.repaint();
}
};
private boolean isSorted(int[] arr) {
for (int i = 0; i < a.length - 1; i++) {
if (a[i] > a[i + 1]) {
return false;
}
}
return true;
}
}
public class ArrayVisualizer extends JPanel {
private InsertionSort insertionSort = new InsertionSort();
protected static int[] array;
private final int REC_WIDTH = 1; //1980 rectangles
private final int NUMBER_OF_RECS = Window.WINDOW_WIDTH / REC_WIDTH;
public ArrayVisualizer() {
array = new int[NUMBER_OF_RECS];
generateRandom(array);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D graphics = (Graphics2D) g.create();
graphics.setColor(Color.orange);
for(int i = 0; i < NUMBER_OF_RECS; i++) {
int height = array[i] * 4; //Done for scaling
int recX = i + (REC_WIDTH - 1) * i; //Read fillRect documentation
int recY = Window.WINDOW_HEIGHT - height; //Read fillRect documentation
graphics.fillRect(recX, recY, REC_WIDTH, height);
}
}
//This will return the Dimension of the actual rectangles. i.e the rectangles will only exist when this exists, almost like a canvas in javascript
#Override
public Dimension getPreferredSize() {
return new Dimension(Window.WINDOW_WIDTH, Window.WINDOW_HEIGHT);
}
//Creates a random unsorted array with numbers 1-200
protected void generateRandom(int[] array) {
Random number = new Random();
for(int i = 0; i < NUMBER_OF_RECS; i++) {
array[i] = number.nextInt(200);
}
}
protected void shuffle(int[] array) {
generateRandom(array);
}
protected void sort(int[] array) {
insertionSort.sortWithDelay(array);
}
}
public class InsertionSort {
private int i = 1;
public void sortWithDelay(int arr[])
{
int n = arr.length;
if(i < n) {
int key = arr[i];
int j = i - 1;
/* Move elements of arr[0..i-1], that are
greater than key, to one position ahead
of their current position */
while(j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j = j - 1;
}
arr[j + 1] = key;
++i;
}
}
}
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.
I am making a basic platformer game and I need to make it so that every time you jump all the platforms that are below where you jumped disappear and every platform above moves down. It is still in development so just ignore some of the things that are commented out and the messy mode.
Basically, my problem is that when I try to clear all the platforms below the one that you clicked it works fine using this code:
for(int i = 0; i < platforms.length; i++) {
if(platforms[i] == null) {
continue;
}
if(parameters[i][1] > y) {
platforms[i].setVisible(false);
}
}
However later when I try to use .setvisible(true) on the platform that you clicked it doesn't work. Here is the full code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.math.*;
import java.util.*;
public class Main implements ActionListener {
static JFrame frame;
static JPanel panel;
//static JLabel title;
//static JLabel expl;
static JButton[] platforms;
static Random random;
static boolean playing;
static int[][] parameters;
static int platformsMade;
static int clicked;
static int preClicked;
static int score;
static int lives;
static int standing;
static int speed;
static double gravity;
static int jump;
static String action;
public Main(int seed) throws Exception {
frame = new JFrame("Snakes 'n' Adders");
panel = new JPanel(null);
//title = new JLabel("Snakes 'n' Adders", SwingConstants.CENTER);
//expl = new JLabel("");
platforms = new JButton[100000];
random = new Random();
parameters = new int[100000][5];
lives = 3;
speed = 10;
gravity = 9.81;
jump = 5;
action = "";
//panel.add(title);
//panel.add(expl);
frame.add(panel);
panel.setVisible(true);
frame.setVisible(true);
frame.setSize(500, 500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
random.setSeed(seed);
panel.setBackground(Color.CYAN);
//title.setFont(new Font("Arial", Font.PLAIN, 50));
}
public void drawPlatform(int x, int y, int w, int h, int colour) {
platforms[platformsMade] = new JButton();
platforms[platformsMade].addActionListener(this);
platforms[platformsMade].setBounds(x, y, w, h);
switch(colour) {
case 0:
platforms[platformsMade].setBackground(Color.BLUE);
break;
case 1:
platforms[platformsMade].setBackground(Color.GREEN);
break;
case 2:
platforms[platformsMade].setBackground(Color.RED);
break;
case 3:
platforms[platformsMade].setBackground(Color.ORANGE);
break;
case 4:
platforms[platformsMade].setBackground(Color.GRAY);
break;
}
parameters[platformsMade][0] = x;
parameters[platformsMade][1] = y;
parameters[platformsMade][2] = w;
parameters[platformsMade][3] = h;
parameters[platformsMade][4] = colour;
panel.add(platforms[platformsMade]);
platforms[platformsMade].setVisible(true);
panel.repaint();
platformsMade++;
}
public void movePlatforms(int distance, int i) {
if(platforms[i] == null) {
NullPointerException end = new NullPointerException();
throw end;
}
//this.drawPlatform(parameters[i][0], parameters[i][1] + distance, parameters[i][2], parameters[i][3], parameters[i][4]);
//platforms[i].setVisible(false);
platforms[i].setBounds(parameters[i][0], parameters[i][1] + distance, parameters[i][2], parameters[i][3]);
parameters[i][1] = parameters[i][1] + distance;
}
#Override
public void actionPerformed(ActionEvent event) {
preClicked = clicked;
for(int i = 0; i < platforms.length; i++) {
if(event.getSource() == platforms[i]) {
clicked = i;
break;
}
}
int y = parameters[clicked][1];
for(int i = 0; i < platforms.length; i++) {
if(platforms[i] == null) {
continue;
}
if(parameters[i][1] > y) {
platforms[i].setVisible(false);
}
else {
this.movePlatforms(150, i);
}
this.movePlatforms(150, clicked);
}
for(int i = 0; i <= random.nextInt(2); i++) {
this.drawPlatform(random.nextInt(500), random.nextInt(500), 50, 50, random.nextInt(4));
}
/*for(int i = 0; i < platforms.length; i++) {
if(platforms[i] == null) {
continue;
}
if(platforms[i].getY() > frame.getY() - platforms[i].getHeight()) {
platforms[i].setVisible(false);
}
}*/
platforms[clicked].addActionListener(this);
platforms[clicked].setVisible(true);
standing = clicked;
for(int i = 0; i < platforms.length; i++) {
if(platforms[i] == null) {
continue;
}
if(clicked == i) {
platforms[i].setText("|");
}
else {
platforms[i].setText("");
}
}
}
public static void main(String[] args) throws Exception {
Main sna = new Main(7845);
//title.setVisible(true);
//expl.setVisible(false);
//Thread.sleep(5000);
//title.setVisible(false);
//expl.setVisible(true);
//Thread.sleep(40000);
for(int i = 0; i < 3; i++) {
sna.drawPlatform(random.nextInt(500), random.nextInt(500), 50, 50, random.nextInt(4));
}
int ticks = 0;
platforms[standing].setText("|");
}
}
I am working on a game that my friends invented - a little variation on tic tac toe: using a 4x4 board, one player (x) need to get 3 x's in a particular manner while the other can place one 'z' and one 'o' every turn and needs to fill the entire board. My problem is not with the rules and algoritems, but with the graphics: I don't have a lot of experience with graphics, and just can't get my board to work (even without any rules - just show up as needed).
I have a Board class that represents a board. A board has a two dimensional array of Cells. Each Cell (Cell = another class of mine) is also a JButton, and I would like that every time a button is clicked his image will change - so I decided to use ImageIcon. I also have a GameMain class to control the game and a Tools class to add two buttons - 'Exit' and 'Reset'.
If you could please help me by suggesting ways to get my board to load properly, I would appreciate that. Currently the board doesn't show up at all, and if I tweak the code a bit it shows up but the buttons won't show at all.
Here's the code: GameMain.java:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class GameMain extends JPanel {
private Turn _turn;
Board _board;
private Tools _buttons;
private boolean isOver = false;
public enum GameState {PLAYING, xWON, oWON};
private GameState _currentState;
// Name-constants for the various dimensions used for graphics drawing
public static final int CELL_SIZE = 100; // cell width and height (square)
public static final int CANVAS_WIDTH = CELL_SIZE * 4; // the drawing canvas
public static final int CANVAS_HEIGHT = CELL_SIZE * 4;
public static final int GRID_WIDTH = 8; // Grid-line's width
public static final int GRID_WIDTH_HALF = GRID_WIDTH / 2; // Grid-line's half-width
public GameMain() {
this.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if (_currentState == GameState.PLAYING) {
updateGame();
} else {
initGame(); //game over, restart
}
repaint();
}
});
setLayout(new BorderLayout());
setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT + 30));
_board = new Board();
_buttons = new Tools();
initGame();
_buttons.SetObject(_board);
add(_board, BorderLayout.CENTER);
add(_buttons, BorderLayout.SOUTH);
}
public void initGame() {
_turn = Turn.X;
_board.init();
_currentState = GameState.PLAYING;
}
public void updateGame() {
if (_board.hasWonX()) {
_currentState = GameState.xWON;
} else if (_board.hasWonO()) {
_currentState = GameState.oWON;
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
setBackground(Color.WHITE);
_board.paint(g);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("xBlock");
frame.setSize(500, 500);
// Set the content-pane of the JFrame to an instance of main JPanel
frame.setContentPane(new GameMain());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null); // center the application window
frame.setVisible(true); // show it
}
});
}
}
Board:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class Board extends JPanel implements ActionListener {
private Cell[][] _cells;
private Turn _turn;
public Board() {
setLayout(new GridLayout(4, 4));
_cells = new Cell[4][4];
_turn = Turn.X;
for (int i = 0; i < _cells.length; i++) {
for (int j = 0; j < _cells[0].length; j++) {
_cells[i][j] = new Cell(i, j);
_cells[i][j].addActionListener(this);
add(_cells[i][j]);
}
}
}
//initiate board
public void init() {
_turn = Turn.X;
for (int i = 0; i < _cells.length; i++) {
for (int j = 0; j < _cells[0].length; j++) {
_cells[i][j].setState(State.EMPTY);
}
}
}
public void fillCell(Cell c) {
if (c.getState() == State.EMPTY) {
c.setState(_turn.ordinal());
c.setEnabled(false);
c.draw();
_turn = _turn.getNext();
}
}
public void checkCellsAround(Cell c) {
State state = c.getState();
State right, left, up, down;
if (c.getJ() < 3 && c.getJ() > 0) {
right = _cells[c.getI()][c.getJ() + 1].getState();
left = _cells[c.getI()][c.getJ() - 1].getState();
} else if (c.getJ() == 0) {
right = _cells[c.getI()][c.getJ() + 1].getState();
left = State.EMPTY;
} else {
right = State.EMPTY;
left = _cells[c.getI()][c.getJ() - 1].getState();
}
if (c.getI() < 3 && c.getI() > 0) {
up = _cells[c.getI() - 1][c.getJ()].getState();
down = _cells[c.getI() + 1][c.getJ()].getState();
} else if (c.getI() == 0) {
up = State.EMPTY;
down = _cells[c.getI() + 1][c.getJ()].getState();
} else {
up = _cells[c.getI() - 1][c.getJ()].getState();
down = State.EMPTY;
}
switch (state) {
case EMPTY:
break;
case X:
if ((left == State.O && right == State.O) || (up == State.O && down == State.O) || (left == State.Z && right == State.Z) || (up == State.Z && down == State.Z)) {
c.setState(State.HOURGLASS);
}
break;
case O:
if ((left == State.X && right == State.X) || (up == State.X && down == State.X)) {
c.setState(State.EMPTY);
}
break;
case Z:
if ((left == State.X && right == State.X) || (up == State.X && down == State.X)) {
c.setState(State.HOURGLASS);
}
break;
case HOURGLASS:
break;
case SCRIBBLE:
break;
}
}
public void actionPerformed(ActionEvent E) {
Cell c = (Cell) E.getSource();
fillCell(_cells[c.getI()][c.getJ()]);
}
public boolean hasWonO() {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (_cells[i][j].getState() == State.EMPTY) {
return false;
}
}
}
return true;
}
public boolean hasWonX() {
return false;
}
public void paint(Graphics g) {
g.setColor(Color.GRAY);
for (int i = 1; i < 4; i++) {
g.fillRoundRect(0, GameMain.CELL_SIZE * i - GameMain.GRID_WIDTH_HALF,
GameMain.CANVAS_WIDTH - 1, GameMain.GRID_WIDTH,
GameMain.GRID_WIDTH, GameMain.GRID_WIDTH);
}
for (int j = 1; j < 4; j++) {
g.fillRoundRect(GameMain.CELL_SIZE * j - GameMain.GRID_WIDTH_HALF, 0,
GameMain.GRID_WIDTH, GameMain.CANVAS_HEIGHT - 1,
GameMain.GRID_WIDTH, GameMain.GRID_WIDTH);
}
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
_cells[i][j].draw();
}
}
}
}
Cell.java:
import javax.swing.ImageIcon;
import javax.swing.JButton;
public class Cell extends JButton {
private int _i, _j;
private State _state;
ImageIcon X = new ImageIcon(this.getClass().getResource("x-icon.png"));
ImageIcon O = new ImageIcon(this.getClass().getResource("o-icon.png"));
ImageIcon Z = new ImageIcon(this.getClass().getResource("z-icon.png"));
ImageIcon Hourglass = new ImageIcon(this.getClass().getResource("hourglass-icon.png"));
ImageIcon Scribble = new ImageIcon(this.getClass().getResource("scribble-icon.png"));
public Cell() {
this.setEnabled(true);
_i = 0;
_j = 0;
_state = State.EMPTY;
}
public Cell(int i, int j) {
this.setEnabled(true);
_i = i;
_j = j;
_state = State.EMPTY;
}
public int getI() {
return _i;
}
public int getJ() {
return _j;
}
public void setState(State state) {
_state = state;
if (state == State.EMPTY) {
this.setEnabled(true);
}
}
public void setState(int index) {
_state = State.values()[index];
}
public State getState() {
return _state;
}
public void draw() {
switch (_state) {
case EMPTY:
this.setIcon(null);
break;
case X:
this.setIcon(X);
break;
case O:
this.setIcon(X);
break;
case Z:
this.setIcon(X);
break;
case HOURGLASS:
this.setIcon(X);
break;
case SCRIBBLE:
this.setIcon(X);
break;
}
}
public void highlight() {
}
}
Tools.java:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class Tools extends JPanel {
private JButton _exit, _reset;
private Board _board;
Tools() {
setLayout(new FlowLayout());
_exit = new JButton("Exit");
_reset = new JButton("Reset");
_exit.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
System.exit(0);
}
});
_reset.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
_board.init();
}
});
add(_exit);
add(_reset);
}
public void SetObject(Board b) {
_board = b;
}
}
State.java:
public enum State {
EMPTY, X, O, Z, HOURGLASS, SCRIBBLE;
public State getNext() {
return State.values()[(this.ordinal() + 1) % State.values().length];
}
}
Turn.java:
public enum Turn {
X, O, Z;
public Turn getNext() {
return Turn.values()[(this.ordinal() + 1) % Turn.values().length];
}
}
Thanks in advance!
So after running it, you are getting an ArrayIndexOutOfBoundsException at this line in the paint method of the Board class:
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
_cells[i][j].draw(); <==========
}
}
Not sure how your game works, but by looking at the loops previous to this one, you are accessing only up to index 3 ( for (int j = 1; j < 4; j++) { ). So if you change the loop max to 4, it gets the game up and running.
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
_cells[i][j].draw();
}
}
Learning to read exceptions and stack traces is very important. It will save you a lot of future headaches. Take some time to read What is a stack trace, and how can I use it to debug my application errors?
And like I said in my comments,
It doesn't look like you're doing anything with the paintComponent in the GameMain. You should just get rid of the paintComponent method altogether in that class. Instead of trying to call board.paint(g);, try to just call board.repaint() in the mouse listener, instead of trying to repaint the main game panel. And just set the background in the constructor of the GameMain instead of in the paintComponent method.
Also in the Board class use paintComponent rather than paint. and don't forget to call super.paintComponent in the paintComponent method
Fixing all the things above, get it work (I guess).
UPDATE
As the MadMan pointed out in the comment below, it would be better to use the _cells.length to avoid having to rely magic numbers. This way you will be sure not to access an inexistant index
for (int i = 0; i < cells.length; i++) {
for (int j = 0; j < cells[i].length; j++) {
_cells[i][j].draw();
}
}
I'm working on a Java implementation of Conway's game of life as a personal project for myself. So far it works but the rules are coming out wrong. The expected patterns aren't showing up as well as it should. Is there something wrong with my code?
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Cell extends JComponent implements MouseListener {
private int row, col;
private boolean isLiving;
public Cell(int r, int c) {
this.row = r;
this.col = c;
this.addMouseListener(this);
}
public void isAlive(int neighbors) {
if (this.isLiving) {
if (neighbors < 2) {
this.isLiving = false;
} else if (neighbors == 2 || neighbors == 3) {
this.isLiving = true;
} else if (neighbors > 3) {
this.isLiving = false;
}
} else {
if (neighbors == 3) {
this.isLiving = true;
}
}
}
public boolean isLiving() {
return this.isLiving;
}
public void paintComponent(Graphics g) {
if (this.isLiving) {
g.fillRect(0, 0, 10, 10);
} else {
g.drawRect(0, 0, 10, 10);
}
}
public void mouseClicked(MouseEvent e) {
this.isLiving = !this.isLiving;
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
}
I suspect1 the problem with your code is that it is setting the cells to living or dead as soon as the neighbors are checked. That caused my early variants of this code to fail. That change of state has to be delayed until the entire grid (biosphere) has been checked.
This example shows typical Game of Life behavior.
Note that "suspicions ain't answers", so it is best to post an SSCCE.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.Random;
public class GameOfLife extends JPanel {
private final int row, col;
private boolean isLiving;
public static Random random = new Random();
public GameOfLife(int r, int c) {
this.row = r;
this.col = c;
MouseListener listener = new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
isLiving = !isLiving;
repaint();
}
};
this.addMouseListener(listener);
isLiving = random.nextBoolean();
}
public boolean isAlive(int neighbors) {
boolean alive = false;
if (this.isLiving) {
if (neighbors < 2) {
alive = false;
} else if (neighbors == 2 || neighbors == 3) {
alive = true;
} else if (neighbors > 3) {
alive = false;
}
} else {
if (neighbors == 3) {
alive = true;
}
}
return alive;
}
public void setAlive(boolean alive) {
isLiving = alive;
}
public boolean isLiving() {
return this.isLiving;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (this.isLiving) {
g.fillRect(0, 0, getWidth() - 1, getHeight() - 1);
} else {
g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
}
}
public static void main(String[] args) {
final int s = 40;
final GameOfLife[][] biosphere = new GameOfLife[s][s];
final JPanel gui = new JPanel(new GridLayout(s, s, 2, 2));
for (int ii = 0; ii < s; ii++) {
for (int jj = 0; jj < s; jj++) {
GameOfLife cell = new GameOfLife(ii, jj);
cell.setPreferredSize(new Dimension(10, 10));
gui.add(cell);
biosphere[ii][jj] = cell;
}
}
ActionListener al = (ActionEvent ae) -> {
boolean[][] living = new boolean[s][s];
for (int ii = 0; ii < s; ii++) {
for (int jj = 0; jj < s; jj++) {
int top = (jj > 0 ? jj - 1 : s - 1);
int btm = (jj < s - 1 ? jj + 1 : 0);
int lft = (ii > 0 ? ii - 1 : s - 1);
int rgt = (ii < s - 1 ? ii + 1 : 0);
int neighbors = 0;
if (biosphere[ii][top].isLiving()) {
neighbors++;
}
if (biosphere[ii][btm].isLiving()) {
neighbors++;
}
if (biosphere[lft][top].isLiving()) {
neighbors++;
}
if (biosphere[lft][btm].isLiving()) {
neighbors++;
}
if (biosphere[lft][jj].isLiving()) {
neighbors++;
}
if (biosphere[rgt][jj].isLiving()) {
neighbors++;
}
if (biosphere[rgt][top].isLiving()) {
neighbors++;
}
if (biosphere[rgt][btm].isLiving()) {
neighbors++;
}
living[ii][jj] = biosphere[ii][jj].isAlive(neighbors);
}
}
for (int ii = 0; ii < s; ii++) {
for (int jj = 0; jj < s; jj++) {
biosphere[ii][jj].setAlive(living[ii][jj]);
}
}
gui.repaint();
};
Timer timer = new Timer(50, al);
timer.start();
JOptionPane.showMessageDialog(null, gui);
timer.stop();
}
}