9.相机控制类

上一个教程实现了相机类,而相机的控制是在Controller命名空间下的FpsCameraController类和FreeCameraController类实现的。

FpsCameraController类的代码如下,具体解释可参见2.3 创建一个第一人称射击游戏(FPS)的相机:Quake风格的相机。主要原理就是在这个类中改变camera.pose.Rotation和camera.pose.Position,而在CameraSceneNode类中根据这两个变量创建视矩阵。

namespace StunEngine.Controllers
{
    /// <summary>
    /// 控制一个相机对象。
    /// 使用鼠标控制相机上下左右旋转,键盘控制前进后退左右平移。
    /// 相机不能翻滚,只在水平面xz上移动。
    /// </summary>
    public class FpsCameraController : Controller
    {
        构造函数和成员变量

        /// <summary>
        /// 获取或设置相机允许俯仰的最大角度。
        /// </summary>
        public float Elevation
        {
            get { return elevation; }
            set { elevation = value; }
        }
        
        /// <summary>
        /// 主控制逻辑
        /// </summary>
        /// <param name="gameTime"></param>
        public override void ControllerAction(GameTime gameTime)
        {
            if (camera == null)
            {
                camera = (CameraSceneNode)controlledNode;
                //将鼠标初始位置设置为屏幕中央
                //将鼠标初始位置设置为屏幕中央
                Mouse.SetPosition(engine.GraphicsDevice.Viewport.Width / 2, engine.GraphicsDevice.Viewport.Height / 2);
                originalMouseState = Mouse.GetState();            
            }
            float timeDifference = (float)gameTime.ElapsedGameTime.TotalMilliseconds / 1000.0f;
            ProcessInput(timeDifference);
        }

        private void ProcessInput(float ellapsedTime)
        {
            //相机的上下旋转
            float cameraPitch = 0;
            //相机的左右旋转
            float cameraYaw = 0;
            //前进后退
            float movement = 0;
            //左右平移
            float stride = 0;
            //相机每帧旋转和移动距离
            float rotationPerFrame = ellapsedTime * camera.RotationSpeed;
            float movementPerFrame = ellapsedTime * camera.MovementSpeed;

            if (engine.IsActive)
            {
                //获取鼠标的当前状态
                MouseState currentMouseState = Mouse.GetState();
                
                //如果鼠标发生移动,则通过鼠标控制相机的俯仰和左右旋转
                if (currentMouseState != originalMouseState)
                {
                    //获取鼠标在水平和竖直方向的移动大小
                    float xDifference = currentMouseState.X - originalMouseState.X;
                    float yDifference = currentMouseState.Y - originalMouseState.Y;
                    
                    //水平方向的移动控制相机的左右旋转
                    cameraYaw += rotationPerFrame * xDifference;
                    //竖直方向的移动控制相机的上下旋转
                    cameraPitch -= rotationPerFrame * yDifference;
                    
                    //将光标重新设置到屏幕的中央
                    Mouse.SetPosition(originalMouseState.X, originalMouseState.Y);
                }
            }

            //键盘控制相机的前后左右移动
            if (Input.KeyboardUpPressed|| Input.Keyboard .IsKeyDown (Keys.W))
                movement += movementPerFrame;
            if (Input.KeyboardDownPressed|| Input.Keyboard .IsKeyDown(Keys.S))
                movement -= movementPerFrame;
            if (Input.KeyboardRightPressed|| Input.Keyboard.IsKeyDown(Keys.D))
                stride += movementPerFrame;
            if (Input.KeyboardLeftPressed || Input.Keyboard.IsKeyDown(Keys.A))
                stride -= movementPerFrame;            

            //如果相机旋转或移动
            if (cameraYaw != 0 || cameraPitch != 0 || movement != 0 || stride != 0)
            {
                //-------------------------
                //  当相机旋转时                        
                //-------------------------
                if (cameraYaw != 0 || cameraPitch != 0)
                {
                    //isDirty设为true表示需要更新相机
                    isDirty = true;
                    
                    //将相机的俯仰限制在elevation范围之内
                    if (Math.Abs(cameraPitch + camera.AngleRotation.X) > elevation)
                        cameraPitch = Math.Sign(cameraPitch) * (elevation - Math.Abs(camera.AngleRotation.X));
                    
                    //基于cameraPitch, cameraYaw设置相机的旋转。
                    if (cameraYaw != 0 || cameraPitch != 0)
                    {
                        Vector3 angleRotation = new Vector3(cameraPitch, cameraYaw, 0);
                        camera.AngleRotation += angleRotation;
                        camera.pose.Rotation = Quaternion.CreateFromYawPitchRoll(MathHelper.ToRadians(-camera.AngleRotation.Y), MathHelper.ToRadians(camera.AngleRotation.X), 0);
                    }
                }

                //--------------------
                //  当相机前后移动时
                //--------------------
                if (movement != 0)
                {
                    //isDirty设为true表示需要更新相机
                    isDirty = true;

                    //计算前进后退的矢量
                    Vector3 forwardMove = camera.GetMoveForwardVector(movement);
                    camera.pose.Position += forwardMove;                    
                }

                //--------------------
                //  当相机左右移动时
                //--------------------
                if (stride != 0)
                {
                    //isDirty设为true表示需要更新相机
                    isDirty = true;
                    
                    //计算左右平移的矢量
                    Vector3 strideMove = CameraSceneNode.GetMoveVector(Quaternion.CreateFromAxisAngle(Vector3.Up, MathHelper.ToRadians(-camera.AngleRotation.Y)), Vector3.Right, stride*0.5f);
                    camera.Pose.Position += strideMove;
                }

                //更新相机
                if (isDirty)
                {                    
                    camera.UpdateViewMatrix(false);
                    isDirty = false;
                }
            } 
        }
    }
}

FreeCameraController类的代码如下,具体解释可参见2.4 创建一个Freelancer风格的相机:使用四元数的3D旋转。大部分代码与FpsCameraController相同,不同之处在于键盘的左右键控制相机的左右旋转而不是左右平移,按照鼠标左键不放也可以控制相机的上下左右旋转,按Q和E键还可以绕Z轴滚动。

namespace StunEngine.Controllers
{
    /// <summary>
    /// 自由相机的控制器。
    /// </summary>
    public class FreeCameraController : Controller
    {
        构造函数和成员变量

        /// <summary>
        /// 主控制逻辑。
        /// </summary>
        /// <param name="gameTime"></param>
        public override void ControllerAction(GameTime gameTime)
        {
            if (camera == null)
            {
                camera = (CameraSceneNode)controlledNode;
                //将鼠标初始位置设置为屏幕中央
                Mouse.SetPosition(engine.GraphicsDevice.Viewport.Width / 2, engine.GraphicsDevice.Viewport.Height / 2);
                originalMouseState = Mouse.GetState();                
            }

            float timeDifference = (float)gameTime.ElapsedGameTime.TotalMilliseconds / 1000.0f;
            ProcessInput(timeDifference);
        }

        private void ProcessInput(float ellapsedTime)
        {
            //相机是否旋转
            bool isRotation = false;
            //相机的滚动
            float cameraRoll = 0;
            //相机的俯仰
            float cameraPitch = 0;
            //相机的左右旋转
            float cameraYaw = 0;
            //相机的移动
            float movement = 0;

            ////相机每帧旋转和移动距离
            float rotationPerFrame= ellapsedTime *camera.RotationSpeed  ;
            float movementPerFrame = ellapsedTime * camera.MovementSpeed;

            if (engine.IsActive)
            {
                MouseState currentMouseState = Mouse.GetState();
                //如果按住鼠标左键并且鼠标发生移动
                if (Input.MouseLeftButtonPressed && currentMouseState != originalMouseState)
                {
                    //获取鼠标在水平和竖直方向的移动大小
                    float xDifference = currentMouseState.X - originalMouseState.X;
                    float yDifference = currentMouseState.Y - originalMouseState.Y;

                    //水平方向的移动控制相机的左右旋转
                    cameraYaw += rotationPerFrame * xDifference;
                    //竖直方向的移动控制相机的上下旋转
                    cameraPitch -= rotationPerFrame * yDifference;

                    //将光标重新设置到屏幕的中央
                    Mouse.SetPosition(originalMouseState.X, originalMouseState.Y);
                }
            }

            //键盘控制相机的前后左右移动和翻滚
            if (Input.KeyboardUpPressed || Input.Keyboard.IsKeyDown(Keys.W))
                movement += movementPerFrame;
            if (Input.KeyboardDownPressed || Input.Keyboard.IsKeyDown(Keys.S))
                movement -= movementPerFrame;
            if (Input.KeyboardRightPressed || Input.Keyboard.IsKeyDown(Keys.D))
                cameraYaw += rotationPerFrame;
            if (Input.KeyboardLeftPressed || Input.Keyboard.IsKeyDown(Keys.A))
                cameraYaw -= rotationPerFrame;
            if (Input.Keyboard.IsKeyDown(Keys.Q))
                cameraRoll -= rotationPerFrame;
            if (Input.Keyboard.IsKeyDown(Keys.E))
                cameraRoll += rotationPerFrame;

            //如果相机旋转
            if (cameraRoll != 0 || cameraPitch != 0 || cameraYaw != 0)
            {
                Vector3 angleRotation = new Vector3(cameraPitch, cameraYaw, cameraRoll );
                camera.AngleRotation += angleRotation;
                //设置相机的旋转
                camera.pose.Rotation = Quaternion.CreateFromYawPitchRoll(MathHelper.ToRadians(-camera.AngleRotation.Y), MathHelper.ToRadians(camera.AngleRotation.X), MathHelper.ToRadians(camera.AngleRotation.Z));
                
                //isDirty设为true表示需要更新相机
                isDirty = true;
                isRotation = true;
            }

            //如果相机移动
            if (movement != 0)
            {
                //isDirty设为true表示需要更新相机
                isDirty = true;

                //设置相机的位置
                camera .pose .Position +=camera.GetMoveForwardVector(movement);
            }
            
            if (isDirty)
            {
                camera.UpdateViewMatrix(false );
                isDirty = false;
            }
        }
    }
}

发布时间:2010/1/14 下午3:33:38  阅读次数:6483

2006 - 2024,推荐分辨率 1024*768 以上,推荐浏览器 Chrome、Edge 等现代浏览器,截止 2021 年 12 月 5 日的访问次数:1872 万 9823 站长邮箱

沪 ICP 备 18037240 号-1

沪公网安备 31011002002865 号