Libgdx第一人称相机控制

6
我刚开始尝试在libgdx中制作3D游戏。我已经知道了如何绘制基本的Model,并尝试使用CameraController进行操作。现在我想创建一个FirstPersonCamera或者FirstPersonCameraController。我考虑扩展PerspectiveCamera并向其添加一个MyMovingObject target。这个MyMovingObject会保存一个x, y, z position,其中y是一个常量值,因为我目前无法移动上/下。所以我的移动基本上是在2D平面内进行的。这个MyMovingObject还将存储left/right rotation,这是它的moving direction/xSpeed, zSpeed所需的。但是Player也应该能够上下看,而这个上下旋转对于MyMovingObject并不是真正需要的,因为它只改变视角而不影响其他属性。所以我不确定我是否走了正确的路线。
我希望通过使用W,A,S,D控制前进、向左、向右、向后,并通过鼠标左右旋转来实现左右旋转,同时通过鼠标上下移动来实现上下查看,就像大多数第一人称游戏一样。
我应该使用另一种方式,而不是通过扩展PerspectiveCamera创建自己的相机吗? 或者这种方法很好,我只需要将上下旋转也存储在MyMovingObject中,即使它仅用于视角?
还是更好的方法是使用W,A,S,D和Mouse来控制相机,并根据相机的位置和旋转更新MyMovingObject的位置?
希望你能理解我的意思。对我来说,解释起来有点复杂。

编辑: 我现在正在为我的NPC和玩家使用Vector3 directionVector3 positionVector3 size。我通过执行以下操作计算速度:xSpeed = direction.x / (direction.x + direction.z) * speed; zSpeed类似。通过这样做,我将y值“过滤”出来,得到x和y的百分比。唯一的问题是,当我直视天空时,x和z都是0。我可以通过使用一个“UpVecotr”,在进行“俯仰旋转”时将其旋转来修复它。但是如何旋转它?我需要围绕侧向量旋转它。谢谢。

编辑:现在旋转和移动都正常了(请参见我的回答),但是我在“俯仰旋转”的限制方面遇到了真正的大问题。我正在使用:if (direction.y < 0.9 && angle > 1) doPitchRotation(); else if (direction.y > -0.9 && angle < 1) doPitchRotation(); 所以如果我向下旋转并且我仍然看向至少-0.9y,它就不执行旋转。但真正发生的事情是:我旋转到-0.9,然后围绕Y轴旋转,在另一侧向上旋转,即使我向下移动鼠标也是如此。你能解释一下为什么吗?为什么当我向下看时Y轴翻转了呢? 编辑:现在它正常工作了。似乎我的upVector有时会出现错误值。对于基于陆地的摄像机,您还可以使用Y轴和方向向量的叉积。不需要upVector。
3个回答

6
嘿,谢谢您分享这个链接:这里。我发现非常有用。 以下是我的代码,旋转陆地相机似乎没有问题。
private int mouseX = 0;
private int mouseY = 0;
private float rotSpeed = 0.2f;

@Override
public boolean mouseMoved(int screenX, int screenY) {
    int magX = Math.abs(mouseX - screenX);
    int magY = Math.abs(mouseY - screenY);

    if (mouseX > screenX) {
        cam.rotate(Vector3.Y, 1 * magX * rotSpeed);
        cam.update();
    }

    if (mouseX < screenX) {
        cam.rotate(Vector3.Y, -1 * magX * rotSpeed);
        cam.update();
    }

    if (mouseY < screenY) {
        if (cam.direction.y > -0.965)
            cam.rotate(cam.direction.cpy().crs(Vector3.Y), -1 * magY * rotSpeed);
        cam.update();
    }

    if (mouseY > screenY) {

        if (cam.direction.y < 0.965)
            cam.rotate(cam.direction.cpy().crs(Vector3.Y), 1 * magY * rotSpeed);
        cam.update();
    }

    mouseX = screenX;
    mouseY = screenY;

    return false;
}

这适用于陆地相机。如果您想制作飞行控制相机,则必须围绕cam.direction.crs(cam.up)进行俯仰旋转。不要使用Vector3.cpy(),而是应该存储一个Vector3 help,它获取这些临时值,因为Vector3.cpy()会创建一个新的Vector3,并且此操作会在每个渲染循环中执行。 对于飞行控制相机,还需要添加roll旋转,并在cam.up向量周围进行yaw旋转。

好的回答。只有两个建议: 1)Vector3.cpy()似乎会创建一个new Vector3()并将其赋值为其他Vecotr3的值。在每个渲染循环中创建新对象不是很好。因此,我会使用一个帮助函数Vector3,它可以获取临时值。 2)对于陆地相机,您也可以围绕方向.crs(Vector3.Y)进行俯仰旋转,据我所知。我应该编辑您的答案吗?然后我会标记它为已接受! - Robert P

3

这篇文章 在我看来真的很有帮助。我找到了一个应该有效的解决方案,但我还没有尝试过。我的MovingObject全部都有一个Vector3 positionVector3 directionVector3 sizeVecotr3 upVectorPlayer类继承了这个MovingObject类,并添加了MouseKeycontroll控制移动。 在MovingObject类中,我有以下方法:

  1. rotateYaw(float degrees): rotates the Vector3 direction arround the Y-Axis by the given degrees (libgdx has a rotate function for Vector3)--> Simple
  2. rotatePitch(float degrees): rotates the Vector3 direction arround the: direction.cross(Vector3.Y), which is the rotated side Vector of your MovingObject, by the given degrees. Also a Pitch-Rotation has to rotate the upVector, so you rotate the upVector arround the same axis, by the given degrees. As soon as you understand this it is simple.
  3. move(delta) moves your MovingObject in x,z direction by doing:

    if (direction.y == 1) {
      // You are looking straight up, no x,z direction, move in the opposite
      // direction of upVector
      xSpeed = upVector.x / (Math.abs(upVetor.x) + Math.abs(upVector.z)) * (-speed);
      zSpeed = upVector.z / (Math.abs(upVetor.x) + Math.abs(upVector.z)) * (-speed);
      position.add(xSpeed * delta, 0, ySpeed * delta);
    } else if (direction.y == -1) {
      // You are looking straight down, no x,z direction, move in the direction of
      // upVector
      xSpeed = upVector.x / (Math.abs(upVetor.x) + Math.abs(upVector.z)) * speed;
      zSpeed = upVector.z / (Math.abs(upVetor.x) + Math.abs(upVector.z)) * speed;
      position.add(xSpeed * delta, 0, ySpeed * delta);
    } else {
      // You are not looking straight up or down, so you have x,z direction. Use
      // that.
      xSpeed = direction.x / (Math.abs(direction.x) + Math.abs(direction.z)) * speed;
      zSpeed = direction.z / (Math.abs(direction.x) + Math.abs(direction.z)) * speed;
      position.add(xSpeed * delta, 0, ySpeed * delta);
    }
    
我现在还没有测试过,但我认为它应该可以工作。注意,在Pitch-rotation中,你也应该将其限制为直上/直下。通过检查x和z的符号来做到这一点。如果在进行Pitch-rotation时它们发生变化,那么你旋转了超过90度。
我仍在等待其他答案,如果我错了,请纠正我!
编辑:我已经测试过了。它像这样工作,但需要注意以下几点:
1. direction.cross(upVector)会改变direction向量。所以要先将数据存储在某个地方!在使用后重置方向向量。 2. Pitch限制有一个问题:如果你控制符号变化,就像我建议的那样,会出现以下情况:你向上看,signum x和signum z都是0。你往下看,符号改变,你的操作(限制)开始。因此,要确保你还检查它是否为零。 3. 每当你修改任何内容时,都要考虑归一化你的direction和upVector!
我认为这应该能够很好地工作。如果你有任何改进,请让我知道,我会在这里更新。如果你有另外的解决方案,请添加一个答案!谢谢

3

我知道这个问题已经有很好的答案了。但是我对所选答案有些问题。我只想帮助寻找相同解决方案的人们。 我注意到所选答案存在一些奇怪的行为。它不能保持Y轴向上,而在FPS游戏中这当然非常重要。所以这个答案并不完美,但我还是想把它放在这里。

// put into the create() method.
Gdx.input.setInputProcessor(new InputProcessor() {
        private int dragX, dragY;
        float rotateSpeed = 0.2f;
        // dont' forget to override other methods.
        @Override
        public boolean mouseMoved(int screenX, int screenY) {
            Vector3 direction = cam.direction.cpy();

            // rotating on the y axis
            float x = dragX -screenX;
            // change this Vector3.y with cam.up if you have a dynamic up.
            cam.rotate(Vector3.Y,x * rotateSpeed);

            // rotating on the x and z axis is different
            float y = (float) Math.sin( (double)(dragY -screenY)/180f);
            if (Math.abs(cam.direction.y + y * (rotateSpeed*5.0f))< 0.9) {
                cam.direction.y +=  y * (rotateSpeed*5.0f) ;
            }

            cam.update();
            dragX = screenX;
            dragY = screenY;
            return true;
        }

    });

注意:不要使用任何摄像机控制器。
注意2:此代码可能会有用:Gdx.input.setCursorCatched(true);

编辑:我想分享一下我使用的行走功能,该功能可使用wasd键改变相机位置。当按下W键时,forward为true。当松开键时,forward为false。其他方向也是同样的原理。要检测按键按下和松开,请在上面的代码中使用InputProcessor
这将在二维空间(X-Z轴)中创建移动。相机的方向不会改变运动方向,因为我们消除了方向的Y轴。即使相机指向天空(与地面呈非90度角),方向向量的长度也不固定为1.0f。因此,不会失去移动速度。
为了测试这一点,我将相机上下旋转(向前或向后移动鼠标),方向向量的x和z值没有发生变化。因此,当方向向量的y轴被消除时,我们得到一个二维方向向量,不受相机的Y角度影响。

private void walking(float timeElapsed) {
        float speed = movementSpeed;
        if ((forward | back) & (right | left)) {
            speed /= Math.sqrt(2);
        }
        System.out.println(speed);
        if (forward) {
            Vector3 v = cam.direction.cpy();
            v.y = 0f;
            v.x *= speed * timeElapsed;
            v.z *= speed * timeElapsed;
            cam.translate(v);
            cam.update();
        }
        if (back) {
            Vector3 v = cam.direction.cpy();
            v.y = 0f;
            v.x = -v.x;
            v.z = -v.z;
            v.x *= speed * timeElapsed;
            v.z *= speed * timeElapsed;
            cam.translate(v);
            cam.update();
        }
        if (left) {
            Vector3 v = cam.direction.cpy();
            v.y = 0f;
            v.rotate(Vector3.Y, 90);
            v.x *= speed * timeElapsed;
            v.z *= speed * timeElapsed;
            cam.translate(v);
            cam.update();
        }
        if (right) {
            Vector3 v = cam.direction.cpy();
            v.y = 0f;
            v.rotate(Vector3.Y, -90);
            v.x *= speed * timeElapsed;
            v.z *= speed * timeElapsed;
            cam.translate(v);
            cam.update();

        }
    }

1
谢谢你的回答。但我认为上轴不应该与y轴相同。想一想:你直视向上。这意味着,你的方向向量、上向量和y轴都是相同的。当按下前进键时,你怎么知道往哪里移动?因此,在进行“俯仰旋转”时,我也会旋转上向量,并使用y轴进行“偏航旋转”。所以当我直视向上时,上向量指向我的后面,这样我就知道按前进键时往哪里移动了。 - Robert P
但是如果你直视上方呢?v.x和v.y不是都会变成0吗?那么你就没有速度了,对吧? - Robert P
@Springrbua 你好。我明白你不需要俯仰和偏航。经典射击游戏没有这个(例如CS)。我尝试了被选中的答案,但它出现了意外的俯仰和偏航问题。因此,我写了这个方法。这个方法的唯一目的是防止出现俯仰、扭曲等情况。 - ossobuko
@Springrbua 它不允许你向上看90度(只有81度)。而且你不能直接向上看。如果你走得太远,相机就会颠倒过来。 - ossobuko
@Springrbua 如果你想要投影,相机可以围绕方向向量旋转。我没有测试过这个功能。 - ossobuko
好的,如果将俯仰旋转限制在<90°内,那么就应该没问题了。感谢您的回答,我想这可能会帮助未来的某个人。+1 - Robert P

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接