1

好的,所以我得到了这个空指针异常,我自己和我的讲师都无法弄清楚。(他认为它的逻辑不好,我倾向于同意)

我正在制作自己的 3d 对象,并将其直接渲染为 x 和 y 的屏幕空间坐标。这正是我想要的练习,我能够优化数字以获得平滑的渲染,而不会在我的课程计算机上丢掉很多帧(这意味着我不能为我的讲师重现空指针)但是一旦我运行它家里的野兽机器代码帧似乎运行得更快,并且我正在渲染的立方体反复闪烁,同时向控制台吐出空指针异常(即使我操纵数字以减慢它的速度)。但它永远不会崩溃。

这是正在使用的类。

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++;
        }

    }

}

立方体.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));
        }
    }
}

顶点.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];} 在视口类中调用paint方法时在 Vector3 类中跟踪它,但是当我尝试逐步执行时,它们永远不会为空。我得到的印象是 jvm 在尝试在旋转期间为顶点重新分配新位置时正在赶上自己。

编辑:堆栈跟踪

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)

附加信息:

管理器.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);
        }
    }

}

道具.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

windowSize =1000,800
startInFullscreen =false
randomSplashes =false
defaultTitle =3D Prototype
tileSheet =tileSheet

目录.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
4

2 回答 2

1

更新:将您的字段标记xyz为。我对 JMM 的阅读表明,如果没有同步器或 final,事件调度线程 (EDT) 是合法的,从您的堆栈跟踪而来,在构造函数时看到构造函数尚未初始化的非 final 字段在单独的(您的)线程中运行。请参阅此 SO 问题JMM 常见问题解答Vector3finalViewport.run()

在 JLS 中查看此内容的正确位置是在第 17.5 节中(尽管您确实需要阅读全部 17 篇,并且很多次才能开始理解这一点):

当一个对象的构造函数完成时,它被认为是完全初始化的。只有在对象完全初始化后才能看到对该对象的引用的线程可以保证看到该对象的最终字段的正确初始化值。

请注意,同样的保证不适用于非最终字段;如果你想要那里的安全,你将需要使用某种同步边缘(显式同步器,易失性,等等。)

来自 Alex Miller的相关讨论,我认为他在 StackOverflow 周围的推杆是https://stackoverflow.com/users/7671/alex-miller,如果我们打招呼可能会戳他的头。)还有一个相关的 SO 问题

原始答案

看起来你的Vector3(double[])构造函数中有一个错误,它永远不会初始化该double[] xyz字段。只有我能找到该字段不会被初始化并产生 NPE 的地方。

我无法从您的代码中看出您运行了多少线程。如果您Vector3在一个线程中创建实例并将它们放入一个集合中,同时它们正在由单独的渲染线程显示,那么您可能会在部分构造状态下观察它们。一般来说,事情的安排使这不太可能(我不记得内存模型关于发布部分构造的对象的内容,但如果你this从构造函数中滑出,它可能会发生;我不明白发生在这里。)

JA

于 2013-03-15T19:15:35.833 回答
1
    catch(RuntimeException e)
    {
        System.out.println(e);
    }

此段允许您在不正确初始化数组的情况下创建 Vector3。这是一个潜在的问题。

这两个非默认构造函数在分配它们之前都不会尝试检查 x、y 或 z 是否为空。

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

于 2013-03-15T19:22:26.137 回答