好的,所以我得到了这个空指针异常,我和我的讲师都无法弄明白。 (他认为这是错误的逻辑,我倾向于同意)
我正在创建自己的3d对象并将其直接渲染到x和y的screenpace co-ords。这给了我正是我想要的练习,我能够优化数字以获得平滑的渲染,而不会在我的课程计算机上丢失很多帧(意味着我无法为我的讲师重现空指针)但是一旦我运行它在家里的野兽机器代码帧看起来要快得多,并且我正在渲染的立方体反复闪烁,同时将空指针异常吐出到控制台(即使我操纵数字使其全部减速)。它永远不会崩溃。
以下是正在使用的类。
Viewport.java(附加到JFrame)
package com.my3d.graphics;
import java.awt.BasicStroke;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.RenderingHints;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import com.my3d.geometry.Cube;
import com.my3d.utils.Props;
public class Viewport extends Canvas implements Runnable
{
private static final long serialVersionUID = 1L;
private static final String DEFAULT_NAME = "Viewport";
private static int viewportCount = 0;
private static final BasicStroke stroke = new BasicStroke(0.5f);
private Cube cube;
private boolean running;
private String name;
private int number;
public Viewport()
{
super();
//name the Viewport
if(viewportCount < 10)
setThisName(DEFAULT_NAME +"_0" +viewportCount);
else
setThisName(DEFAULT_NAME +"_" +viewportCount);
//set Identity
setNumber(viewportCount);
//increment the Viewport counter
viewportCount++;
cube = new Cube();
cube.printCubeVertices();
cube.rotateZ(20);
cube.printCubeVertices();
cube.rotateX(20);
cube.printCubeVertices();
running = true;
}
public Viewport(String n)
{
setThisName(n);
viewportCount++;
cube = new Cube();
//cube.printCubeVertices();
//cube.rotateZ(20);
// cube.printCubeVertices();
//cube.rotateX(20);
//cube.printCubeVertices();
running = true;
}
public void tick()
{
//cube.rotateY(0.0001);
//cube.rotateX(0.0001);
repaint();
tock();
}
private void tock()
{
tick();
}
public void setThisName(String n)
{
name = n;
}
private void setNumber(int n)
{
number = n;
}
public Cube getCube()
{
return cube;
}
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setStroke(stroke);
Vertex[] verts = cube.getVerts();
for(int i = 0; i < verts.length; i++)
{
ArrayList<Vertex> links = verts[i].getLinks();
for(int j = 0; j < links.size(); j++)
{
Line2D l = new Line2D.Double(verts[i].getPosition().x()+500,/**/verts[i].getPosition().y()+400,/*|*/links.get(j).getPosition().x()+500,/**/links.get(j).getPosition().y()+400);
g2.setColor(Color.RED);
g2.draw(l);
}
}
}
@Override
public void run()
{
int frame = 0;
while(running)
{
if(frame == 1)
{
cube.rotateY(0.0004);
cube.rotateZ(-0.0005);
cube.rotateX(-0.0003);
}
if(frame > 200000)
{
//cube.rotateY(0.0001);
//cube.rotateZ(0.000015);
repaint();
frame = 0;
}
frame++;
}
}
}
Cube.java
package com.my3d.geometry;
import java.util.ArrayList;
import com.my3d.graphics.Vector3;
import com.my3d.graphics.Vertex;
public class Cube
{
private static final String DEFAULT_NAME = "Cube";
private static final double DEFAULT_SIZE = 100;
private static int cubeCount = 0;
private Vertex[] corners;
private Vector3 position;
private Vector3 rotation;
private String name;
private int number;
public Cube()
{
if(cubeCount < 10)
setName(DEFAULT_NAME +"_0" +cubeCount);
else
setName(DEFAULT_NAME +"_" +cubeCount);
//assign Id
number = cubeCount;
//Increment cube counter
cubeCount++;
corners = new Vertex[8];
for(int i = 0; i < 8; i++)
corners[i] = new Vertex();
corners[0].setPosition(new Vector3(DEFAULT_SIZE,DEFAULT_SIZE,DEFAULT_SIZE));
corners[1].setPosition(new Vector3(-DEFAULT_SIZE,DEFAULT_SIZE,DEFAULT_SIZE));
corners[2].setPosition(new Vector3(-DEFAULT_SIZE,DEFAULT_SIZE,-DEFAULT_SIZE));
corners[3].setPosition(new Vector3(DEFAULT_SIZE,DEFAULT_SIZE,-DEFAULT_SIZE));
corners[4].setPosition(new Vector3(DEFAULT_SIZE,-DEFAULT_SIZE,DEFAULT_SIZE));
corners[5].setPosition(new Vector3(-DEFAULT_SIZE,-DEFAULT_SIZE,DEFAULT_SIZE));
corners[6].setPosition(new Vector3(-DEFAULT_SIZE,-DEFAULT_SIZE,-DEFAULT_SIZE));
corners[7].setPosition(new Vector3(DEFAULT_SIZE,-DEFAULT_SIZE,-DEFAULT_SIZE));
corners[0].addLink(corners[1]);
corners[1].addLink(corners[2]);
corners[2].addLink(corners[3]);
corners[3].addLink(corners[0]);
corners[0].addLink(corners[4]);
corners[1].addLink(corners[5]);
corners[2].addLink(corners[6]);
corners[3].addLink(corners[7]);
corners[4].addLink(corners[5]);
corners[5].addLink(corners[6]);
corners[6].addLink(corners[7]);
corners[7].addLink(corners[4]);
// corners[0].addLink(corners[5]);
// corners[1].addLink(corners[6]);
// corners[2].addLink(corners[7]);
// corners[3].addLink(corners[4]);
//
// corners[0].addLink(corners[2]);
// corners[5].addLink(corners[7]);
setPosition(new Vector3());
}
public Cube(String n)
{
setName(n);
//assign Id
number = cubeCount;
//Increment cube counter
cubeCount++;
}
////////////////////////////////////////////////////////////////
//Setters
////////////////////////////////////////////////////////////////
private void setName(String n)
{
name = n;
}
private void setPosition(Vector3 v)
{
position = v;
}
public Vertex[] getVerts()
{
return corners;
}
public void printCubeVertices()
{
for(int i = 0; i < 8; i++)
System.out.println(corners[i].getPosition().toString());
}
public void rotateZ(double degrees)
{
for(int i = 0; i < corners.length; i++)
{
double tempZ = corners[i].getPosition().z();
double newX = (corners[i].getPosition().x()-position.x())*Math.cos(degrees)-(corners[i].getPosition().y()-position.y())*Math.sin(degrees);
double newY = (corners[i].getPosition().x()-position.x())*Math.sin(degrees)+(corners[i].getPosition().y()-position.y())*Math.cos(degrees);
corners[i].setPosition(new Vector3(newX,newY,tempZ));
}
}
public void rotateX(double degrees)
{
for(int i = 0; i < corners.length; i++)
{
double tempX = corners[i].getPosition().x();
double newZ = (corners[i].getPosition().z()-position.z())*Math.cos(degrees)-(corners[i].getPosition().y()-position.y())*Math.sin(degrees);
double newY = (corners[i].getPosition().z()-position.z())*Math.sin(degrees)+(corners[i].getPosition().y()-position.y())*Math.cos(degrees);
corners[i].setPosition(new Vector3(tempX,newY,newZ));
}
}
public void rotateY(double degrees)
{
for(int i = 0; i < corners.length; i++)
{
double tempY = corners[i].getPosition().y();
double newZ = (corners[i].getPosition().z()-position.z())*Math.cos(degrees)-(corners[i].getPosition().x()-position.x())*Math.sin(degrees);
double newX = (corners[i].getPosition().z()-position.z())*Math.sin(degrees)+(corners[i].getPosition().x()-position.x())*Math.cos(degrees);
corners[i].setPosition(new Vector3(newX,tempY,newZ));
}
}
}
Vertex.java
package com.my3d.graphics;
import java.util.ArrayList;
public class Vertex
{
private Vector3 position;
private ArrayList<Vertex> linked = new ArrayList<Vertex>();
public Vertex()
{
setPosition(new Vector3(0.0,0.0,0.0));
}
public Vertex(Vector3 v)
{
setPosition(v);
}
public void setPosition(Vector3 pos)
{
position = pos;
}
public void addLink(Vertex v)
{
linked.add(v);
}
public void removeLink(Vertex v)
{
linked.remove(v);
}
/////////////////////////////////////////////////////////////////
//Getters
/////////////////////////////////////////////////////////////////
public Vector3 getPosition()
{
return position;
}
public ArrayList<Vertex> getLinks()
{
return linked;
}
}
Vector3.java
package com.my3d.graphics;
public class Vector3
{
private double[] xyz;
// public double x;
// public double y;
// public double z;
public Vector3()
{
xyz = new double []{0.0,0.0,0.0};
// x = xyz[0];
// y = xyz[1];
// z = xyz[2];
}
public Vector3(double x,double y,double z)
{
xyz = new double []{x,y,z};
// xyz[0] = x;
// xyz[1] = y;
// xyz[2] = z;
}
public Vector3(double[] array)
{
try
{
if(array.length > 3)
{
RuntimeException e = new RuntimeException("The Vector3 is too big by (" +(array.length - 3) +") elements you muppet.\nremember that a Vector3 supports arrays with a length of 3. You have (" +array.length +")");
throw e;
}
if(array.length < 3)
{
RuntimeException e = new RuntimeException("The Vector3 is too small by (" +(3 - array.length) +") elements you muppet.\nremember that a Vector3 supports arrays with a length of 3. You have (" +array.length +")");
throw e;
}
xyz[0] = array[0];
xyz[1] = array[1];
xyz[2] = array[2];
}
catch(RuntimeException e)
{
System.out.println(e);
}
}
///////////////////////////////////////////////////////////////////////////
public void x(double a)
{
xyz[0] = a;
}
public void y(double a)
{
xyz[1] = a;
}
public void z(double a)
{
xyz[2] = a;
}
public double x()
{
return xyz[0];
}
public double y()
{
return xyz[1];
}
public double z()
{
return xyz[2];
}
public void normalize()
{
double length = Math.sqrt((xyz[0] * xyz[0]) + (xyz[1] * xyz[1]) + (xyz[2] * xyz[2]));
xyz[0] = xyz[0]/length;
xyz[1] = xyz[1]/length;
xyz[2] = xyz[2]/length;
}
public String toString()
{
return "(" + xyz[0] + "," + xyz[1] + "," + xyz[2] + ")";
}
}
我能做的最好的事情就是将其追溯到public double x(){return xyz[0];}
在view3类中调用paint方法时,在Vector3类中,但是当我尝试单步执行时,它们永远不会为null。我得到的印象是,在旋转过程中尝试将新位置重新分配给顶点时,jvm正在追赶自己。
编辑:Stacktrace
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at com.my3d.graphics.Vector3.x(Vector3.java:70)
at com.my3d.graphics.Viewport.paint(Viewport.java:107)
at java.awt.Canvas.update(Unknown Source)
at sun.awt.RepaintArea.updateComponent(Unknown Source)
at sun.awt.RepaintArea.paint(Unknown Source)
at sun.awt.windows.WComponentPeer.handleEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)
其他信息:
Manager.java
package com.my3d.core;
import java.awt.Color;
import com.my3d.graphics.Viewport;
import com.my3d.graphics.Wind;
import com.my3d.utils.Props;
public class Manager {
/**
* @param args
*/
public static void main(String[] args)
{
Props.loadPropsFromFile();
Wind wind = new Wind();
wind.setVisible(true);
Viewport view = new Viewport("Main view");
view.setPreferredSize(Props.getWindowSize());
view.setBackground(Color.LIGHT_GRAY);
wind.add(view);
view.setVisible(true);
wind.pack();
view.run();
}
}
Wind.java
package com.my3d.graphics;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import com.my3d.utils.Props;
import javax.swing.JFrame;
public class Wind extends JFrame
{
/**
*
*/
private static final long serialVersionUID = 1L;
private boolean fullScreen = false;
//Constructor
public Wind()
{
super(Props.getDefaultTitle() +Props.getTitleSplash());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
fullScreen = Props.getFullScreen();
if (fullScreen)
{
setUndecorated(true);
GraphicsDevice vc;
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
vc= ge.getDefaultScreenDevice();
vc.setFullScreenWindow(this);
}
else
{
setBounds(0,0,Props.getWindowWidth(),Props.getWindowHeight());
setLocationRelativeTo(null);
}
}
public void setTitle(String s)
{
//if(Props.getRandomSplashes())
super.setTitle(Props.getDefaultTitle() +s);
}
public void setRandomTitle()
{
if(Props.getRandomSplashes())
Props.setTitleSplash(Props.randomizeSplash());
super.setTitle(Props.getDefaultTitle() +Props.getTitleSplash());
}
public void setFullScreen(boolean b)
{
fullScreen = b;
}
//this method wont work >>
public void refactorWindow()
{
if (fullScreen)
{
setUndecorated(true);
GraphicsDevice vc;
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
vc= ge.getDefaultScreenDevice();
vc.setFullScreenWindow(this);
}
else
{
setSize(Props.getWindowSize());
setLocationRelativeTo(null);
}
}
}
Props.java
package com.my3d.utils;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;
import java.util.Scanner;
import javax.imageio.ImageIO;
public class Props
{
private static String gameTitle;
private static String splashTitle;
private static Dimension windowSize;
//private static int gameState;
private static boolean fullScreen;
private static boolean randomSplashes;
private static BufferedImage tileSheet;
///////////////////////////////////////////////////////////////
//Getters
///////////////////////////////////////////////////////////////
public static Dimension getWindowSize()
{
return windowSize;
}
public static int getWindowWidth()
{
return (int) windowSize.getWidth();
}
public static int getWindowHeight()
{
return (int) windowSize.getHeight();
}
public static String getTitleSplash()
{
return splashTitle;
}
public static String getDefaultTitle()
{
return gameTitle;
}
public static boolean getFullScreen()
{
return fullScreen;
}
public static boolean getRandomSplashes()
{
return randomSplashes;
}
public static BufferedImage getTileSheet()
{
return tileSheet;
}
/////////////////////////////////////////////////////////////////////
//Setters
/////////////////////////////////////////////////////////////////////
public static void setWindowSize(int width, int height)
{
windowSize = new Dimension(width,height);
}
public static void setTitleSplash(String s)
{
splashTitle = s;
}
private static void setDefaultTitle(String s)
{
gameTitle = s;
}
////////////////////////////////////////////////////////////////////
//loader
////////////////////////////////////////////////////////////////////
public static void loadPropsFromFile()
{
File file = new File("res\\config\\props.props");
try
{
Scanner input = new Scanner(file);
String lInput = input.nextLine().trim();
setWindowSize(Integer.parseInt(lInput.substring(lInput.indexOf('=')+1,lInput.indexOf(','))),Integer.parseInt(lInput.substring(lInput.indexOf(',')+1,lInput.length())));
lInput = input.nextLine().trim();
if(lInput.substring(lInput.indexOf('=')+1,lInput.length()).equals("true"))
fullScreen = true;
else
fullScreen = false;
lInput = input.nextLine().trim();
if(lInput.substring(lInput.indexOf('=')+1,lInput.length()).equals("true"))
{
lInput = input.nextLine().trim();
setDefaultTitle(lInput.substring(lInput.indexOf('=')+1,lInput.length()));
randomSplashes = true;
setTitleSplash(randomizeSplash());
}
else
{
lInput = input.nextLine().trim();
setDefaultTitle(lInput.substring(lInput.indexOf('=')+1,lInput.length()));
setTitleSplash("");
randomSplashes = false;
}
lInput = input.nextLine().trim();
try
{
tileSheet = ImageIO.read(new File(loadFromDirectory(lInput.substring(lInput.indexOf('=')+1,lInput.length()))));
}
catch (IOException e)
{
System.out.println("Tilesheet Cannot be found.");
}
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
}
///////////////////////////////////////////////////////////////////
//Utility
///////////////////////////////////////////////////////////////////
public static String loadFromDirectory(String key)
{
String s = "";
File sFile = new File("res\\config\\dirs.dir");
try
{
Scanner sInput = new Scanner(sFile);
while(sInput.hasNextLine())
{
String st = sInput.nextLine().trim();
if(key.equals(st.substring(1,st.length())));
s = sInput.nextLine().trim();
}
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
return s;
}
public static String randomizeSplash()
{
if(randomSplashes)
{
File sFile = new File("res\\config\\splash.props");
ArrayList<String> sArray = new ArrayList<String>();
try
{
Scanner sInput = new Scanner(sFile);
while(sInput.hasNextLine())
{
sArray.add(sInput.nextLine().trim());
}
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
Random rand = new Random();
String s = sArray.get(rand.nextInt(sArray.size()));
splashTitle = s;
return s;
}
else
return "";
}
}
props.props
windowSize =1000,800
startInFullscreen =false
randomSplashes =false
defaultTitle =3D Prototype
tileSheet =tileSheet
dirs.dir
<startGame>
res\\img\\button\\Start_game_text.png
res\\img\\button\\Start_game_active.png
res\\img\\button\\Start_game_click.png
<quitGame>
res\\img\\button\\Quit_game_text.png
res\\img\\button\\Quit_game_active.png
res\\img\\button\\Quit_game_click.png
<menuBackground>
res\\img\\Menu_background.png
<resumeGame>
res\\img\\button\\Resume_game_text.png
res\\img\\button\\Resume_game_active.png
res\\img\\button\\Resume_game_click.png
<tileSheet>
res\\img\\Final_Sub_Hunt.png
答案 0 :(得分:1)
更新:将xyz
中的字段Vector3
标记为final
。我对JMM的阅读说,如果没有同步器或者最后一个,它对于事件调度线程(EDT)是合法的,从你的堆栈跟踪到来,看到构造函数在构造函数时尚未初始化的非最终字段在一个单独的(Viewport.run()
)线程中运行。请参阅this SO question或the JMM FAQ。
在JLS中查找的正确位置是Section 17.5(尽管你真的需要阅读所有17个,很多时候,开始理解这个):
当一个对象被认为是完全初始化时 构造函数完成。一个只能看到对一个引用的线程 保证该对象已完全初始化后的对象 查看该对象最终的正确初始化值 字段。
请注意,同样的保证不适用于非最终字段;如果你想要那里的安全,你将需要使用某种同步边缘(显式同步器,易失性,等等。)
相关的discussion from Alex Miller,我认为他们将StackOverflow作为https://stackoverflow.com/users/7671/alex-miller,并且如果我们打招呼,可能会捅他的头。)和related SO question。
您的Vector3(double[])
构造函数中似乎有错误,该错误从不初始化double[] xyz
字段。只有我可以找到那个字段不会被初始化并产生NPE的地方。
我无法从你的代码中看出你运行了多少线程。如果要在一个线程中创建Vector3
的实例,并在它们由单独的呈现线程显示时将它们放入集合中,则可能是在部分构造状态下观察它们。一般来说,事情的安排使得这种情况不太可能(我记不起内存模型关于发布部分构造的对象的内容,但是如果你让this
从构造函数中滑出来就会发生;我不认为这发生在那里。)
JA
答案 1 :(得分:1)
catch(RuntimeException e)
{
System.out.println(e);
}
此段允许您在未正确初始化阵列的情况下创建Vector3。那是一个大问题。
在分配它们之前,两个非默认构造函数都不会尝试检查x,y或z是否为空。
我会从那里开始,然后考虑单元测试。