Domanda

Ho scritto una semplice implementazione del gioco della vita con le applet Java. Ecco il codice sorgente per il Applet e il Modello.

Quando faccio clic sul pulsante per ottenere la successiva iterazione, queste eccezioni vengono lanciate.

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)
È stato utile?

Soluzione

Prova questo codice che aggiunge il pulsante una volta anziché ogni chiamata a paint(). Si noti che questa sorgente lancia ancora Aioobe se si fa clic fuori dalla griglia (e non sul pulsante), ma sembra un errore logico di base che si dovrebbe indagare una volta che il pulsante è stato corretto.

// <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");
  }
}

Ulteriori suggerimenti

  1. Non usare i componenti AWT in questo millennio, usa invece Swing.
  2. Non usare null layout. L'area renderizzata personalizzata non ne ha bisogno e il pulsante dovrebbe essere dimensionato e posizionato utilizzando un gestore di layout (con forse un bordo per strapparlo).

Aggiornare

Questo codice implementa il secondo suggerimento dall'alto "usa layout", ma lo lascia come un esercizio per il lettore aggiornare i componenti a qualcosa che potrebbe essere usato in questo millennio (cioè swing).

La fonte sotto i "trucchi" in un certo senso mostrare la GUI alle sue dimensioni naturali. Questo è difficile da fare in un'applet, poiché le dimensioni sono impostate da HTML. Ma metti la GUI in un'oscillazione JOptionPane E può essere messo sullo schermo, imballato alle sue dimensioni naturali, in un paio di righe di codice.

Ecco come appare alla "dimensione naturale" (ho giocato con alcuni numeri, per rendere la GUI più piccola).

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");
    }
}

Altre questioni

  1. Il codice sposta il dipinto personalizzato su a Panel. Questo aggira i problemi comuni della pittura direttamente a un contenitore di alto livello. Inoltre, consente un facile riutilizzo della stessa GUI in diversi contenitori. In questo caso sia un'applet, sia per l '"applicazione" (che di solito verrebbe inserita in un frame), a JOptionPane. Ora è ciò che è noto come "applet/applicazione ibrido" (più facile per il test).
  2. Il componente dipinto personalizzato Ecosystem (scrolla le spalle) Informa il layout di quale dimensione preferisce essere. Questo ci aiuta a evitare di dover impostare le dimensioni o i limiti di qualsiasi cosa.
  3. Il pulsante sarà esattamente grande come deve essere.

Altri suggerimenti

Innanzitutto, penso che tu stia leggendo l'eccezione, traccia sbagliata. L'eccezione è un ArrayIndexOutOfBoundsException e si verifica sulla riga 63 di GameOfLifeApplet.java. Che la tua app è un'applet o che l'eccezione si verifica sul thread AWT-EventQueue-1 Non ha alcuna rilevanza.

La causa principale è che non hai sincronizzato correttamente il modello e l'idea di View di quante celle ci sono nella griglia. Almeno, dovresti considerare di verificare che l'utente abbia effettivamente fatto clic all'interno della griglia prima di accedere all'elemento array.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top