user2614974
user2614974

Reputation: 43

java.lang.NullPointerException Can't Find Source

I'm in the process of programming a java rpg game, and have reached an impass. My code currently has sprite animation, a random map generation with perlin noise and collision detection. The map is tiled base, so i'm currently trying to convert the perlin noise to tiles. The perlin functions generate a array, and im each number of that array to a tile png. This is where the problem comes: RUNTIME ERROR: Java.Lang.NullPointerException. The probleme is my compiler (netbeans) does not show me where the error occurs, but instead only gives me this error code. With a process of exclusion I managed to locate the error, which occurs at line 364. If this site doesnt support lines, it is at the loadTile() method, at "if(perlinIsland[x][y] <= 0.05)blockImg[x][y] = TILE[0];". I believe all the variables are correctly initialized, but I can't manage to find a solution. Please excuse the long code, but I included everything for the sake of information. Thanks you in advance for you help!

package java4k;

import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.*;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import java.math.*;
import java.util.*;

/**
 *
 * @author Christophe
 */
public class Main extends JFrame implements Runnable{

    public Image dbImage;
    public Graphics dbGraphics;

    //Image + Array size
    final static int listWidth = 500, listHeight = 500;


    //Move Variables
    int playerX = 320, playerY = 240, xDirection, yDirection;

    //Sprites
    BufferedImage spriteSheet;

    //Lists for sprite sheet: 1 = STILL; 2 = MOVING_1; 3 = MOVING_2
    BufferedImage[] ARCHER_NORTH = new BufferedImage[4];
    BufferedImage[] ARCHER_SOUTH = new BufferedImage[4];
    BufferedImage[] ARCHER_EAST = new BufferedImage[4];
    BufferedImage[] ARCHER_WEST = new BufferedImage[4];

    Image[] TILE = new Image[8];

    //Animation Variables
    int currentFrame = 0, framePeriod = 150;
    long frameTicker = 0l;
    Boolean still = true;
    Boolean MOVING_NORTH = false, MOVING_SOUTH = false, MOVING_EAST = false, MOVING_WEST = false;

    BufferedImage player = ARCHER_SOUTH[0];

    //World Tile Variables
    //20 X 15 = 300 tiles 
    Rectangle[][] blocks = new Rectangle[listWidth][listHeight];
    Image[][] blockImg = new Image[listWidth][listHeight];
    Image[][] blockImgTrans = new Image[listWidth][listHeight];
    Boolean[][] isSolid = new Boolean[listWidth][listHeight];
    int tileX = 0, tileY = 0;
    Random r = new Random();

    Rectangle playerRect = new Rectangle(playerX + 4,playerY+20,32,20);
    //Map Navigation
    static final byte PAN_UP = 0, PAN_DOWN  = 1, PAN_LEFT = 2, PAN_RIGHT = 3;

    //Perlin noise variables:
    Color test = new Color(0, 0, 0);
    static float[][] perlinNoise = new float[listWidth][listHeight];
    static float[][] gradiantNoise = new float[listWidth][listHeight];
    static float[][] perlinIsland = new float[listWidth][listHeight];
    static float[][] biome = new float[listWidth][listHeight];
    //Saved as png
    static BufferedImage perlinImage;



    public Main(){

        this.setTitle("JAVA4K");
        this.setSize(640,505);
        this.setResizable(false);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);

        addKeyListener(new AL());

        TILE[0] = new ImageIcon("C:/Users/Christophe/Documents/NetBeansProjects/Java4k/src/java4k/TILE_GRASS_1.png").getImage();
        TILE[1] = new ImageIcon("C:/Users/Christophe/Documents/NetBeansProjects/Java4k/src/java4k/TILE_GRASS_2.png").getImage();
        TILE[2] = new ImageIcon("C:/Users/Christophe/Documents/NetBeansProjects/Java4k/src/java4k/TILE_GRASS_3.png").getImage();
        TILE[3] = new ImageIcon("C:/Users/Christophe/Documents/NetBeansProjects/Java4k/src/java4k/TILE_WATER_1.png").getImage();
        TILE[4] = new ImageIcon("C:/Users/Christophe/Documents/NetBeansProjects/Java4k/src/java4k/TILE_TREE_1_BOT.png").getImage();
        TILE[5] = new ImageIcon("C:/Users/Christophe/Documents/NetBeansProjects/Java4k/src/java4k/TILE_TREE_1_TOP.png").getImage();
        TILE[6] = new ImageIcon("C:/Users/Christophe/Documents/NetBeansProjects/Java4k/src/java4k/TILE_TREE_2_BOT.png").getImage();
        TILE[7] = new ImageIcon("C:/Users/Christophe/Documents/NetBeansProjects/Java4k/src/java4k/TILE_TREE_2_TOP.png").getImage();

        loadTiles();

        init();
    }


    //Step 1: Generates array of random number 0 < n < 1
    public static float[][] GenerateWhiteNoise(int width, int height){

        Random r = new Random();
        Random random = new Random(r.nextInt(1000000000)); //Seed to 0 for testing
        float[][] noise = new float[width][height];

        for (int i = 0; i < width; i++)
        {
            for (int j = 0; j < height; j++)
            {
                noise[i][j] = (float)random.nextDouble() % 1;
            }
        }

        return noise;

    }

    //Step 2: Smooths out random numbers
    public static float[][] GenerateSmoothNoise(float[][] baseNoise, int octave){

        int width = baseNoise.length;
        int height = baseNoise.length;

        float[][] smoothNoise = new float[width][height];

        int samplePeriod = 1 << octave; // calculates 2 ^ k
        float sampleFrequency = 1.0f / samplePeriod;

        for (int i = 0; i < width; i++)
        {
            //calculate the horizontal sampling indices
            int sample_i0 = (i / samplePeriod) * samplePeriod;
            int sample_i1 = (sample_i0 + samplePeriod) % width; //wrap around
            float horizontal_blend = (i - sample_i0) * sampleFrequency;

            for (int j = 0; j < height; j++)
            {
                //calculate the vertical sampling indices
                int sample_j0 = (j / samplePeriod) * samplePeriod;
                int sample_j1 = (sample_j0 + samplePeriod) % height; //wrap around
                float vertical_blend = (j - sample_j0) * sampleFrequency;

                //blend the top two corners
                float top = Interpolate(baseNoise[sample_i0][sample_j0],
                    baseNoise[sample_i1][sample_j0], horizontal_blend);

                //blend the bottom two corners
                float bottom = Interpolate(baseNoise[sample_i0][sample_j1],
                    baseNoise[sample_i1][sample_j1], horizontal_blend);

                //final blend
                smoothNoise[i][j] = Interpolate(top, bottom, vertical_blend);
            }
        }

        return smoothNoise;
    }

    //Used in GeneratePerlinNoise() to derivate functions
    public static float Interpolate(float x0, float x1, float alpha)
    {
        float ft = alpha * 3.1415927f; 
        float f = (float) (1 - Math.cos(ft)) * .5f;
        return x0*(1-f) + x1*f;
    }

    //Step 3: Combines arrays together to generate final perlin noise
    public static float[][] GeneratePerlinNoise(float[][] baseNoise, int octaveCount)
    {
    int width = baseNoise.length;
    int height = baseNoise[0].length;

    float[][][] smoothNoise = new float[octaveCount][][]; //an array of 2D arrays containing

    float persistance = 0.5f;

    //generate smooth noise
    for (int i = 0; i < octaveCount; i++)
    {
        smoothNoise[i] = GenerateSmoothNoise(baseNoise, i);
    }

        float[][] perlinNoise = new float[width][height];
        float amplitude = 1.0f;
        float totalAmplitude = 0.0f;

        //blend noise together
        for (int octave = octaveCount - 1; octave >= 0; octave--)
        {
        amplitude *= persistance;
        totalAmplitude += amplitude;

        for (int i = 0; i < width; i++)
        {
            for (int j = 0; j < height; j++)
            {
                perlinNoise[i][j] += smoothNoise[octave][i][j] * amplitude;
            }
        }
        }

    //normalisation
    for (int i = 0; i < width; i++)
    {
        for (int j = 0; j < height; j++)
        {
            perlinNoise[i][j] /= totalAmplitude;
        }
    }

    return perlinNoise;
    }

    //Step 4: Generate circular gradiant: center = 0, outside = 1
    public static float[][] GenerateCircularGradiant(float[][] base, int size, int centerX, int centerY){

        base = new float[size][size];

        for (int x = 0; x < base.length; x++) {
            for (int y = 0; y < base.length; y++) {

                //Simple squaring, you can use whatever math libraries are available to you to make this more readable
                //The cool thing about squaring is that it will always give you a positive distance! (-10 * -10 = 100)
                float distanceX = (centerX - x) * (centerX - x);
                float distanceY = (centerY - y) * (centerY - y);

                float distanceToCenter = (float) Math.sqrt(distanceX + distanceY);

                //Make sure this value ends up as a float and not an integer
                //If you're not outputting this to an image, get the correct 1.0 white on the furthest edges by dividing by half the map size, in this case 64. You will get higher than 1.0 values, so clamp them!
                float mapSize = base.length/2;
                //mapSize = 500;
                distanceToCenter = distanceToCenter / mapSize;

                base[x][y] = distanceToCenter - 0.2f;

            }
        }

        return base;
    }

    //step 5: Combine perlin noise with circular gradiant to create island
    public static float[][] GenerateIsland(float[][] baseCircle, float[][] baseNoise){

        float[][] baseIsland = new float[baseNoise.length][baseNoise.length];

        for(int x = 0; x < baseNoise.length; x++){
            for(int y = 0; y < baseNoise.length; y++){
                baseIsland[x][y] = baseNoise[x][y] - baseCircle[x][y];
            }
        }

        return baseIsland;
    }

    //Method for optional paramater = float[][] biome
    public static void GreyWriteImage(float[][] data, String filename){

        float[][] temp = null;
        GreyWriteImage(data, temp, filename);
    }

    //Converts array data to png image
    public static void GreyWriteImage(float[][] data, float[][] biome, String fileName){
        //this takes and array of doubles between 0 and 1 and generates a grey scale image from them

        BufferedImage image = new BufferedImage(data.length,data[0].length, BufferedImage.TYPE_INT_RGB);

        for (int y = 0; y < data[0].length; y++)
        {
          for (int x = 0; x < data.length; x++)
          {


              if (data[x][y]>1){
                data[x][y]=1;
            }
            if (data[x][y]<0){
                data[x][y]=0;
            }


            Color col;

            //Deep Water 0 - 0.05
            if(data[x][y] <= 0.05) col = new Color(0, 0, 255);
            //Shallow Water 0.05 - 0.08
            else if(data[x][y] <= 0.08) col = new Color(100, 100, 255);
            //Beach 0.08 - 0.2
            else if(data[x][y]<=0.15) col = new Color(255, 255, 0);
            //Forest 0.2 - 0.6 + 0 0 0.7
            else if(data[x][y]<=0.6 && biome != null && (biome[x][y] < 0.6 || biome[x][y] > 0.9)){
                //Forest
                if(biome[x][y] < 0.6) col = new Color(0, 150, 0);
                //Desert
                else col = new Color(200, 200, 0);
            }
            //Plains 0.2 - 0.6
            else if(data[x][y] <= 0.6)   col = new Color(0, 255, 0);
            //Rocky Mountains 0.6 - 0.8
            else if(data[x][y] <= 0.65) col = new Color(100, 100, 100);            
            //Snowy Mountains 0.6 - 1
            else col = new Color(255, 255, 255);

            image.setRGB(x, y, col.getRGB());
          }
        }

        try {
            // retrieve image
            File outputfile = new File(fileName);
            outputfile.createNewFile();

            ImageIO.write(image, "png", outputfile);
        } catch (IOException e) {
            System.out.println("GREY WRITE IMAGE ERROR 303: " + e);
        }
    }



    //First called to store image tiles in blockImg[][] and tile rectangles in blocks[][]
    private void loadTiles(){

        //Primary Perlin Noise Generation
        perlinNoise = GenerateWhiteNoise(listWidth, listHeight);
        GreyWriteImage(perlinNoise, "perlinNoise.png");
        perlinNoise = GenerateSmoothNoise(perlinNoise, 7);
        GreyWriteImage(perlinNoise, "smoothNoise.png");
        perlinNoise = GeneratePerlinNoise(perlinNoise, 5);
        GreyWriteImage(perlinNoise, "finalPerlin.png");
        gradiantNoise = GenerateCircularGradiant(gradiantNoise, listWidth, listWidth/2 - 1, listHeight/2 - 1);
        GreyWriteImage(gradiantNoise, "gradiantNoise.png");
        perlinIsland = GenerateIsland(gradiantNoise, perlinNoise);
        GreyWriteImage(perlinIsland, "perlinIsland.png");


        //Biome Perlin Noise Generation
        biome = GenerateWhiteNoise(listWidth, listHeight);
        biome = GenerateSmoothNoise(biome, 6);
        biome = GeneratePerlinNoise(biome, 5);
        GreyWriteImage(perlinIsland, biome, "biome.png");






        for(int y = 0; y < listHeight; y++){
            for(int x = 0; x < listWidth; x++){

                //Sets boundaries: 0 < perlinIsland[x][y] < 1 
                if (perlinIsland[x][y]>1) perlinIsland[x][y]=1;
                if (perlinIsland[x][y]<0) perlinIsland[x][y]=0;

                //Deep Water 0 - 0.05
                if(perlinIsland[x][y] <= 0.05)blockImg[x][y] = TILE[0];
                //Shallow Water 0.05 - 0.08
                else if(perlinIsland[x][y] <= 0.08) blockImg[x][y] = TILE[3];
                //Beach 0.08 - 0.2
                else if(perlinIsland[x][y]<=0.15) blockImg[x][y] = TILE[4];
                //Forest 0.2 - 0.6 + 0 0 0.7
                else if(perlinIsland[x][y]<=0.6 && biome != null && (biome[x][y] < 0.6 || biome[x][y] > 0.9)){
                    //Forest
                    if(biome[x][y] < 0.6) blockImg[x][y] = TILE[5];
                    //Desert
                    else blockImg[x][y] = TILE[3];
                }
                //Plains 0.2 - 0.6
                else if(perlinIsland[x][y] <= 0.6) blockImg[x][y] = TILE[2];   
                //Rocky Mountains 0.6 - 0.8
                else if(perlinIsland[x][y] <= 0.65) blockImg[x][y] = TILE[3];            
                //Snowy Mountains 0.6 - 1
                else blockImg[x][y] = TILE[1];

                blocks[x][y] = new Rectangle(x*32, y*32, 32, 32);

            }
        }

    }


    //collision detection
    public boolean collide(Rectangle in)
    {
        if(blocks[0][0] != null){
            for (int y = (int)((playerRect.y - blocks[0][0].y) / 32)-1; y <= (int)((playerRect.y+playerRect.height - blocks[0][0].y) / 32)+1; y++){
                for (int x = (int)((playerRect.x - blocks[0][0].x) / 32)-1; x <= (int)((playerRect.x+playerRect.width - blocks[0][0].x) / 32) + 1; x++){
                    if (x >= 0 && y >= 0 && x < 32 && y < 32){
                        if (blockImg[x][y] != null)
                        {
                                if (in.intersects(blocks[x][y]) && isSolid[x][y] == true){
                                {
                                    return true;
                                }
                            }
                        }
                    }
                }
            }
        }
      return false;
    }


    //Key Listener
    public class AL extends KeyAdapter{
        public void keyPressed(KeyEvent e){

            int keyInput = e.getKeyCode();
            still = false;
            if(keyInput == e.VK_LEFT){

                navigateMap(PAN_RIGHT);
                MOVING_WEST = true;

            }if(keyInput == e.VK_RIGHT){

                navigateMap(PAN_LEFT);
                MOVING_EAST = true;

            }if(keyInput == e.VK_UP){

                navigateMap(PAN_DOWN);
                MOVING_NORTH = true;

            }if(keyInput == e.VK_DOWN){

                navigateMap(PAN_UP);
                MOVING_SOUTH = true;
            }
        }
        public void keyReleased(KeyEvent e){

            int keyInput = e.getKeyCode();
            setYDirection(0);
            setXDirection(0);

            if(keyInput == e.VK_LEFT){

                MOVING_WEST = false;
                player = ARCHER_WEST[0];

            }if(keyInput == e.VK_RIGHT){

                MOVING_EAST = false;
                player = ARCHER_EAST[0];

            }if(keyInput == e.VK_UP){

                MOVING_NORTH = false;
                player = ARCHER_NORTH[0];

            }if(keyInput == e.VK_DOWN){

                MOVING_SOUTH = false;
                player = ARCHER_SOUTH[0];
            }
            if( MOVING_SOUTH == MOVING_NORTH == MOVING_EAST == MOVING_WEST == false){
                still = true;
            }
        }
    }


    public void moveMap(){


        for(int a = 0; a < 30; a++){
                for(int b = 0; b < 30; b++){
                    if(blocks[a][b] != null){
                        blocks[a][b].x += xDirection;
                        blocks[a][b].y += yDirection;
                    }
                }
            }

        if(collide(playerRect) && blocks[0][0]!= null){
            for(int a = 0; a < 30; a++){
                for(int b = 0; b < 30; b++){
                    blocks[a][b].x -= xDirection;
                    blocks[a][b].y -= yDirection;
                }
            }
        }
    }


    public void navigateMap(byte pan){
        switch(pan){
            default:
                System.out.println("Unrecognized pan!");
                break;
            case PAN_UP:
                setYDirection(-1);
                break;
            case PAN_DOWN:
                setYDirection(+1);
                break;
            case PAN_LEFT:
                setXDirection(-1);
                break;
            case PAN_RIGHT:
                setXDirection(+1);
                break;
        }
    }


    //Animation Update
    public void update(long gameTime) {

        if (gameTime > frameTicker + framePeriod) {
            frameTicker = gameTime;
            currentFrame++;
            if (currentFrame >= 4) {
                currentFrame = 0;
            }
        }
        if(MOVING_NORTH) player = ARCHER_NORTH[currentFrame];
        if(MOVING_SOUTH) player = ARCHER_SOUTH[currentFrame];
        if(MOVING_EAST) player = ARCHER_EAST[currentFrame];
        if(MOVING_WEST) player = ARCHER_WEST[currentFrame];
    }


    public void setXDirection(int xdir){

        xDirection = xdir;
    }


    public void setYDirection(int ydir){

        yDirection = ydir;
    }


    //Method to get sprites
    public BufferedImage grabSprite(int x, int y, int width, int height){
        BufferedImage sprite = spriteSheet.getSubimage(x, y, width, height);
        return sprite;
    }


    private void init(){    
        spriteSheet = null;
        try {
            spriteSheet = loadImage("ARCHER_SPRITESHEET.png");
        } catch (IOException ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        }
        for(int i = 0; i <= 3; i++){
            ARCHER_NORTH[i] = grabSprite(i*16, 16, 16,16);
            ARCHER_SOUTH[i] = grabSprite(i*16, 0, 16, 16);
            ARCHER_EAST[i] = grabSprite(i*16, 32, 16, 16);
            ARCHER_WEST[i] = grabSprite(i*16, 48, 16, 16);
        }
    }


    public BufferedImage loadImage(String pathRelativeToThis) throws IOException{

        URL url = this.getClass().getResource(pathRelativeToThis);
        BufferedImage img = ImageIO.read(url);
        return img;
    }


    public void paint(Graphics g){

        dbImage = createImage(getWidth(), getHeight());
        dbGraphics = dbImage.getGraphics();
        paintComponent(dbGraphics);
        g.drawImage(dbImage, 0, 25, this);

    }

    public void paintComponent(Graphics g){
        requestFocus();

        /**

        //Draws tiles and rectangular boundaries for debugging
        for(int a = 200; a < 230; a++){
            for(int b = 200; b < 230; b++){
                if(blockImg[a][b] != null && blocks[a][b] != null){
                    g.drawImage(blockImg[a][b], Math.round(blocks[a][b].x), Math.round(blocks[a][b].y), 32, 32, null);
                }
            }
        }  

        //Draw player and rectangular boundary for collision detection
        g.drawImage(player, playerX, playerY, 40, 40, null);
        repaint();

        //Draws transparent tiles
        for(int a = 0; a < 20; a++){
            for(int b = 0; b < 15; b++){
                if(blockImgTrans[a][b] != null && blocks[a][b] != null){
                    g.drawImage(blockImgTrans[a][b], blocks[a][b].x, blocks[a][b].y, 32, 32, null);
                }
            }
       }


        **/
    }

    public void run(){
        try{
            while(true){
                moveMap();
                if(!still) update(System.currentTimeMillis());
                Thread.sleep(13);
            }
        }catch(Exception e){
            System.out.println("RUNTIME ERROR: " + e);
        }    
    }


    public static void main(String[] args) {
        Main main = new Main();

        //Threads
        Thread thread1 = new Thread(main);
        thread1.start();
    }
}

Upvotes: 2

Views: 6274

Answers (1)

Hovercraft Full Of Eels
Hovercraft Full Of Eels

Reputation: 285405

Your limited catch block code hampers your ability to find your nulls.

For instance, these lines of code:

  try {
     while (true) {
        moveMap();
        if (!still)
           update(System.currentTimeMillis());
        Thread.sleep(13);
     }
  } catch (Exception e) {
     System.out.println("RUNTIME ERROR: " + e);
  }

Will only print

RUNTIME ERROR: java.lang.NullPointerException

without line numbers or stack trace when this code runs into an NPE.

First off, you should not be trapping for plain Exception but rather for explicit Exceptions. Next you should use a more informative catch block, for instance one that at least prints out the stack trace via e.printStackTrace().

The block above should really be written:

public void run() {
  while (true) {
     moveMap();
     if (!still)
        update(System.currentTimeMillis());
     try {
        Thread.sleep(13);
       // only catch the explicit exception and in localized code if possible.
     } catch (InterruptedException e) {
        e.printStackTrace();
     }
  }
}

Do this, and you'll see that the NPE occurs here:

if (in.intersects(blocks[x][y]) && isSolid[x][y] == true) {

Then you can stuff code in front of that line to see which variable is causing the problem:

e.g.,

              if (blockImg[x][y] != null) {
                 System.out.println("in is null: " + (in == null));
                 System.out.println("blocks[x][y] is null: "
                       + (blocks[x][y] == null));
                 System.out.println("isSolid is null: "
                       + (isSolid == null));
                 System.out.println("isSolid[x][y] is null: "
                       + (isSolid[x][y] == null));

                 if (in.intersects(blocks[x][y]) && isSolid[x][y] == true) {
                    {
                       return true;
                    }
                 }
              }

And you'll see the problem is that isSolid[x][y] is null:

in is null: false
blocks[x][y] is null: false
isSolid is null: false
isSolid[x][y] is null: true
in is null: false
blocks[x][y] is null: false
isSolid is null: false
isSolid[x][y] is null: true
in is null: false
blocks[x][y] is null: false
isSolid is null: false
isSolid[x][y] is null: true
Exception in thread "Thread-3" java.lang.NullPointerException
    at pkg.Main.collide(Main.java:465)
    at pkg.Main.moveMap(Main.java:557)
    at pkg.Main.run(Main.java:693)
    at java.lang.Thread.run(Unknown Source)
in is null: false
blocks[x][y] is null: false
isSolid is null: false
isSolid[x][y] is null: true
in is null: false
blocks[x][y] is null: false
isSolid is null: false
isSolid[x][y] is null: true
in is null: false
blocks[x][y] is null: false
isSolid is null: false
isSolid[x][y] is null: true

And why is that? It's an array of Booleans, not booleans, so it is not initialized to Boolean.FALSE but rather it defaults to null. Solution: either use boolean[][] array or initialize your array explicitly.

Most important: use informative catch blocks and don't catch for general Exceptions.


Edit note that as an aside, in order for me to get your code to run, I had to disable your use of images and sprite sheets, since these are resources that are unavailable to me. This effort should be yours though since you are the one seeking in the future. I ask that in the future, you limit your code to the smallest code that we can test and run, that demonstrates your problem, but that has no code unrelated to your problem, and that does not rely on outside resources such as images, databases, etc..., an sscce.

Upvotes: 2

Related Questions