我正在编写一个java rpg游戏的编程过程中,并且已经达到了一个难关。我的代码目前有精灵动画,带有perlin噪声和碰撞检测的随机地图生成。地图是平铺的,因此我正在尝试将perlin噪声转换为平铺。 perlin函数生成一个数组,并将该数组的每个数字都添加到tile png中。这就是问题所在:RUNTIME ERROR:Java.Lang.NullPointerException。 问题是我的编译器(netbeans)没有显示错误发生的位置,而是只给我这个错误代码。通过排除过程,我设法找到错误,该错误发生在第364行。如果此站点不支持行,则它位于loadTile()方法的“if(perlinIsland [x] [y]< = 0.05) blockImg [x] [y] = TILE [0];“。我相信所有变量都已正确初始化,但我无法找到解决方案。请原谅长代码,但我为了提供信息而包含了所有内容。提前感谢您的帮助!
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();
}
}
答案 0 :(得分:2)
你的有限捕获代码阻碍了你找到空值的能力。
例如,这些代码行:
try {
while (true) {
moveMap();
if (!still)
update(System.currentTimeMillis());
Thread.sleep(13);
}
} catch (Exception e) {
System.out.println("RUNTIME ERROR: " + e);
}
只会打印
RUNTIME ERROR: java.lang.NullPointerException
当此代码进入NPE时没有行号或堆栈跟踪。
首先,您不应该捕获普通的异常,而应该捕获明确的异常。接下来,您应该使用更具信息性的catch块,例如至少通过e.printStackTrace()
打印出堆栈跟踪的catch块。
上面的块应该真的写成:
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();
}
}
}
这样做,你会发现NPE出现在这里:
if (in.intersects(blocks[x][y]) && isSolid[x][y] == true) {
然后你可以在该行前面填充代码以查看导致问题的变量:
例如,
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;
}
}
}
你会发现问题是isSolid[x][y]
为空:
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
那是为什么?它是一个布尔数组,而不是布尔值,所以它没有初始化为Boolean.FALSE,而是默认为null。解决方案:使用boolean [] []数组或显式初始化数组。
最重要的是:使用信息丰富的catch块,而不是捕获一般的例外。
编辑请注意,为了让我能够运行您的代码,我必须禁用图像和精灵表的使用,因为这些资源对我来说是不可用的。这项工作应该是你的,因为你是未来的人。我要求您将来将代码限制为我们可以测试和运行的最小代码,这可以证明您的问题,但是没有与您的问题无关的代码,并且不依赖于外部资源,如图像,数据库等等......,sscce。