Pregunta

Escribí una implementación simple del juego de la vida con los applets de Java. Aquí está el código fuente para el Applet y el Modelo.

Cuando hago clic en el botón para obtener la siguiente iteración, estas excepciones se lanzan.

Z:\GameOfLife>appletviewer driver.html
Exception in thread "AWT-EventQueue-1" java.lang.ArrayIndexOutOfBoundsException:
 65
        at GameOfLifeApplet.mouseClicked(GameOfLifeApplet.java:63)
        at java.awt.Component.processMouseEvent(Component.java:6219)
        at java.awt.Component.processEvent(Component.java:5981)
        at java.awt.Container.processEvent(Container.java:2041)
        at java.awt.Component.dispatchEventImpl(Component.java:4583)
        at java.awt.Container.dispatchEventImpl(Container.java:2099)
        at java.awt.Component.dispatchEvent(Component.java:4413)
        at java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
        at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThre    ad.java:269)
        at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThre    ad.java:174)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
        at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
¿Fue útil?

Solución

Pruebe este código que agrega el botón una vez en lugar de cada llamada a paint(). Tenga en cuenta que esta fuente todavía arroja aioobe si hace clic fuera de la cuadrícula (y no en el botón), pero eso parece un error lógico básico que debe investigar una vez que se solucione el botón.

// <applet code='GameOfLifeApplet' width=580 height=650></applet>
import java.awt.*;
import java.awt.event.*;
import java.applet.*;

public class GameOfLifeApplet extends Applet implements MouseListener,ActionListener
{
  //the x and y coordinates to get the location of the clicked points
  private int xCo, yCo;
  private int diff_x, diff_y;
  private GameOfLife game = new GameOfLife();
  private Button nextButton = null;
  public void init()
  {
    setLayout(null);
    nextButton = new Button("Next Stage");
    diff_x = diff_y = 600 / game.getGridSize();
    nextButton.setLocation(250, 575);
    nextButton.setSize(120, 30);
    // add the button once only!
    add(nextButton);
    addMouseListener(this);
  }

  private void drawEmptyGrid(Graphics g)
  {
    g.setColor(Color.white);
    g.fillRect(0,0,600,600);
    g.setColor(Color.black);
    for(int i=0;i<game.getGridSize();++i)
    {
      g.drawLine(0,i*diff_x,600,i*diff_x);
      g.drawLine(i*diff_x,0,i*diff_x,600);
    }
    g.setColor(Color.white);
  }

  public void paint(Graphics g)
  {
    drawEmptyGrid(g);
    g.setColor(Color.red);
    for(int i=0;i<game.getGridSize();++i)
    {
      for(int j=0;j<game.getGridSize();++j)
      {
        if( game.grid[i][j] )
        {
          g.fillRect(i*diff_x,j*diff_y,diff_x,diff_y);
        }
      }
    }
    g.setColor(Color.white);
  }

 // This method will be called when the mouse has been clicked.
 public void mouseClicked (MouseEvent me) {

  // Save the coordinates of the click lke this.
  xCo = me.getX();
  yCo = me.getY();

  int x_init = xCo / diff_x;
  int y_init = yCo / diff_y;

  System.out.println(x_init + "x" + y_init);
  game.grid[x_init][y_init] = true;
  //show the results of the click
  repaint();

 }

 // This is called when the mous has been pressed
 public void mousePressed (MouseEvent me) {}

 // When it has been released
 // not that a click also calls these Mouse-Pressed and Released.
 // since they are empty nothing hapens here.
 public void mouseReleased (MouseEvent me) {}

 // This is executed when the mouse enters the applet. it will only
 // be executed again when the mouse has left and then re-entered.
 public void mouseEntered (MouseEvent me) {}

 // When the Mouse leaves the applet.
 public void mouseExited (MouseEvent me) {}

 public void actionPerformed(ActionEvent evt)
  {
  // Here we will ask what component called this method
    if (evt.getSource() == nextButton)
    {
      System.out.println("I got clicked!");
      game.nextIteration();
      repaint();
    }
  }

}

class GameOfLife
{
  private final int GRID_SIZE = 64;
  public boolean [][] grid = new boolean[GRID_SIZE][GRID_SIZE];
  //default constructor
  public GameOfLife()
  {
    for(int i=0;i<GRID_SIZE;++i)
    {
      for(int j=0;j<GRID_SIZE;++j)
    {
      grid[i][j] = false;
    }
    }
  }

  public int getGridSize()
  {
    return GRID_SIZE;
  }

  public int getLiveNeighbors(int i,int j)
  {
    int neighbors = 0;
    for( int tmp_i = i-1; tmp_i <= i+1; ++tmp_i )
    {
      for( int tmp_j = j-1; tmp_j <= j+1; ++tmp_j )
      {
        if( tmp_i < 0 || tmp_i >= GRID_SIZE || tmp_j < 0 || tmp_j >= GRID_SIZE )
        {}
        else
        {
          if( grid[tmp_i][tmp_j] )
          {
            neighbors++;
          }
        }
      }
    }
    return neighbors;
  }

  public void nextIteration()
  {
    boolean [][] newGrid = new boolean[GRID_SIZE][GRID_SIZE];

    for(int i=0;i<GRID_SIZE;++i)
    {
      for(int j=0;j<GRID_SIZE;++j)
      {
        newGrid[i][j] = grid[i][j];
      }
    }

    for( int i=0;i<GRID_SIZE;++i)
    {
      for( int j=0;j<GRID_SIZE;++j)
      {
        int my_neighbors = getLiveNeighbors(i,j);
        if( !newGrid[i][j] && my_neighbors == 3)
        {
          grid[i][j] = true;
        }

        else if( newGrid[i][j] && ( my_neighbors == 2 || my_neighbors == 3 ) )
        {
          grid[i][j] = true;
        }

        else
        {
          grid[i][j] = false;
        }
      }
    }
    System.out.println("Change of assignment");
  }
}

Más consejos

  1. No use componentes AWT en este milenio, use swing en su lugar.
  2. No lo usas null diseños. El área renderizada personalizada no lo necesita, y el botón debe dimensionarse y colocarse utilizando un administrador de diseño (con un borde para rellenarlo).

Actualizar

Este código implementa la segunda sugerencia de "Usar diseños" anteriores, pero deja como un ejercicio para que el lector actualice los componentes a algo que podría usarse en este milenio (es decir, swing).

La siguiente fuente 'trampa' en cierto sentido para mostrar la GUI con su tamaño natural. Esto es difícil de hacer en un applet, ya que el tamaño es establecido por el HTML. Pero pon a la GUI en un swing basado en JOptionPane Y se puede colocar en pantalla, empaquetado en su tamaño natural, en solo un par de líneas de código.

Así es como se ve en el 'tamaño natural' (jugué con algunos números, para que la GUI sea más pequeña).

GameOfLifeApplet

// <applet code='GameOfLifeApplet' width=320 height=350></applet>
import java.awt.*;
import java.awt.event.*;
import java.applet.*;

public class GameOfLifeApplet extends Applet implements ActionListener
{
    private Button nextButton = null;
    private Ecosystem ecosystem;

    public void init()
    {
        add(getGui());
    }

    public Component getGui() {
        Panel gui = new Panel(new BorderLayout(3,3));
        ecosystem = new Ecosystem();
        gui.add(ecosystem, BorderLayout.CENTER);
        nextButton = new Button("Next Stage");

        Panel p = new Panel(new FlowLayout());
        p.add(nextButton);
        gui.add(p, BorderLayout.SOUTH);
            nextButton.addActionListener(this);
        return gui;
    }

    public void actionPerformed(ActionEvent evt)
    {
        // Here we will ask what component called this method
        if (evt.getSource() == nextButton)
        {
            System.out.println("I got clicked!");
            ecosystem.nextIteration();
            ecosystem.repaint();
        }
    }

    public static void main(String[] args) {
        GameOfLifeApplet gola = new GameOfLifeApplet();
        // quick cheat to get it on-screen (packed).
        javax.swing.JOptionPane.showMessageDialog(null,gola.getGui());
    }
}

class Ecosystem extends Panel implements MouseListener {
    private GameOfLife game = new GameOfLife();
    //the x and y coordinates to get the location of the clicked points
    private int xCo, yCo;
    private int diff_x, diff_y;
    private int size = 300;

    Ecosystem() {
        diff_x = diff_y = 600 / game.getGridSize();
        setPreferredSize(new Dimension(size,size));
        addMouseListener(this);
    }

    public void nextIteration() {
        game.nextIteration();
    }

    private void drawEmptyGrid(Graphics g)
    {
        g.setColor(Color.white);
        g.fillRect(0,0,size,size);
        g.setColor(Color.black);
        for(int i=0;i<game.getGridSize();++i)
        {
            g.drawLine(0,i*diff_x,size,i*diff_x);
            g.drawLine(i*diff_x,0,i*diff_x,size);
        }
        g.setColor(Color.white);
    }

    public void paint(Graphics g)
    {
        drawEmptyGrid(g);
        g.setColor(Color.red);
        for(int i=0;i<game.getGridSize();++i)
        {
            for(int j=0;j<game.getGridSize();++j)
            {
                if( game.grid[i][j] )
                {
                    g.fillRect(i*diff_x,j*diff_y,diff_x,diff_y);
                }
            }
        }
        g.setColor(Color.white);
    }

    // This method will be called when the mouse has been clicked.
    public void mouseClicked (MouseEvent me) {
        Point point = me.getPoint();

        // Save the coordinates of the click lke this.
        xCo = (int)point.getX();
        yCo = (int)point.getY();

        int x_init = xCo / diff_x;
        int y_init = yCo / diff_y;

        System.out.println(x_init + "x" + y_init);
        game.grid[x_init][y_init] = true;
        //show the results of the click
        repaint();
    }

    // This is called when the mous has been pressed
    public void mousePressed (MouseEvent me) {}

    // When it has been released
    // not that a click also calls these Mouse-Pressed and Released.
    // since they are empty nothing hapens here.
    public void mouseReleased (MouseEvent me) {}

    // This is executed when the mouse enters the applet. it will only
    // be executed again when the mouse has left and then re-entered.
    public void mouseEntered (MouseEvent me) {}

    // When the Mouse leaves the applet.
    public void mouseExited (MouseEvent me) {}
}

class GameOfLife
{
    private final int GRID_SIZE = 60;
    public boolean [][] grid = new boolean[GRID_SIZE][GRID_SIZE];

    //default constructor
    public GameOfLife()
    {
        for(int i=0;i<GRID_SIZE;++i)
        {
            for(int j=0;j<GRID_SIZE;++j)
            {
                grid[i][j] = false;
            }
        }
    }

    public int getGridSize()
    {
        return GRID_SIZE;
    }

    public int getLiveNeighbors(int i,int j)
    {
        int neighbors = 0;
        for( int tmp_i = i-1; tmp_i <= i+1; ++tmp_i )
        {
            for( int tmp_j = j-1; tmp_j <= j+1; ++tmp_j )
            {
                if( tmp_i < 0 || tmp_i >= GRID_SIZE || tmp_j < 0 || tmp_j >= GRID_SIZE )
                {}
                else
                {
                    if( grid[tmp_i][tmp_j] )
                    {
                    neighbors++;
                    }
                }
            }
        }
        return neighbors;
    }

    public void nextIteration()
    {
        boolean [][] newGrid = new boolean[GRID_SIZE][GRID_SIZE];

        for(int i=0;i<GRID_SIZE;++i)
        {
            for(int j=0;j<GRID_SIZE;++j)
            {
                newGrid[i][j] = grid[i][j];
            }
        }

        for( int i=0;i<GRID_SIZE;++i)
        {
            for( int j=0;j<GRID_SIZE;++j)
            {
                int my_neighbors = getLiveNeighbors(i,j);
                if( !newGrid[i][j] && my_neighbors == 3)
                {
                    grid[i][j] = true;
                }
                else if( newGrid[i][j] && ( my_neighbors == 2 || my_neighbors == 3 ) )
                {
                    grid[i][j] = true;
                }
                else
                {
                    grid[i][j] = false;
                }
            }
        }
        System.out.println("Change of assignment");
    }
}

Otros asuntos

  1. El código mueve la pintura personalizada a un Panel. Esto se acerca a los problemas comunes de pintar directamente a un recipiente de nivel superior. También permite una fácil reutilización de la misma GUI en diferentes contenedores. En este caso, tanto un applet como para la 'aplicación' (que generalmente se colocaría en un marco), un JOptionPane. Ahora es lo que se conoce como 'applet/aplicación híbrida' (más fácil para las pruebas).
  2. El componente pintado personalizado Ecosystem (encogimiento de hombros) informa el diseño de qué tamaño prefiere ser. Esto nos ayuda a evitar la necesidad de establecer el tamaño o los límites de cualquier cosa.
  3. El botón será exactamente tan grande como es necesario.

Otros consejos

Primero, creo que estás leyendo el rastro de excepción mal. La excepción es una ArrayIndexOutOfBoundsException y ocurre en la línea 63 de GameOfLifeApplet.java. Que su aplicación es un applet o que la excepción ocurre en el hilo AWT-EventQueue-1 no tiene relevancia en absoluto.

La causa raíz es que no ha sincronizado correctamente el modelo y la idea de la vista de cuántas células hay en su cuadrícula. Al menos, debe considerar verificar que el usuario realmente hizo clic dentro de la cuadrícula antes de acceder al elemento de matriz.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top