0

我用java小程序编写了一个简单的生命游戏实现。这是小程序模型的源代码。

当我单击按钮进行下一次迭代时,会抛出这些异常。

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)
4

2 回答 2

3

试试这个添加按钮一次的代码,而不是每次调用paint(). 请注意,如果您在网格外部(而不是在按钮上)单击,此源仍会引发 AIOOBE,但这似乎是一个基本逻辑错误,您应该在修复按钮后进行调查。

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

更多提示

  1. 在这个千年不要使用 AWT 组件,而是使用 Swing。
  2. 不要使用null布局。自定义渲染区域不需要它,并且应该使用布局管理器调整按钮的大小和位置(可能有一个边框来填充它)。

更新

此代码实现了上面“使用布局”的第二条建议,但将其作为练习让读者将组件更新为可能在这个千年中使用的东西(即 Swing)。

从某种意义上说,“作弊”下方的源代码以自然大小显示了 GUI。这在小程序中很难做到,因为大小是由 HTML 设置的。但是将 GUI 放入基于 Swing 的程序JOptionPane中,只需几行代码,就可以将其放在屏幕上,压缩成自然大小。

这是它在“自然大小”下的样子(我玩了一些数字,以使 GUI 更小)。

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

其他事项

  1. 该代码将自定义绘画移动到Panel. 这解决了直接绘制到顶级容器的常见问题。它还允许在不同的容器中轻松地重复使用相同的 GUI。在这种情况下,一个小程序和“应用程序”(通常放在一个框架中),一个JOptionPane. 它现在被称为“混合小程序/应用程序”(更易于测试)。
  2. 自定义绘制组件Ecosystem(耸肩)通知布局它喜欢的大小。这有助于我们避免需要设置任何东西的大小或边界。
  3. 该按钮将与需要的一样大。
于 2012-02-20T06:42:18.107 回答
2

首先,我认为您正在错误地阅读异常跟踪。例外是ArrayIndexOutOfBoundsException并且发生在 的第 63 行GameOfLifeApplet.java。您的应用程序是一个小程序或线程上发生异常AWT-EventQueue-1完全没有相关性。

根本原因是您没有正确同步模型和视图对网格中有多少单元格的想法。至少,您应该考虑在访问数组元素之前检查用户是否实际在网格内单击。

于 2012-02-20T06:43:37.437 回答