如何修复3D物体的运动?

时间:2014-01-08 12:52:52

标签: java 3d game-physics jmonkeyengine

我有一个3D飞船在3D场景中正向前后移动,但向右和向左移动不正确,A和D按钮看起来与摄像机位置有关。这是用于按钮W(向前)和S(向后)的侦听器代码,而按钮A和D不能完全按照它们的要求进行操作。

enter image description here

当我启动3D空间场景时,宇宙飞船的转向正在工作,按钮A和D左右移动宇宙飞船,但在更换摄像机并旋转视图后,按钮A和D仍然是相反的方向但是不是左右两边,而是取决于相机的位置。

    public void onAnalog(String name, float value, float tpf) {
    // computing the normalized direction of the cam to move the node
    int movement = 80000;
    int rotation = 1;
    direction.set(cam.getDirection()).normalizeLocal();
    if (name.equals("moveForward")) {
        direction.multLocal(movement * tpf);
        ufoNode.move(direction);
    }
    if (name.equals("moveBackward")) {
        direction.multLocal(-movement * tpf);
        ufoNode.move(direction);
    }
    if (name.equals("moveRight")) {
        direction.crossLocal(Vector3f.UNIT_Y).multLocal(movement * tpf);
        ufoNode.move(direction);
    }
    if (name.equals("moveLeft")) {
        direction.crossLocal(Vector3f.UNIT_Y).multLocal(-movement * tpf);
        ufoNode.move(direction);
    }
    if (name.equals("rotateRight") && rotate) {
        ufoNode.rotate(0, 1 * tpf, 0);
    }
    if (name.equals("rotateLeft") && rotate) {
        ufoNode.rotate(0, -1 * tpf, 0);
    }
    if (name.equals("rotateUp") && rotate) {
        ufoNode.rotate(0, 0, -1 * tpf);
    }
    if (name.equals("rotateDown") && rotate) {
        ufoNode.rotate(0, 0, 1 * tpf);
    }
}

你能帮助我并告诉我应该怎样做才能解决左右的动作吗?整个代码是

public class SpaceStation extends SimpleApplication implements AnalogListener,
        ActionListener {

    private PlanetAppState planetAppState;
    private Geometry mark;
    private Node ufoNode;       
    private Node spaceStationNode;  
    private Node jumpgateNode;
    private Node jumpgateNode2;     
    private BetterCharacterControl ufoControl;
    CameraNode camNode;
    boolean rotate = false;
    Vector3f direction = new Vector3f();
    private BulletAppState bulletAppState;

    public static void main(String[] args) {
        AppSettings settings = new AppSettings(true);
        settings.setResolution(1024, 768);
        SpaceStation app = new SpaceStation();
        app.setSettings(settings);
        // app.showSettings = true;
        app.start();
    }

    @Override
    public void simpleInitApp() {
        // Only show severe errors in log
        java.util.logging.Logger.getLogger("com.jme3").setLevel(
                java.util.logging.Level.SEVERE);

        bulletAppState = new BulletAppState();
        bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
        stateManager.attach(bulletAppState);
        bulletAppState.setDebugEnabled(false);

        DirectionalLight sun = new DirectionalLight();
        sun.setDirection(new Vector3f(-.1f, 0f, -1f));
        sun.setColor(new ColorRGBA(0.75f, 0.75f, 0.75f, 1.0f));
        rootNode.addLight(sun);

        // Add sky
        Node sceneNode = new Node("Scene");
        sceneNode.attachChild(Utility.createSkyBox(this.getAssetManager(),
                "Textures/blue-glow-1024.dds"));
        rootNode.attachChild(sceneNode);

        // Create collision test mark
        Sphere sphere = new Sphere(30, 30, 5f);
        mark = new Geometry("mark", sphere);
        Material mark_mat = new Material(assetManager,
                "Common/MatDefs/Misc/Unshaded.j3md");
        mark_mat.setColor("Color", ColorRGBA.Red);
        mark.setMaterial(mark_mat);

        // Add planet app state
        planetAppState = new PlanetAppState(rootNode, sun);
        stateManager.attach(planetAppState);

        // Add planet
        FractalDataSource planetDataSource = new FractalDataSource(4);
        planetDataSource.setHeightScale(900f);
        Planet planet = Utility.createEarthLikePlanet(getAssetManager(),
                293710.0f, null, planetDataSource);
        planetAppState.addPlanet(planet);
        rootNode.attachChild(planet);

        // Add moon
        FractalDataSource moonDataSource = new FractalDataSource(5);
        moonDataSource.setHeightScale(300f);
        Planet moon = Utility.createMoonLikePlanet(getAssetManager(), 50000,
                moonDataSource);
        planetAppState.addPlanet(moon);
        rootNode.attachChild(moon);
        moon.setLocalTranslation(new Vector3f(10f, 10f, 505000f));//-950000f, 0f, 0f);
        // add saucer
        ufoNode = (Node) assetManager.loadModel("usaucer_v01.j3o");
        ufoNode.setLocalScale(100f);
        ufoNode.setLocalTranslation((new Vector3f(1000f, -1000f, 328000f)));
        jumpgateNode = (Node) assetManager.loadModel("JumpGate.j3o");
        jumpgateNode.setLocalScale(10000f);
        jumpgateNode.setLocalTranslation((new Vector3f(10f, 10f, 708000f)));

        spaceStationNode = (Node) assetManager.loadModel("SpaceStation.j3o");
        spaceStationNode.setLocalScale(4000f);
        spaceStationNode.setLocalTranslation((new Vector3f(10000f, -10f, 425000f)));    

        jumpgateNode2 = (Node) assetManager.loadModel("JumpGate.j3o");
        jumpgateNode2.setLocalScale(10000f);
        jumpgateNode2.setLocalTranslation((new Vector3f(10f, 10f, 798300f)));

        /* This quaternion stores a 180 degree rolling rotation */
        // Quaternion roll180 = new Quaternion();
        // roll180.fromAngleAxis(FastMath.PI, new Vector3f(0, 0, 1));
        /* The rotation is applied: The object rolls by 180 degrees. */
        // ufoNode.setLocalRotation(roll180);
        rootNode.attachChild(jumpgateNode);
        rootNode.attachChild(jumpgateNode2);
        rootNode.attachChild(spaceStationNode);     

        // creating the camera Node
        camNode = new CameraNode("CamNode", cam);
        // Setting the direction to Spatial to camera, this means the camera
        // will copy the movements of the Node
        camNode.setControlDir(ControlDirection.SpatialToCamera);
        // attaching the camNode to the teaNode
        ufoNode.attachChild(camNode);
        // setting the local translation of the cam node to move it away a bit
        camNode.setLocalTranslation(new Vector3f(-40, 0, 0));
        // setting the camNode to look at the teaNode
        camNode.lookAt(ufoNode.getLocalTranslation(), Vector3f.UNIT_Y);
        // disable the default 1st-person flyCam (don't forget this!!)
        ufoControl = new BetterCharacterControl(100000f, 80000f, 5000f);// (2, 4, 0.5f);
        // radius (meters), height (meters), gravity (mass)
        //ufoNode.addControl(ufoControl);
        //rootNode.attachChild(ninjaNode);
        //bulletAppState.getPhysicsSpace().add(ufoControl);
        //getPhysicsSpace().add(ufoControl);
        rootNode.attachChild(ufoNode);
        flyCam.setEnabled(false);
        registerInput();
    }

    private PhysicsSpace getPhysicsSpace() {
        return bulletAppState.getPhysicsSpace();
    }

    public void registerInput() {
        inputManager.addMapping("moveForward", new KeyTrigger(keyInput.KEY_UP),
                new KeyTrigger(keyInput.KEY_W));
        inputManager.addMapping("moveBackward", new KeyTrigger(
                keyInput.KEY_DOWN), new KeyTrigger(keyInput.KEY_S));
        inputManager.addMapping("moveRight",
                new KeyTrigger(keyInput.KEY_RIGHT), new KeyTrigger(
                        keyInput.KEY_D));
        inputManager.addMapping("moveLeft", new KeyTrigger(keyInput.KEY_LEFT),
                new KeyTrigger(keyInput.KEY_A));
        inputManager.addMapping("toggleRotate", new MouseButtonTrigger(
                MouseInput.BUTTON_LEFT));
        inputManager.addMapping("rotateRight", new MouseAxisTrigger(
                MouseInput.AXIS_X, true));
        inputManager.addMapping("rotateLeft", new MouseAxisTrigger(
                MouseInput.AXIS_X, false));
        inputManager.addMapping("rotateUp", new MouseAxisTrigger(
                MouseInput.AXIS_Y, true));
        inputManager.addMapping("rotateDown", new MouseAxisTrigger(
                MouseInput.AXIS_Y, false));
        inputManager.addListener(this, "moveForward", "moveBackward",
                "moveRight", "moveLeft");
        inputManager.addListener(this, "rotateRight", "rotateLeft", "rotateUp",
                "rotateDown", "toggleRotate");
        // Toggle mouse cursor
        inputManager.addMapping("TOGGLE_CURSOR", new MouseButtonTrigger(
                MouseInput.BUTTON_LEFT), new KeyTrigger(KeyInput.KEY_SPACE));
        inputManager.addListener(actionListener, "TOGGLE_CURSOR");
        // Toggle wireframe
        inputManager.addMapping("TOGGLE_WIREFRAME", new KeyTrigger(
                KeyInput.KEY_T));
        inputManager.addListener(actionListener, "TOGGLE_WIREFRAME");
        // Collision test
        inputManager.addMapping("COLLISION_TEST", new MouseButtonTrigger(
                MouseInput.BUTTON_RIGHT));
        inputManager.addListener(actionListener, "COLLISION_TEST");
    }

    public void onAnalog(String name, float value, float tpf) {
        // computing the normalized direction of the cam to move the node
        int movement = 80000;
        int rotation = 1;
        direction.set(cam.getDirection()).normalizeLocal();
        if (name.equals("moveForward")) {
            direction.multLocal(movement * tpf);
            ufoNode.move(direction);
        }
        if (name.equals("moveBackward")) {
            direction.multLocal(-movement * tpf);
            ufoNode.move(direction);
        }
        if (name.equals("moveRight")) {
            direction.crossLocal(Vector3f.UNIT_Y).multLocal(movement * tpf);
            ufoNode.move(direction);
        }
        if (name.equals("moveLeft")) {
            direction.crossLocal(Vector3f.UNIT_Y).multLocal(-movement * tpf);
            ufoNode.move(direction);
        }
        if (name.equals("rotateRight") && rotate) {
            ufoNode.rotate(0, 1 * tpf, 0);
        }
        if (name.equals("rotateLeft") && rotate) {
            ufoNode.rotate(0, -1 * tpf, 0);
        }
        if (name.equals("rotateUp") && rotate) {
            ufoNode.rotate(0, 0, -1 * tpf);
        }
        if (name.equals("rotateDown") && rotate) {
            ufoNode.rotate(0, 0, 1 * tpf);
        }
    }

    public void onAction(String name, boolean keyPressed, float tpf) {
        // toggling rotation on or off
        if (name.equals("toggleRotate") && keyPressed) {
            rotate = true;
            inputManager.setCursorVisible(false);
        }
        if (name.equals("toggleRotate") && !keyPressed) {
            rotate = false;
            inputManager.setCursorVisible(true);
        }
        if (name.equals("TOGGLE_CURSOR") && !keyPressed) {
            if (inputManager.isCursorVisible()) {
                inputManager.setCursorVisible(false);
            } else {
                inputManager.setCursorVisible(true);
            }
        }
        if (name.equals("TOGGLE_WIREFRAME") && !keyPressed) {
            for (Planet planet : planetAppState.getPlanets()) {
                planet.toogleWireframe();
            }
        }
        if (name.equals("COLLISION_TEST") && !keyPressed) {
            CollisionResults results = new CollisionResults();
            Ray ray = new Ray(cam.getLocation(), cam.getDirection());
            // Test collision with closest planet's terrain only
            planetAppState.getNearestPlanet().getTerrainNode()
                    .collideWith(ray, results);
            System.out.println("----- Collisions? " + results.size() + "-----");
            for (int i = 0; i < results.size(); i++) {
                // For each hit, we know distance, impact point, name of
                // geometry.
                float dist = results.getCollision(i).getDistance();
                Vector3f pt = results.getCollision(i).getContactPoint();
                String hit = results.getCollision(i).getGeometry().getName();
                System.out.println("* Collision #" + i);
                System.out.println("  You shot " + hit + " at " + pt + ", "
                        + dist + " wu away.");
            }
            if (results.size() > 0) {
                // The closest collision point is what was truly hit:
                CollisionResult closest = results.getClosestCollision();
                // Let's interact - we mark the hit with a red dot.
                mark.setLocalTranslation(closest.getContactPoint());
                rootNode.attachChild(mark);
            } else {
                // No hits? Then remove the red mark.
                rootNode.detachChild(mark);
            }
        }
    }

    private ActionListener actionListener = new ActionListener() {
        public void onAction(String name, boolean pressed, float tpf) {
            if (name.equals("TOGGLE_CURSOR") && !pressed) {
                if (inputManager.isCursorVisible()) {
                    inputManager.setCursorVisible(false);
                } else {
                    inputManager.setCursorVisible(true);
                }
            }
            if (name.equals("TOGGLE_WIREFRAME") && !pressed) {
                for (Planet planet : planetAppState.getPlanets()) {
                    planet.toogleWireframe();
                }
            }
            if (name.equals("COLLISION_TEST") && !pressed) {
                CollisionResults results = new CollisionResults();
                Ray ray = new Ray(cam.getLocation(), cam.getDirection());
                // Test collision with closest planet's terrain only
                planetAppState.getNearestPlanet().getTerrainNode()
                        .collideWith(ray, results);
                System.out.println("----- Collisions? " + results.size()
                        + "-----");
                for (int i = 0; i < results.size(); i++) {
                    // For each hit, we know distance, impact point, name of
                    // geometry.
                    float dist = results.getCollision(i).getDistance();
                    Vector3f pt = results.getCollision(i).getContactPoint();
                    String hit = results.getCollision(i).getGeometry()
                            .getName();
                    System.out.println("* Collision #" + i);
                    System.out.println("  You shot " + hit + " at " + pt + ", "
                            + dist + " wu away.");
                }
                if (results.size() > 0) {
                    // The closest collision point is what was truly hit:
                    CollisionResult closest = results.getClosestCollision();
                    // Let's interact - we mark the hit with a red dot.
                    mark.setLocalTranslation(closest.getContactPoint());
                    rootNode.attachChild(mark);
                } else {
                    // No hits? Then remove the red mark.
                    rootNode.detachChild(mark);
                }
            }
        }
    };
}

1 个答案:

答案 0 :(得分:1)

关闭你的最后评论,我发布这个作为答案(虽然我不确定该用什么作为交叉向量。

当检索交叉矢量时,我们希望垂直于飞行器前部的直线和垂直于直线的垂直线垂直穿过飞行器的中心。

我认为方向是我们的前向矢量,在这种情况下(无论视图如何)我们想要穿过飞行中心的垂直线。这两个矢量的crossLocal将是两条垂直线,无论是从飞行器的左侧还是右侧(无论相机还是工艺方向)。

对于我的代码修复,我将假设craftSkewer是一个虚构的串,垂直穿过飞行器的中心。

direction.crossLocal(craftSkewer.UNIT_Y).multLocal(movement * tpf);

我认为这最初起作用的原因是因为UNIT_Y返回0 - 但是在移动飞行器或相机后,它会被错误地重新计算?