Search code examples
javaswinganimationjpaneljcomponent

Can anyone offer some help/advice on how to correctly create an animation?


So I'm creating the game of life in java but I'm having trouble with the animation part. I have the cells drawn on a JComponent which is displaying as it should but I have a start button that should start the whole animation process but nothing happens when I click the start button.

From the tutorials I have watched and research I've done I know you must have an ActionListener that listens on the button and an actionPerformed() method that holds the code to start the animation. this being said my code is as follows:

import javax.swing.JFrame;
import java.util.Random;
import java.util.Arrays;
import javax.swing.JComponent;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Color;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.*;
import javax.swing.*;
import java.awt.BorderLayout;

public class GameOfLife extends JFrame {

  int PIXELSIZE = 10;
  int ROW = 75;
  int COLUMN = 75;
  Random random = new Random();
  JButton start;

  public GameOfLife() {
    JPanel panel = new JPanel();
    DrawCells cellsComp = new DrawCells();
    DrawCells buttonListener = new DrawCells();
    start = new JButton("Start");
    start.addActionListener(cellsComp);
    panel.setLayout(new BorderLayout());
    start.setToolTipText("Click to start game of life");
    this.setTitle("Game Of Life: Java");
    this.setSize(PIXELSIZE * ROW, PIXELSIZE * COLUMN);
    this.setLocationRelativeTo(null);
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    panel.add(start, BorderLayout.SOUTH);
    panel.add(cellsComp, BorderLayout.CENTER);
    this.add(panel);
    this.setVisible(true);
  }

  private class DrawCells extends JComponent implements ActionListener {
    int x;
    int y;
    int grid[][] = new int[ROW][COLUMN];
    int updatedGrid[][] = new int[ROW][COLUMN];

    public void createGrid() {
      for (int i = 0; i < grid.length; i++) {
        for (int j = 0; j < grid[i].length; j++) {
          grid[i][j] = random.nextInt(2);
        }
      }
    }

    public void applyRules() {
      for (int i = 1; i < ROW - 1; i++) {
        for (int j = 1; j < COLUMN - 1; j++) {
          int neighboursCount = 0; // used to count the neighbours of each cell

          neighboursCount += grid[i-1][j-1]; // top left
          neighboursCount += grid[i][j-1]; // top center
          neighboursCount += grid[i+1][j-1]; // top right

          neighboursCount += grid[i-1][j]; // middle left
          neighboursCount += grid[i+1][j]; // middle right

          neighboursCount += grid[i-1][j+1]; // top left
          neighboursCount += grid[i][j+1]; // top center
          neighboursCount += grid[i+1][j+1]; // top right

          // Game of Life rules:

          // Alive cells:
          if (grid[i][j] == 1) {
            // rule 1 any live cell with fewer than two live neighboours dies, as if by underpopulation
            if (neighboursCount < 2) {
              updatedGrid[i][j] = 0;
            }
            // rule 2 any live cell with two or three live neighbours, lives on to the generation
            else if (neighboursCount == 2 || neighboursCount == 3) {
              updatedGrid[i][j] = 1;
            }
            // rule 3 any live cell with more than three live neighbours dies, as if by overpopulation
            else if (neighboursCount > 3 && neighboursCount <= 8) {
              updatedGrid[i][j] = 0;
            }
            else {
              updatedGrid[i][j] = 0;
            }
          }
          // Dead cells
          else if (grid[i][j] == 0) {
            // rule 4 any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction
            if (neighboursCount == 3) {
              updatedGrid[i][j] = 1;
            }
            else {
              updatedGrid[i][j] = 0;
            }
          }
        }
      }
      for (int i = 0; i < ROW; i++) {
        for (int j = 0; j < COLUMN; j++) {
          grid[i][j] = updatedGrid[i][j];
        }
      }
    }

    public void paintComponent(Graphics g) {
      super.paintComponent(g);
      createGrid();
      Graphics2D g2 = (Graphics2D) g;
      for (int i = 0; i < ROW; i++) {
        for (int j = 0; j < COLUMN; j++) {
          if (grid[i][j] == 1) {
            g2.setColor(Color.RED);
            g2.fillRect(i * PIXELSIZE, j * PIXELSIZE, PIXELSIZE, PIXELSIZE);
          }
        }
      }
    }

    public void actionPerformed(ActionEvent e) {
      if (e.getSource() == start) {
        applyRules();
        repaint();
        thread.sleep();
      }
    }
  }

  public static void main(String[] args) {
    GameOfLife gol = new GameOfLife();
  }
}

What I'm actually asking for is a clear explanation on how to animate some code or a nice point in the right direction in what I'm missing/need to add in order to get my code animated.

Thanks in advance for the help :D


Solution

  • I'm going to pretend that your gui works. In your action performed you need to start the animation. A common way to do an animation in swing is to use a Timer.

    Timer animation;
    public void actionPerformed(ActionEvent e) {
      if (e.getSource() == start && animation == null) {
          createGrid();  //remove this from paint component.
          int delay = 1000; //milliseconds
          ActionListener taskPerformer = new ActionListener() {
              public void actionPerformed(ActionEvent evt) {
                  applyRules();
                  repaint();
              }
          };
          animation = new Timer(delay, taskPerformer);
          animation.start();
      }
    }
    

    I used the anonymous class taskPerformer because that is from the example in the javadoc link provided.