Java俄罗斯方块游戏中的空指针异常

Java俄罗斯方块游戏中的空指针异常,java,tetris,Java,Tetris,您好,我对java比较陌生,我正在编写一个俄罗斯方块风格的程序。我目前收到一个nullpointerException,如下所示: Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException at tetris2.GameBoard.drawSquare(Tetris.java:359) at tetris2.GameBoard.paint(Tetris.java:250) 我已经标出了异常所指向的行。我理解Nul

您好,我对java比较陌生,我正在编写一个俄罗斯方块风格的程序。我目前收到一个nullpointerException,如下所示:

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at tetris2.GameBoard.drawSquare(Tetris.java:359)
at tetris2.GameBoard.paint(Tetris.java:250)
我已经标出了异常所指向的行。我理解NulPointerException是什么,但如果不创建,我就无法计算出引用了什么类 这是我的密码:

public class Tetris {

public static void createGUI()
{
    final JFrame frame = new JFrame("159.235 - A2");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    final JPanel contentPane = new JPanel(new BorderLayout());
    frame.setContentPane(contentPane);

    final GameBoard gameBoard = new GameBoard();
    contentPane.add(gameBoard, BorderLayout.CENTER);

    frame.addKeyListener(new KeyListener() {

        @Override
        public void keyTyped(KeyEvent e)
        {
            gameBoard.keyTyped(e);

        }

        @Override
        public void keyReleased(KeyEvent e)
        {
            gameBoard.keyReleased(e);
        }

        @Override
        public void keyPressed(KeyEvent e)
        {
            switch (e.getKeyChar()) {
            case KeyEvent.VK_ESCAPE:
                gameBoard.pauseGame();
                System.exit(0);
                break;
            default:
                gameBoard.keyPressed(e);
            }
        }
    });

    frame.pack();
    frame.setResizable(false);
    frame.setVisible(true);

    // Note: you might want to add a button to start, pause, or resume the
    // game instead of automatically starting it here
    gameBoard.startGame();
}

public static void main(String[] args)
{
    SwingUtilities.invokeLater(new Runnable() {

        @Override
        public void run()
        {
            createGUI();
        }
    });
}
}


class GameBoard extends JPanel implements KeyListener {

private static final long serialVersionUID = 1L;

// the number of rows and columns on the game board
public static final int NUM_COLS = 10;
public static final int NUM_ROWS = 20;

// the size of each cell in pixels
public static final int CELL_SIZE = 20;

// the game board size in pixels
public  final int GAME_FIELD_WIDTH = NUM_COLS * CELL_SIZE;
public  final int GAME_FIELD_HEIGHT = NUM_ROWS * CELL_SIZE;

// the interval between game state updates
private int m_updateInterval = 500;







// the game timer initiates an update to the game state
private final Timer m_gameTimer = new Timer(m_updateInterval,
        new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e)
            {
                if(isFallingFinished) {
                    isFallingFinished = false;
                    newPiece();;
                } else {
                    oneLineDown();
                }
            }
        });

// the game board, with [0][0] being the bottom left cell
//private final Block[][] m_cells = new Block[NUM_ROWS][NUM_COLS];

// the currently active shape
private Shapes m_currentShape = new Shapes();



public int curX = 0;
public int curY = 0;
tetrisPieces[] board;
boolean isFallingFinished = false;
boolean isStarted = false;
boolean isPaused = false;   
int numLinesRemoved = 0;

// ////////////////////////////////////////////////////////////////////
public GameBoard()
{
    setMinimumSize(new Dimension(GAME_FIELD_WIDTH + 1, GAME_FIELD_HEIGHT));
    setPreferredSize(new Dimension(GAME_FIELD_WIDTH + 1, GAME_FIELD_HEIGHT));
    setOpaque(true);

    // set-up the timer for the render loop
    m_gameTimer.setInitialDelay(m_updateInterval);
    m_gameTimer.setRepeats(true);
    board = new tetrisPieces[GAME_FIELD_WIDTH * GAME_FIELD_HEIGHT];
    clearBoard();
    newPiece();



}



int squareWidth() { return (int) getSize().getWidth() / GAME_FIELD_WIDTH;}
int squareHeight() { return (int) getSize().getHeight() / GAME_FIELD_HEIGHT; }
tetrisPieces shapeAt(int x, int y) { return board[(y * GAME_FIELD_WIDTH) + x]; }

@Override
public void keyPressed(KeyEvent e)
{
    switch (e.getKeyCode()) {
    case KeyEvent.VK_UP:
        m_currentShape.rotateShape();
        break;
    case KeyEvent.VK_DOWN: // move down
        oneLineDown();
        break;
    case KeyEvent.VK_LEFT: // move left
        tryMove(m_currentShape, curX - 1, curY);
        break;
    case KeyEvent.VK_RIGHT: // move right
        tryMove(m_currentShape, curX + 1, curY);
        break;
    case KeyEvent.VK_SPACE: // toggle pause / resume
        if (m_gameTimer.isRunning()) pauseGame();
        else startGame();
        break;
    }
}

@Override
public void keyReleased(KeyEvent e)
{
    switch (e.getKeyCode()) {
    case KeyEvent.VK_DOWN: // disable down key
        break;
    case KeyEvent.VK_LEFT: // disable left key
        break;
    case KeyEvent.VK_RIGHT: // disable right key
        break;
    }
}

@Override
public void keyTyped(KeyEvent e)
{}

public void startGame()
{
    if(isPaused)
        return;

    isStarted = true;
    isFallingFinished = false;
    numLinesRemoved = 0;
    clearBoard();

    board = new tetrisPieces[getWidth() * getHeight()];
    newPiece();
    m_gameTimer.start();
}

public void pauseGame()
{
    if (!isStarted)
            return;

        isPaused = !isPaused;
        if(isPaused) {
            m_gameTimer.stop();
            //statusbar.setText("paused");
        } else {
            m_gameTimer.start();
            //statusbar.setText(String.valueOf(numLinesRemoved));
            }
            repaint();
}


public void paint(Graphics g)
{
    super.paint(g);

    Dimension size = getSize();
    int boardTop = (int) size.getHeight() - GAME_FIELD_HEIGHT * squareHeight();

    for (int i = 0; i < GAME_FIELD_HEIGHT; ++i) {
        for (int j = 0; j < GAME_FIELD_WIDTH; ++j) {
            tetrisPieces shapes = shapeAt(j, GAME_FIELD_HEIGHT - i -1);
            if (shapes != tetrisPieces.noPiece)
                drawSquare(g, 0 + j * squareWidth(),boardTop + i * squareHeight(), shapes); 
               //**This line above**
        }
    }

    if(m_currentShape.getShape() != tetrisPieces.noPiece) {
        for(int i = 0; i <4; ++i) {
            int x = curX + + m_currentShape.x(i);
            int y = curY - m_currentShape.y(i);
            drawSquare(g, 0 + x * squareWidth(), 
                          boardTop + (GAME_FIELD_HEIGHT - y - 1) * squareHeight(),
                          m_currentShape.getShape());
        }
    }
}




private void oneLineDown()
{
    if(!tryMove(m_currentShape, curX, curY - 1))
        pieceDropped();
}

private void clearBoard() {
    for (int i = 0; i <GAME_FIELD_HEIGHT * GAME_FIELD_WIDTH; ++i)
        board[i] = tetrisPieces.noPiece;
}


private boolean tryMove(Shapes newPiece, int newX, int newY) {
    for(int i = 0; i< 4; ++i) {
        int x = newX + newPiece.x(i);
        int y = newY - newPiece.y(i);
        if( x < 0 || x >= GAME_FIELD_WIDTH || y < 0 || y >= GAME_FIELD_HEIGHT)
            return false;
        if(shapeAt(x, y) != tetrisPieces.noPiece)
            return false;
    }

    m_currentShape = newPiece;
    curX = newX;
    curY = newY;
    repaint();
    return true;
}

private void pieceDropped() {
    for (int i =0; i < 4; ++i) {
        int x = curX + m_currentShape.x(i);
        int y = curY - m_currentShape.y(i);
        board[(y * GAME_FIELD_WIDTH) + x] = m_currentShape.getShape();
    }

    removeFullLines();

    if(!isFallingFinished)
        newPiece();
}

private void removeFullLines() {

    int numFullLines = 0;

    for(int i = GAME_FIELD_HEIGHT -1; i >= 0; --i) {
        boolean lineIsFull = true;

        for(int j = 0; j < GAME_FIELD_WIDTH; ++j) {
            if(shapeAt(j, i) == tetrisPieces.noPiece) {
                lineIsFull = false;
                break;
            }
        }
        if(lineIsFull) {
            ++numFullLines;
            for(int k = i; k < GAME_FIELD_HEIGHT - 1; ++k) {
                for(int j = 0; j < GAME_FIELD_WIDTH; ++j)
                    board[(k* GAME_FIELD_WIDTH) + j] = shapeAt(j, k + 1);
            }
         }
    }
}

private void newPiece() {
     m_currentShape.setRandomShape();
    curX = GAME_FIELD_WIDTH / 2 + 1;
    curY = GAME_FIELD_HEIGHT - 1 + m_currentShape.minY();

    if(!tryMove(m_currentShape, curX, curY)) {
        m_currentShape.setShape(tetrisPieces.noPiece);
        m_gameTimer.stop();
        isStarted = false;

    }
}



private void drawSquare(Graphics g, int x, int y, tetrisPieces shape)
{
    Color colors[] = { new Color(0, 0, 0), new Color(204, 102, 102),
                          new Color(102, 204, 102), new Color(102, 102, 204), 
                          new Color(204, 204, 102), new Color(204, 102, 204), 
                          new Color(102, 204, 204), new Color(218, 170, 0)
        };

Color color = colors[shape.ordinal()]; //**This line here**

g.setColor(color);
g.fillRect(x + 1, y + 1, squareWidth() - 2, squareHeight() - 2);

g.setColor(color.brighter());
g.drawLine(x, y + squareHeight() - 1, x, y);
g.drawLine(x, y, x + squareWidth() - 1, y);

g.setColor(color.darker());
        g.drawLine(x + 1, y + squareHeight() - 1,
                    x + squareWidth() - 1, y + squareHeight() - 1);
        g.drawLine(x + squareWidth() - 1, y + squareHeight() - 1,
                        x + squareWidth() - 1, y + 1);
}
}
形状类:

public class Shapes {

enum tetrisPieces { noPiece, ZShape, SSHape, TShape, LShape, SquareShape,
                    ReverseLShape}

private tetrisPieces pieceShape;
private int coords[][];
private int coordsTable[][][];


public Shapes() {

    coords = new int[4][2];
    setShape(tetrisPieces.noPiece);

}

public void setShape(tetrisPieces shape) {

    coordsTable = new int[][][] {
        { { 0, 0 },   { 0, 0 },   { 0, 0 },   { 0, 0 } },
        { { 0, -1 },  { 0, 0 },   { -1, 0 },  { -1, 1 } },
        { { 0, -1 },  { 0, 0 },   { 1, 0 },   { 1, 1 } },
        { { 0, -1 },  { 0, 0 },   { 0, 1 },   { 0, 2 } },
        { { -1, 0 },  { 0, 0 },   { 1, 0 },   { 0, 1 } },
        { { 0, 0 },   { 1, 0 },   { 0, 1 },   { 1, 1 } },
        { { -1, -1 }, { 0, -1 },  { 0, 0 },   { 0, 1 } },
        { { 1, -1 },  { 0, -1 },  { 0, 0 },   { 0, 1 } }
    };

    for(int i = 0; i < 4; i++) {
        for(int j = 0; j<2; ++j) {
            coords[i][j] = coordsTable[shape.ordinal()][i][j];
        }
    }
    pieceShape = shape;

}

private void setX(int index, int x) { coords[index][0] = x; }
private void setY(int index, int y) { coords[index][1] = y; }
public int x(int index) { return coords[index][0]; }
public int y(int index) { return coords[index][1]; }
public tetrisPieces getShape() { return pieceShape; }

public void setRandomShape()
{
    Random r = new Random();
    int x = Math.abs(r.nextInt()) % 6 + 1;
    tetrisPieces[] values = tetrisPieces.values();
    setShape(values[x]);
}

public int minX() {
    int m =coords[0][0];
    for (int i = 0; i < 4; i++) {
        m = Math.min(m, coords[i][0]);
    }
    return m;
}

public int minY() {
    int m = coords[0][1];
    for (int i =0; i <4; i++) {
        m = Math.min(m, coords[i][1]);
    }
    return m;
}



public Shapes rotateShape() {
    if(pieceShape == tetrisPieces.SquareShape)
        return this;

    Shapes result = new Shapes();
    result.pieceShape = pieceShape;

    for(int i = 0; i <4; ++i) {
        result.setX(i, -y(i));
        result.setY(i, x(i));
    }
    return result;
   }
}
在这条直线上唯一可以为空的是形状,因为颜色在前面的直线上是反义的

形状从何而来?排队

tetrisPieces shapes = shapeAt(j, GAME_FIELD_HEIGHT - i -1);
shapeAt的回报是什么

tetrisPieces shapeAt(int x, int y) { return board[(y * GAME_FIELD_WIDTH) + x]; }
在执行该行之前,您是否初始化了线路板阵列的元素?可能不会。调试器将确认这一点。可能有问题的是这些线路:

clearBoard();
board = new tetrisPieces[getWidth() * getHeight()];

第一行初始化电路板阵列的每个元素,但第二行丢弃已初始化的电路板阵列,并用新的空电路板阵列替换它。

哪一行是359?在代码中的这一行前面放一个断点,启动调试器并检查变量的状态……那么形状和四分体在哪里呢?考虑提供一个说明你的问题的方法。这将减少混乱和更好的响应。您可能希望有一个通读版本,它将使人们更容易阅读您的代码和您的阅读others@MadProgrammer好的,对不起,我忘了上我的课了。我将通读这些惯例,谢谢,尽管这都是基于讲师给我们的框架代码。好的,谢谢你删除了这行代码,解决了这个问题,再次非常感谢!
clearBoard();
board = new tetrisPieces[getWidth() * getHeight()];