我的vector3类中的线程“AWT-EventQueue-0”java.lang.NullPointerException中的异常

时间:2013-03-14 16:32:52

标签: java 3d nullpointerexception paint java-memory-model

好的,所以我得到了这个空指针异常,我和我的讲师都无法弄明白。 (他认为这是错误的逻辑,我倾向于同意)

我正在创建自己的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

2 个答案:

答案 0 :(得分:1)

更新:将xyz中的字段Vector3标记为final。我对JMM的阅读说,如果没有同步器或者最后一个,它对于事件调度线程(EDT)是合法的,从你的堆栈跟踪到来,看到构造函数在构造函数时尚未初始化的非最终字段在一个单独的(Viewport.run())线程中运行。请参阅this SO questionthe 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是否为空。

我会从那里开始,然后考虑单元测试。