通常,在FPS游戏中,玩家可以自由地上下左右旋转观察,这些运动对应绕Up(y轴)和Right(x轴)的旋转,或更具体地说,绕(0,1,0)和(1,0,0)方向的旋转。但是绕z轴的旋转是不被允许的,这会导致玩家向左或向右弯曲脖子,在第一人称游戏中不使用,除非玩家被击中倒在地板上。矩阵的乘法顺序是很重要的,因为当你组合两个旋转时,第二个旋转轴会被第一个旋转所改变。
简单地说,需要存储两个变量:绕Up方向的旋转量和绕Right方向的旋转量。然后,当需要计算总的旋转量时,使用“在绕Up旋转之后绕Right旋转”组合两个旋转。输入方面,使用鼠标旋转相机,使用键盘移动相机,再将输入与旋转量联系起来。有了前面的基础,这里就容易多了。
先看Initialize()
1: protected override void Initialize()
2: {
3: // TODO: Add your initialization logic her
4: updownRot = 0.0f;//上下旋转角度
5: rightleftRot = 0.0f;//左右旋转角度
6: rotSpeed = 0.05f;//旋转速率
7: moveSpeed = 0.05f;//移动速率
8: Mouse.SetPosition(Window.ClientBounds.Width / 2, Window.ClientBounds.Height / 2);
9: originalMState = Mouse.GetState();
10:
11:
12: float fov = MathHelper.PiOver4;
13: float aspect = GraphicsDevice.DisplayMode.AspectRatio;
14: float nearClip = 0.5f;
15: float farClip = 100.0f;
16: projMat = Matrix.CreatePerspectiveFieldOfView(fov, aspect, nearClip, farClip);
17:
18: camPosition = new Vector3(0, 2, 10);//相机初始位置
19: originalForward = new Vector3(0, 0, -1);//初始方向
20: originalUp = new Vector3(0, 1, 0);
21:
22: UpadteViewMatrix();
23: base.Initialize();
24: }
然后是UpdateViewMatrix()
1: private void UpadteViewMatrix()
2: {
3: cameraRotation = Matrix.CreateRotationX(updownRot) * Matrix.CreateRotationY(rightleftRot);
4:
5: rotatedForward = Vector3.Transform(originalForward, cameraRotation);
6: rotatedUp = Vector3.Transform(originalUp, cameraRotation);
7:
8: finalLook = camPosition + rotatedForward;
9: //finalUp = camPosition + rotatedUp;
10: finalUp = rotatedUp;
11:
12: viewMat = Matrix.CreateLookAt(camPosition, finalLook, finalUp);
13: }
旋转矩阵设在UpdateViewMatrix中是因为updownRot和rightleftRot是经常变化的。对于finalLook,上一次记录中已经讲过了。但有没有注意到,
finalUp = rotatedUp。这是因为FPS相机中,不是任意旋转的。finalUp是一个方向,而finalLook是一个视点,rotatedForward才是方向。也就是说
finalUp和rotatedUp都是原始方向进过一次旋转变换而来,而finalLook是需要Position和rotatedForward叠加而成。有点啰嗦了。
再是Update()
1: protected override void Update(GameTime gameTime)
2: {
3: // Allows the game to exit
4: if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
5: this.Exit();
6:
7: // TODO: Add your update logic here
8: currentMState = Mouse.GetState();
9: if (currentMState != originalMState)
10: {
11: float xdiffence = currentMState.X - originalMState.X;
12: float ydiffence = currentMState.Y - originalMState.Y;
13:
14: rightleftRot -= rotSpeed * xdiffence;
15: updownRot -= rotSpeed * ydiffence;
16:
17: Mouse.SetPosition(Window.ClientBounds.Width/2, Window.ClientBounds.Height/2);
18: UpadteViewMatrix();
19: }
20:
21: kState = Keyboard.GetState();
22: if (kState.IsKeyDown(Keys.W))
23: MoveCameraPosition(Vector3.Forward);
24: else if (kState.IsKeyDown(Keys.S))
25: MoveCameraPosition(Vector3.Backward);
26: else if (kState.IsKeyDown(Keys.A))
27: MoveCameraPosition(Vector3.Left);
28: else if (kState.IsKeyDown(Keys.D))
29: MoveCameraPosition(Vector3.Right);
30:
31: base.Update(gameTime);
32: }
因为旋转量由鼠标控制。鼠标往右,即人往右看,原本在屏幕中央的物体就往左移动(逆时针)。鼠标往左,即人往左看,原本在屏幕中央的物体就往右移动(顺时针)。而到底旋转多少,由偏离屏幕中央的距离决定。旋转量(updownRot和rightleftRot)变化之后,就是更新观察矩阵(调用UpdateViewMatrix)。旋转矩阵会影响观察矩阵的视点和Up方向,从而影响平移的方向。所以关于键盘的操作在调用UpdateViewMatrix之后。
接下来是MoveCameraPosition()
1: private void MoveCameraPosition(Vector3 direction)
2: {
3: //Vector3 originalForward = new Vector3(-1.7f, -1, -1);
4: Vector3 rotatedForward = Vector3.Transform(direction, cameraRotation);
5: camPosition += moveSpeed * rotatedForward;
6: UpadteViewMatrix();
7:
8: }
和上一个记录一样。