Как исправить движение 3D объекта?

У меня есть 3D-космический корабль, который правильно движется вперед и назад в 3D-сцене, но движения вправо и влево неправильные, и то, что кнопки A и D действительно меняются в зависимости от положения камеры. Это код прослушивателя, который работает для кнопок W (вперед) и S (назад), в то время как кнопки A и D не делают того, что должны.

введите здесь описание изображения

Когда я запускаю трехмерную космическую сцену, управление космическим кораблем работает, и кнопки 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);
                }
            }
        }
    };
}

person Niklas R.    schedule 08.01.2014    source источник
comment
В вашем методе: public void onAnalog (имя строки, значение с плавающей запятой, значение с плавающей точкой tpf), будет ли правильно изменить cam на camNode? Я не вижу камеры доступной в вашем полном коде.   -  person MaineCoder    schedule 08.01.2014
comment
@MaineCoder Я думаю, что cam может быть унаследован от базового класса. При его использовании не было ошибок, и он работает при движении вперед и назад. Вы можете посмотреть, если хотите, мой проект на sourceforge.net/projects/spaceworld/   -  person Niklas R.    schedule 08.01.2014
comment
почему есть crossLocal и multLocal для левого и правого, но только multLocal для прямого и обратного?   -  person MaineCoder    schedule 08.01.2014
comment
Я чувствую, что .crossLocal(Vector3f.UNIT_Y) вызывает здесь проблему, поскольку вы вызываете UNIT_Y из класса Vector3f, а не из экземпляра класса...   -  person MaineCoder    schedule 08.01.2014
comment
@MaineCoder Это работает в начале, но не после того, как я поверну камеру. Я, вероятно, должен использовать crossLocal, но я не знаю, какой вектор использовать там. У вас есть идея, какой вектор должен быть параметром crossLocal? Так как это работает в самом начале, прежде чем я перемещу камеру, мне может понадобиться вектор с камеры, и тогда вектор будет таким же, как вектор движения для НЛО.   -  person Niklas R.    schedule 08.01.2014


Ответы (1)


Начиная с вашего последнего комментария, я публикую это как ответ (хотя я не совсем уверен, что использовать в качестве перекрестного вектора.

При извлечении поперечного вектора мы ищем перпендикуляр к прямой линии впереди корабля и вертикальную линию, перпендикулярную прямой линии, проходящую вертикально через центр корабля.

Я предполагаю, что направление является нашим вектором прямого направления, и в этом случае (независимо от вида) мы хотим пересечь его с вертикальной линией, проходящей через центр корабля. CrossLocal этих двух векторов будет перпендикулярной линией к обоим, идущей либо слева, либо справа от корабля (независимо от ориентации камеры или корабля).

для моего исправления кода я предполагаю, что craftSkewer — это воображаемый вертел, который проходит через центр корабля вертикально.

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

Я думаю, причина, по которой это работает изначально, связана с тем, что UNIT_Y возвращает 0. Но после перемещения корабля или камеры он пересчитывается неправильно?

person MaineCoder    schedule 08.01.2014
comment
Да. Но как вычислить craftSkewer ? Я использую класс Node из jmonkeyengine, и нет метода, который возвращает что-то вроде того, что мне здесь нужно. - person Niklas R.; 08.01.2014
comment
Вам нужно будет сохранить в памяти вектор, который представляет собой вертикаль, перпендикулярную прямому направлению, и обновлять его для всех движений. Или рассчитайте вертикальное положение на основе значений переднего положения и вращения корабля. Я думаю, что сохранить измененный вектор в памяти будет проще всего с текущим кодом. - person MaineCoder; 08.01.2014
comment
Ok. Но как для начала получить перпендикуляр к прямому направлению? Документ Vector3F: jmonkeyengine.org/doc/com/jme/math/ Vector3f.html И я нахожу 2 метода crossLocal и cross и не понимаю разницы. Я думаю, что должен быть такой метод, как getPerpendicular для Vector3f, и способ получить craftSkewer напрямую, но это сложно сделать. - person Niklas R.; 08.01.2014
comment
Cross возвращает крест, crossLocal модифицирует переданный объект. - person MaineCoder; 08.01.2014
comment
Хорошо, спасибо. Я думаю, что ищу что-то с понятием локальности по сравнению с направлением, которое мы получили. Если я получил direction, я ищу что-то вроде direction.localUp(), что, я думаю, является некоторым поворотом, не так ли? Если я поверну переменную direction на 90 градусов, а затем возьму крест из этих двух векторов? Но я до сих пор не знаю, как именно. - person Niklas R.; 08.01.2014
comment
Подойдя ближе к этому методу, я думаю, я бы поиграл с некоторыми разными конфигурациями и посмотрел, что работает (дайте мне знать, какое решение, ха-ха). Я только хотел бы пройти какие-нибудь курсы по 3D-дизайну и векторам, чтобы точно знать ответ... - person MaineCoder; 08.01.2014
comment
Спасибо! Это работает: direction.set(cam.getLeft()).multLocal(-speed * tpf); - person Niklas R.; 09.01.2014