【问题标题】:How to fix the movement of 3D object?如何修复 3D 物体的运动?
【发布时间】:2014-01-08 12:52:52
【问题描述】:

我得到了一个 3D 宇宙飞船,它在 3D 场景中正确地向前和向后移动,但向右和向左的移动不正确,而且 A 和 D 按钮的作用似乎随相机位置而变化。这是适用于按钮 W(前进)和 S(后退)的侦听器代码,而按钮 A 和 D 并没有完全做到它们应该做的事情。

当我开始 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);
                }
            }
        }
    };
}

【问题讨论】:

  • 在您的方法中:public void onAnalog(String name, float value, float tpf),将“cam”更改为“camNode”是否正确?在您的完整代码中,我看不到 cam 是可访问的。
  • @MaineCoder 我认为cam 可能是从基类继承的。使用它没有错误,它可以向前和向后移动。如果您喜欢我的项目,可以查看 sourceforge.net/projects/spaceworld/
  • 为什么左右有crossLocal和multLocal,前进后退只有multLocal?
  • 我觉得 .crossLocal(Vector3f.UNIT_Y) 在这里引起了问题,因为您是从 Vector3f 类而不是类的实例调用 UNIT_Y...
  • @MaineCoder 它在开始时有效,但在我旋转相机后无效。我可能应该使用crossLocal,但我不知道在那里使用哪个 Vector。你知道哪个 Vector 应该是 crossLocal 的参数吗?因为它在我移动相机之前就开始工作了,所以我可能需要来自相机的矢量,并且矢量与 UFO 的移动矢量相同。

标签: java 3d game-physics jmonkeyengine


【解决方案1】:

结束您的最后一条评论,我将其发布为答案(尽管我不确定将什么用作交叉向量。

在检索十字矢量时,我们希望得到一条垂直于飞船前部的直线,以及垂直于该直线的垂直线,垂直穿过飞船的中心。

我假设方向是我们的前向矢量,在这种情况下(无论视图如何),我们都希望与穿过飞船中心的垂直线相交。这两个向量的 crossLocal 将是一条与两者垂直的线,要么从飞行器的左侧或右侧伸出(无论相机或飞行器方向如何)。

对于我的代码修复,我假设 craftSkewer 是一个假想的串,它垂直穿过飞船的中心。

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

我认为这最初起作用的原因是由于 UNIT_Y 返回 0 - 但是在移动飞行器或相机后,它重新计算不正确?

【讨论】:

  • 是的。但是如何计算 craftSkewer 呢?我使用 jmonkeyengine 的 Node 类,没有方法可以返回我需要的东西。
  • 您必须在内存中保留一个垂直于直线方向的向量,并为所有运动保持更新。或者,根据飞行器的前进位置和旋转值计算垂直位置。我认为使用当前代码将修改过的向量保存在内存中是最简单的。
  • 好的。但是如何开始垂直于直线方向呢? Vector3F 文档是jmonkeyengine.org/doc/com/jme/math/Vector3f.html,我找到了两种方法crossLocalcross,但我不明白它们的区别。我认为应该有一个像 getPerpendicular 这样的方法用于 Vector3f 和一种直接获取 craftSkewer 的方法,但很难做到这一点。
  • Cross返回cross,crossLocal修改传入的对象。
  • 更接近我认为的那种方法 - 我会尝试一些不同的配置,看看什么有效(让我知道解决方案是什么哈哈)。我只希望我参加过 3D 设计和矢量方面的任何课程,这样我才能确定答案...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-03-27
  • 1970-01-01
  • 1970-01-01
  • 2015-04-13
  • 2017-07-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多