Unity 自由视角的惯性旋转

旋转视角

基础旋转

相机环绕角色旋转,使用RotateAround函数

视角水平旋转,以角色为圆心,以竖直向上的方向向量为旋转轴

视角上下旋转,从角色正面旋转到角色头顶,以角色为圆心,以相机左侧的方向向量为旋转轴,这种旋转之后,相机可能会倾斜,所以最后要使用LookAt函数修正,让相机正确地朝向角色

transform.RotateAround(focus.position, Vector3.up, anglex);
transform.RotateAround(focus.position, -transform.right, angley);
transform.LookAt(focus);      

限制转动视角

只需要限制视角的上下旋转。

设相机为C,角色为P,拿到一个待旋转角∠C'PC,计算 \underset{CP}{\rightarrow}\underset{CD}{\rightarrow} 的夹角,也就是∠PCD,再间接计算出∠CPQ,最后再判断∠C'PC+∠CPQ是否超出限制的范围即可。

currentAngleY = 90 - Vector3.Angle(-RelativePosition, Vector3.down);            //计算两点连线与水平方向的夹角
if (currentAngleY - angley > MaximumDegree || currentAngleY - angley < MinimumDegree)
    angley = 0;

通过鼠标拖拽速度来旋转视角

思路

按下鼠标左键时,每一帧都获取鼠标的位置,通过两帧之间的鼠标位置之差除以两帧之间的时间,即可求得两帧之间的鼠标速度。

将鼠标速度作为视角旋转的依据,将鼠标速度转换为视角旋转的角度,转化比率可自己设定。

代码

    float mouseVelocityX;
    float mouseVelocityY;
    Vector3? point1;
    //旋转每度,在一帧中需要的速度
    int DragVelocityPerAngle = 170;

    void DragToRotateView_Velocity()
    {
        if (Input.GetMouseButton(0))                //按下鼠标左键的每一帧都执行
        {
            var point2 = Input.mousePosition;
            if (point1 != null)
            {
                mouseVelocityX = -(point1.Value.x - point2.x) / Time.deltaTime;
                mouseVelocityY = -(point1.Value.y - point2.y) / Time.deltaTime;
            }

            point1 = point2;

            float anglex = mouseVelocityX / DragVelocityPerAngle;                   //将鼠标在屏幕上拖拽的速度转化为角度
            float angley = mouseVelocityY / DragVelocityPerAngle;

            currentAngleY = 90 - Vector3.Angle(-RelativePosition, Vector3.down);            //计算两点连线与水平方向的夹角

            if (currentAngleY - angley > MaximumDegree || currentAngleY - angley < MinimumDegree)
                angley = 0;

            transform.RotateAround(focus.position, Vector3.up, anglex);
            transform.RotateAround(focus.position, -transform.right, angley);

            transform.LookAt(focus);                    //如果没有这一句,摄像头转着转着就会歪

            RelativePosition = transform.position - focus.position;                 //更新相对位置
        }

        if(Input.GetMouseButtonUp(0))       //脱手瞬间
        {
            point1 = null;
        }
    }

基于速度的惯性旋转

实现方式

将惯性运动的初速度设定为鼠标左键松开时的鼠标移动速度,随后用协程实现

获取惯性运动的初速度

        if(Input.GetMouseButtonUp(0))       //脱手瞬间
        {
            point1 = null;

            inertialRotation = true;
            lastMouseVelocityX = mouseVelocityX;
            lastMouseVelocityY = mouseVelocityY;
            if (lastMouseVelocityX > maxlastMouseVelocityX) lastMouseVelocityX = maxlastMouseVelocityX;
            else if (lastMouseVelocityX < -maxlastMouseVelocityX) lastMouseVelocityX = -maxlastMouseVelocityX;

            if (lastMouseVelocityX > 0) isCounterClockwise = true;
            else if (lastMouseVelocityX < 0) isCounterClockwise = false;
            //print(lastMouseVelocityX);
        }

        if(inertialRotation==true)
            StartCoroutine("InertialRotation");     //通过协程来实现视角的惯性旋转,调用协程只有写在Update里并且在每一帧都被调用时才会继续执行

通过协程实现视角的惯性旋转

    bool inertialRotation = false;      //是否需要视角的惯性旋转
    float lastMouseVelocityX;           //脱手瞬间鼠标速度
    float lastMouseVelocityY;
    float maxlastMouseVelocityX = 3000; 
    bool isCounterClockwise;            //旋转方向
    IEnumerator InertialRotation()      //在旋转末尾补上一个逐渐减缓的惯性旋转
    {
        yield return null;

        float anglex = lastMouseVelocityX / DragVelocityPerAngle;                   //将鼠标在屏幕上拖拽的速度转化为角度
        float angley = lastMouseVelocityY / DragVelocityPerAngle;

        currentAngleY = 90 - Vector3.Angle(-RelativePosition, Vector3.down);            //计算两点连线与水平方向的夹角

        if (currentAngleY - angley > MaximumDegree || currentAngleY - angley < MinimumDegree + 10)
            angley = 0;

        lastMouseVelocityX -= lastMouseVelocityX * 0.08f;
        lastMouseVelocityY -= lastMouseVelocityY * 0.08f;

        if ((isCounterClockwise && (anglex < 1))||!isCounterClockwise && (anglex > -1))
        {
            StopCoroutine("InertialRotation");
            inertialRotation = false;
        }    
        transform.RotateAround(focus.position, Vector3.up, anglex/3);
        transform.RotateAround(focus.position, -transform.right, Mathf.Abs(angley/25));
        transform.LookAt(focus);
        RelativePosition = transform.position - focus.position;
    }

通过鼠标拖拽距离来旋转视角

思路

按下鼠标左键时,每一帧都获取鼠标的位置。

将两帧之间的鼠标拖拽距离作为视角旋转的依据,将距离转换为视角旋转的角度,转化比率可自己设定。

代码

    Vector3 Point1;
    Vector3 Point2;
    //旋转每度,在一帧中需要拖拽的距离
    int DragDistancePerAngle = 20;

    void DragToRotateView_Distance()
    {
        if (Input.GetMouseButton(0))                //按下鼠标左键的每一帧都执行
        {
            Point2 = Input.mousePosition;
            float dx = Point2.x - Point1.x;
            float dy = Point2.y - Point1.y;

            float anglex = dx / DragDistancePerAngle;                   //将鼠标在屏幕上拖拽的距离转化为角度
            float angley = dy / DragDistancePerAngle;

            currentAngleY = 90 - Vector3.Angle(-RelativePosition, Vector3.down);                    //计算两点连线与水平方向的夹角

            if (currentAngleY - angley > MaximumDegree || currentAngleY - angley < MinimumDegree)
                angley = 0;

            transform.RotateAround(focus.position, Vector3.up, anglex);
            transform.RotateAround(focus.position, -transform.right, angley);

            transform.LookAt(focus);                    //如果没有这一句,摄像头转着转着就会歪

            RelativePosition = transform.position - focus.position;                 //更新相对位置

            Point1 = Point2;
            Point2 = Vector3.zero;
        }
    }

基于鼠标拖拽距离的惯性旋转

按下鼠标瞬间记录拖拽起点,松开鼠标瞬间记录拖拽终点

获取两帧之间的拖拽距离

        if (Input.GetMouseButtonDown(0))                //按下鼠标左键的瞬间,记录起始位置
        {
            Point1 = Input.mousePosition;
            StartPoint = Point1;
        }

        if(Input.GetMouseButtonUp(0))
        {
            EndPoint = Input.mousePosition;
            if (Point1!=EndPoint)                       //鼠标无速度则不进行惯性旋转
                inertialRotation = true;
            dragX = EndPoint.x - StartPoint.x;
            dragY = EndPoint.y - StartPoint.y;
            if (dragX > maxdragX) dragX = maxdragX;
            else if (dragX < -maxdragX) dragX = -maxdragX;

            if (dragX > 0) isCounterClockwise = true;
            else if (dragX < 0) isCounterClockwise = false;
            print(dragX);
        }

        if (inertialRotation == true)
            StartCoroutine("InertialRotation2");

通过协程实现视角的惯性旋转


    Vector3 StartPoint;     //拖拽起点
    Vector3 EndPoint;       //拖拽终点
    float dragX;        //水平拖拽距离
    float dragY;        //垂直拖拽距离
    float maxdragX = 3000;
    float sactor = 10;  //惯性系数
    IEnumerator InertialRotation2()      //在旋转末尾补上一个逐渐减缓的惯性旋转
    {
        yield return null;

        float anglex = dragX / DragDistancePerAngle / sactor;                   //将鼠标在屏幕上拖拽的距离转化为角度
        float angley = dragY / DragDistancePerAngle / sactor;                   

        currentAngleY = 90 - Vector3.Angle(-RelativePosition, Vector3.down);            //计算两点连线与水平方向的夹角

        if (currentAngleY - angley > MaximumDegree || currentAngleY - angley < MinimumDegree + 10)
            angley = 0;


        dragX -= dragX * 0.05f;
        dragY -= dragY * 0.05f;

        print(dragX);

        if ((isCounterClockwise && (anglex < 1)) || !isCounterClockwise && (anglex > -1))
        {
            StopCoroutine("InertialRotation2");
            inertialRotation = false;
        }
        transform.RotateAround(focus.position, Vector3.up, anglex / 4);
        transform.RotateAround(focus.position, -transform.right, Mathf.Abs(angley/4));
        transform.LookAt(focus);
        RelativePosition = transform.position - focus.position;
    }

效果

完整代码

    /*-----------------调整视角------------------*/

    //相机旋转方案
    public CameraRotateBy cameraRotateBy = CameraRotateBy.MouseVelocity;
    public enum CameraRotateBy
    {
        MouseVelocity,
        Distance,
    }

    //最小水平夹角
    public float MinimumDegree = 0;
    //最大水平夹角
    public float MaximumDegree = 60;
    //两点连线与水平方向的夹角
    float currentAngleY;

    /*
        方案一
        以两帧之间的鼠标移动速度为依据进行旋转
    */

    float mouseVelocityX;
    float mouseVelocityY;
    Vector3? point1;
    //旋转每度,在一帧中需要的速度
    int DragVelocityPerAngle = 170;

    //脱手瞬间鼠标速度
    float lastMouseVelocityX;
    float lastMouseVelocityY;

    void DragToRotateView_Velocity()
    {
        if (Input.GetMouseButton(0))                //按下鼠标左键的每一帧都执行
        {
            var point2 = Input.mousePosition;
            if (point1 != null)
            {
                mouseVelocityX = -(point1.Value.x - point2.x) / Time.deltaTime;
                mouseVelocityY = -(point1.Value.y - point2.y) / Time.deltaTime;
            }

            point1 = point2;

            float anglex = mouseVelocityX / DragVelocityPerAngle;                   //将鼠标在屏幕上拖拽的速度转化为角度
            float angley = mouseVelocityY / DragVelocityPerAngle;

            currentAngleY = 90 - Vector3.Angle(-RelativePosition, Vector3.down);            //计算两点连线与水平方向的夹角

            if (currentAngleY - angley > MaximumDegree || currentAngleY - angley < MinimumDegree)
                angley = 0;

            transform.RotateAround(focus.position, Vector3.up, anglex);
            transform.RotateAround(focus.position, -transform.right, angley);

            transform.LookAt(focus);                    //如果没有这一句,摄像头转着转着就会歪

            RelativePosition = transform.position - focus.position;                 //更新相对位置
        }

        if(Input.GetMouseButtonUp(0))       //脱手瞬间
        {
            point1 = null;

            inertialRotation = true;
            lastMouseVelocityX = mouseVelocityX;
            lastMouseVelocityY = mouseVelocityY;
            if (lastMouseVelocityX > maxlastMouseVelocityX) lastMouseVelocityX = maxlastMouseVelocityX;
            else if (lastMouseVelocityX < -maxlastMouseVelocityX) lastMouseVelocityX = -maxlastMouseVelocityX;

            if (lastMouseVelocityX > 0) isCounterClockwise = true;
            else if (lastMouseVelocityX < 0) isCounterClockwise = false;
            //print(lastMouseVelocityX);
        }

        
        if(inertialRotation==true)
            StartCoroutine("InertialRotation");     //通过协程来实现视角的惯性旋转,调用协程只有写在Update里并且在每一帧都被调用时才会继续执行
    }

    bool inertialRotation = false;      //是否需要视角的惯性旋转
    float maxlastMouseVelocityX = 3000; 
    bool isCounterClockwise;            //旋转方向
    IEnumerator InertialRotation()      //在旋转末尾补上一个逐渐减缓的惯性旋转
    {
        yield return null;

        float anglex = lastMouseVelocityX / DragVelocityPerAngle;                   //将鼠标在屏幕上拖拽的速度转化为角度
        float angley = lastMouseVelocityY / DragVelocityPerAngle;

        currentAngleY = 90 - Vector3.Angle(-RelativePosition, Vector3.down);            //计算两点连线与水平方向的夹角

        if (currentAngleY - angley > MaximumDegree || currentAngleY - angley < MinimumDegree + 10)
            angley = 0;

        lastMouseVelocityX -= lastMouseVelocityX * 0.08f;
        lastMouseVelocityY -= lastMouseVelocityY * 0.08f;

        //print(lastMouseVelocityX);

        if ((isCounterClockwise && (anglex < 1))||!isCounterClockwise && (anglex > -1))
        {
            StopCoroutine("InertialRotation");
            inertialRotation = false;
        }    
        transform.RotateAround(focus.position, Vector3.up, anglex/3);
        transform.RotateAround(focus.position, -transform.right, Mathf.Abs(angley/25));
        transform.LookAt(focus);
        RelativePosition = transform.position - focus.position;
    }

    /*
        方案二
        以两帧之间的鼠标移动距离为依据进行旋转
    */

    Vector3 Point1;
    Vector3 Point2;
    //旋转每度,在一帧中需要拖拽的距离
    int DragDistancePerAngle = 20;

    void DragToRotateView_Distance()
    {
        float v = Input.GetAxis("Vertical");
        float h = Input.GetAxis("Horizontal");
        if (!(h==0&&v==0))                  //不运动时的旋转灵敏度
        {
            DragDistancePerAngle = 17;      //松手前拖拽灵敏度
            sactor = 10;                    //松手后拖拽灵敏度
        }
        else                                //运动时的旋转灵敏度
        {
            DragDistancePerAngle = 8;
            sactor = 4;
        }

        if (Input.GetMouseButtonDown(0))                //按下鼠标左键的瞬间,记录起始位置
        {
            Point1 = Input.mousePosition;
            StartPoint = Point1;
        }

        if (Input.GetMouseButton(0))                //按下鼠标左键的每一帧都执行
        {
            Point2 = Input.mousePosition;
            float dx = Point2.x - Point1.x;
            float dy = Point2.y - Point1.y;

            float anglex = dx / DragDistancePerAngle;                   //将鼠标在屏幕上拖拽的距离转化为角度
            float angley = dy / DragDistancePerAngle;

            currentAngleY = 90 - Vector3.Angle(-RelativePosition, Vector3.down);                    //计算两点连线与水平方向的夹角

            if (currentAngleY - angley > MaximumDegree || currentAngleY - angley < MinimumDegree)
                angley = 0;

            transform.RotateAround(focus.position, Vector3.up, anglex);
            transform.RotateAround(focus.position, -transform.right, angley);

            transform.LookAt(focus);                    //如果没有这一句,摄像头转着转着就会歪

            RelativePosition = transform.position - focus.position;                 //更新相对位置

            Point1 = Point2;
            Point2 = Vector3.zero;
        }

        if(Input.GetMouseButtonUp(0))
        {
            EndPoint = Input.mousePosition;
            if (Point1!=EndPoint)                       //鼠标无速度则不进行惯性旋转
                inertialRotation = true;
            dragX = EndPoint.x - StartPoint.x;
            dragY = EndPoint.y - StartPoint.y;
            if (dragX > maxdragX) dragX = maxdragX;
            else if (dragX < -maxdragX) dragX = -maxdragX;

            if (dragX > 0) isCounterClockwise = true;
            else if (dragX < 0) isCounterClockwise = false;
            print(dragX);
        }

        if (inertialRotation == true)
            StartCoroutine("InertialRotation2");
    }

    Vector3 StartPoint;     //拖拽起点
    Vector3 EndPoint;       //拖拽终点
    float dragX;        //水平拖拽距离
    float dragY;        //垂直拖拽距离
    float maxdragX = 3000;
    float sactor = 10;  //惯性系数
    IEnumerator InertialRotation2()      //在旋转末尾补上一个逐渐减缓的惯性旋转
    {
        yield return null;

        float anglex = dragX / DragDistancePerAngle / sactor;                   //将鼠标在屏幕上拖拽的距离转化为角度
        float angley = dragY / DragDistancePerAngle / sactor;                   

        currentAngleY = 90 - Vector3.Angle(-RelativePosition, Vector3.down);            //计算两点连线与水平方向的夹角

        if (currentAngleY - angley > MaximumDegree || currentAngleY - angley < MinimumDegree + 10)
            angley = 0;


        dragX -= dragX * 0.05f;
        dragY -= dragY * 0.05f;

        print(dragX);

        if ((isCounterClockwise && (anglex < 1)) || !isCounterClockwise && (anglex > -1))
        {
            StopCoroutine("InertialRotation2");
            inertialRotation = false;
        }
        transform.RotateAround(focus.position, Vector3.up, anglex / 4);
        transform.RotateAround(focus.position, -transform.right, Mathf.Abs(angley/4));
        transform.LookAt(focus);
        RelativePosition = transform.position - focus.position;
    }

猜你喜欢

转载自blog.csdn.net/weixin_43673589/article/details/123297435