Null pointer when accessing variable from another class

Okay, I’m having another learning experience here. I don’t know what’s going wrong with this.

I’m experimenting with how classes work together, and how to pass data between them. When I run this code, I get a NullPointerException in the Tile constructor when it tries to initialize cSize.

I tried changing the Tile constructor so that it will take colW and rowH as parameters like this:

  Tile(float x, float y, String id, int colW, int rowH) {
// ...
    cSize = new PVector(colW, rowH);
// ...

and change the setTiles() function in Board so that it passes them like this:

tiles[n] = new Tile(grid.centers[c][r].x, 
            grid.centers[c][r].y, 
            str(n + 1), grid.colW, grid.rowH);

then the program runs okay.

From what I can tell, by the time the Tile constructor is reached, those values have already been initialized in the Grid class, they print the correct values in a println statement in the setTiles() function placed before the loop. So I don’t see why the Tile constructor can’t access them directly without needing to be passed as parameters.

Can someone please tell me what I’m missing?

Board board;
void setup() {
  size(500, 500);
  board = new Board();
}
void draw() {
  background(0, 0, 255);
  board.display();
}
class Board {
  Grid grid;
  Tile[] tiles;
  Board() {
    grid = new Grid();
    tiles = new Tile[grid.nCols * grid.nRows];
    setTiles();
  }
  void setTiles() {
    
println(grid.colW, grid.rowH);

    for (int n = 0; n < tiles.length - 1; n = n) {
      for (int r = 0; r < grid.centers.length; r++) {
        for (int c = 0; c < grid.centers.length; c++) {
          tiles[n] = new Tile(grid.centers[c][r].x, 
            grid.centers[c][r].y, 
            str(n + 1));
          n++;
        }
      }
    }
  }
  void display() {
    grid.display();
    for (int n = 0; n < tiles.length - 1; n++) {
      tiles[n].drawTile();
    }
  }
}
class Grid {
  // variables related to basic structure
  int nCols, nRows, colW, rowH;
  PVector cellSize;
  PVector[][] lines;
  PVector[][] centers;

  // variables related to translation
  PVector origin, xyDims, xyCenter;
  Grid() {
    nCols = 3;
    nRows = 3;
    colW = 100;
    rowH = 100;
    cellSize = new PVector(colW, rowH);
    lines = new PVector[nCols + 1][nRows + 1];
    centers = new PVector[nCols][nRows];
    xyDims = new PVector(colW * nCols, rowH * nRows);
    xyCenter = PVector.div(xyDims, 2);
    origin = new PVector(width/2 - xyCenter.x, height/2 - xyCenter.y);

    makeGrid();
    applyTranslation(origin);
  }
  void makeGrid() {
    for (int c = 0; c < nCols + 1; c++) {
      for (int r = 0; r < nRows + 1; r++) {
        lines[c][r] = new PVector(c * cellSize.x, r * cellSize.y);
      }
    }
    for (int c = 0; c < nCols; c++) {
      for (int r = 0; r < nRows; r++) {
        centers[c][r] = new PVector(c * cellSize.x + cellSize.x / 2, 
          r * cellSize.y + cellSize.y / 2);
      }
    }
  }
  void applyTranslation(PVector newOrigin) {
    xyCenter.add(newOrigin);
    for (int c = 0; c < lines.length; c++) {
      for (int r = 0; r < lines.length; r++) {
        lines[c][r].add(newOrigin);
      }
    }
    for (int c = 0; c < centers.length; c++) {
      for (int r = 0; r < centers.length; r++) {
        centers[c][r].add(newOrigin);
      }
    }
  }
  //void setActive(PVector activeCell) {
  //  for (int c = 0; c < centers.length; c++) {
  //    for (int r = 0; r < centers.length; r++) {
  //      if (board.grid.centers[c][r].x == activeCell.x && 
  //        centers[c][r].y == activeCell.y) {
  //        centers[c][r].highlightOn = true;
  //      } else {
  //        centers[c][r].highlightOn = false;
  //      }
  //    }
  //  }
  //}
  void display() {
    for (int c = 0; c < nCols; c++) {
      for (int r = 0; r < nRows; r++) {
        noStroke();
        fill(0, 0, 0, 127);
        rectMode(CENTER);
        rect(centers[c][r].x, centers[c][r].y, cellSize.x, cellSize.y);
      }
    }
    for (int c = 0; c < nCols + 1; c++) {
      for (int r = 0; r < nRows + 1; r++) {
        stroke(0, 0, 255, 127);
        line(lines[c][r].x, 0, lines[c][r].x, height);
        line(0, lines[c][r].y, width, lines[c][r].y);
      }
    }
  }
}
class Tile {
  String id;
  PVector loc, activeCell, cSize;
  boolean locked, highlightOn;
  Tile() {
    loc = new PVector();
    activeCell = new PVector();
    cSize = new PVector(board.grid.colW, board.grid.rowH);
    locked = false;
    highlightOn = false;
  }
  Tile(float x, float y, String id) {
    loc = new PVector();
    loc.x = x;
    loc.y = y;
    activeCell = new PVector();
    cSize = new PVector(board.grid.colW, board.grid.rowH);
    locked = false;
    highlightOn = false;
    this.id = id;
  }
  void snapToCenter() {
    loc = activeCell;
  }
  void drawTile() {
    checkOverTile(); 
    loc.x = constrain(loc.x, board.grid.xyCenter.x - board.grid.xyDims.x/2-1 + cSize.x/2, 
      board.grid.xyCenter.x + board.grid.xyDims.x/2 - cSize.x/2);
    loc.y = constrain(loc.y, board.grid.xyCenter.y - board.grid.xyDims.y/2-1 + cSize.y/2, 
      board.grid.xyCenter.y + board.grid.xyDims.y/2 - cSize.y/2);
    stroke(255);
    if (locked) {
      fill(255, 92, 92, 127);
    } else {
      fill(255, 255, 255, 127);
    }
    rectMode(CENTER);
    rect(loc.x, loc.y, cSize.x, cSize.y);
    textAlign(CENTER, CENTER);
    textSize(36);
    text(id, loc.x, loc.y);
  }
  void checkOverTile() {
    checkOverColCenters();
    checkOverRowCenters();
    //board.grid.setActive(activeCell);
  }
  void checkOverColCenters() {
    for (int i = 0; i < board.grid.centers.length; i++) {
      if (loc.x >= board.grid.centers[i][0].x - board.grid.colW/2 && 
        loc.x <= board.grid.centers[i][0].x + board.grid.colW/2) {
        activeCell.x = board.grid.centers[i][0].x;
      }
    }
  }
  void checkOverRowCenters() {
    for (int i = 0; i < board.grid.centers.length; i++) {
      if (loc.y >= board.grid.centers[0][i].y - board.grid.rowH/2 && 
        loc.y <= board.grid.centers[0][i].y + board.grid.rowH/2) {
        activeCell.y = board.grid.centers[0][i].y;
      }
    }
  }
}
1 Like

Hi dddown,

Hi think it is because the board object does not exist yet.
You are actually building it so even if parts of it is build (the grid for that matter), the null pointer exception is on the board.

1 Like
  • At statement 1, you’re attempting to create a new Board object, and then assign it to the global variable board.
  • And inside Board’s constructor at statement 2, it attempts to instantiate a Tile object.
  • And then inside Tile’s constructor at statement 3, it attempts to read the global variable board.
  • However, the global variable board is still waiting for the Board’s constructor to fully complete in order to finally store the instance created by the new operator!
  • It means that board is still null at statement 3!
2 Likes

Oh, right. That makes sense. Thanks! I really appreciate the help.