Как заставить камеру следить за двумя объектами?

Я делаю свою первую 3D-игру, и это что-то вроде streetfighter/tekken. Я видел, как работают некоторые режимы камеры, т.е. Чейзкам и Камнод. Чего я не знаю, так это того, как заставить камеру следить за обоими игроками одновременно. Я хочу, чтобы камера увеличивала масштаб, когда игроки находятся рядом друг с другом, и уменьшала масштаб, когда их не было.

Буду признателен за любую помощь с идеями или возможными решениями.

Спасибо


person Celly    schedule 26.11.2013    source источник


Ответы (1)


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

Куда должна смотреть камера

Это проще всего определить, камера должна смотреть на среднее положение двух объектов. Как только вы узнаете, что можете установить, куда смотрит камера, используя

Vector3f boxsCentre=box1.getWorldTranslation().add(box2.getWorldTranslation()).mult(0.5f);
cam.lookAt(boxsCentre, Vector3f.UNIT_Y);

Где должна быть камера

Где должна быть камера, более сложно. Вы знаете, что это должно быть где-то на линии, которая проходит от центра двух объектов в направлении, перпендикулярном линии между этими двумя объектами. К счастью, перекрестное произведение дает нам это. Мы хотим, чтобы камера всегда находилась на одном уровне с объектами, поэтому, пересекая вектор разделения вектором, идущим прямо вверх, мы получаем эту перпендикулярную линию.

Vector3f seperationVector=box2.getWorldTranslation().subtract(box1.getWorldTranslation());
Vector3f perpendicularFromTheAction= seperationVector.cross(Vector3f.UNIT_Y);

perpendicularFromTheAction.normalizeLocal();

Итак, это дает нам линию, но где на линии мы должны разместить камеру. Я только что поэкспериментировал с этим и обнаружил, что удвоенное расстояние между объектами выглядит красиво, поэтому

float distance=2*seperationVector.length();
Vector3f newCameraLocation=boxsCentre.add(perpendicularFromTheAction.mult(distance));

Затем вы можете установить положение камеры

cam.setLocation(newCameraLocation);

Собираем все вместе

Я использовал этот код вместе с двумя блоками, которые движутся по циклу, чтобы продемонстрировать это, как вы можете видеть, вы получаете желаемый эффект.

import com.jme3.app.SimpleApplication;
import com.jme3.material.Material;
import com.jme3.math.*;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;

public class FightTest extends SimpleApplication {

    Geometry box1;
    Geometry box2;
    public static void main(String[] args) {
        FightTest app = new FightTest();
        app.start();
    }

    @Override
    public void simpleInitApp() {

        //put in some reference boxes
        for(int i=-20;i<=20;i+=20){
            for(int j=-20;j<=20;j+=20){
                if (j!=0||i!=0){
                    Geometry referenceBox = createBox(ColorRGBA.Red);
                    referenceBox.setLocalTranslation(i, 0, j);
                    rootNode.attachChild(referenceBox);
                }

            }
        }

        //put in our two players
        box1 = createBox(ColorRGBA.Blue);
        box1.setLocalTranslation(5, 0, 0);

        box2 = createBox(ColorRGBA.Green);

        rootNode.attachChild(box1);
        rootNode.attachChild(box2);
    }

    private Geometry createBox(ColorRGBA color){

        Box b = new Box(Vector3f.ZERO, 1, 1, 1);
        Geometry box = new Geometry("Box", b);

        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        mat.setColor("Color", color);
        box.setMaterial(mat);

        return box;
    }

    @Override
    public void simpleUpdate(float tpf) {
        adjustCam();
        movePlayers(tpf);
    }

    @Override
    public void simpleRender(RenderManager rm) {
        //TODO: add render code
    }

    private void adjustCam(){
        //we want our camera to look at the centre of the boxes
        Vector3f boxsCentre=box1.getWorldTranslation().add(box2.getWorldTranslation()).mult(0.5f);

        cam.lookAt(boxsCentre, Vector3f.UNIT_Y);

        //we also want our camera to move closer/further away as the boxes seperate.
        //and move around so its always parallel to the action

        //parallel to the action means on the line given by the cross product of the
        //box seperation and the upwards vector

        Vector3f seperationVector=box2.getWorldTranslation().subtract(box1.getWorldTranslation());
        Vector3f perpendicularFromTheAction= seperationVector.cross(Vector3f.UNIT_Y);

        perpendicularFromTheAction.normalizeLocal();

        //we could (and you should) get complicated on exactly how far the camera should 
        //move backwards, but I'm just going to make the camera twice as far away as the 
        //objects are seperated 
        float distance=2*seperationVector.length();

        Vector3f newCameraLocation=boxsCentre.add(perpendicularFromTheAction.mult(distance));

        cam.setLocation(newCameraLocation);
    }

    float timeAccumulator=0; 
    private void movePlayers(float tpf){
        //basic movement, just for demo
        timeAccumulator+=tpf;

        if (timeAccumulator<2){
            box1.move(new Vector3f(5f*tpf,0,0));
            box2.move(new Vector3f(0,0,5f*tpf));
        }else if (timeAccumulator<4){
            box1.move(new Vector3f(-5f*tpf,0,0));
            box2.move(new Vector3f(0,0,-5f*tpf));
        }else{
             timeAccumulator=0;
        }

    }
}
person Richard Tingle    schedule 27.11.2013
comment
Спасибо за ваш ответ. пока я ждал, я сделал это почти так же, за исключением того, что я сделал узел и привязал camNode к узлу с помощью setControlDir(CameraControl.ControlDirection.SpatialToCamera); Затем используется node.setLocalTranslation(midwayDistance, fixedYvalue, midwayDistance+fixedZvalue). Не уверен, что это хороший способ, но он работает, хотя и нуждается в настройке, чтобы сделать его более плавным. Я попробую ваше решение и посмотрю, как оно работает. - person Celly; 28.11.2013
comment
@Celina Это тоже сработает. Надеюсь, я ответил вовремя, чтобы быть чем-то полезным! - person Richard Tingle; 28.11.2013
comment
Кстати, для чего нужен вложенный цикл в initApp? О, и почему у меня не работает @Richard?? - person Celly; 28.11.2013
comment
@Celina Когда я изначально делал это, вы не могли видеть автоматическое вращение камеры на черном фоне. Петля просто вставляет неподвижные красные кубики, чтобы создать ощущение перспективы. - person Richard Tingle; 28.11.2013
comment
Разве box1.setLocalTranslation(box1.getLocalTranslation().add(new Vector3f(5f*tpf,0,0))); не просто box1.move(5f*tpf,0,0);? - person pat; 03.02.2014