Inertial rotation of Unity free camera

Rotate the angle of view

base rotation

The camera rotates around the character, using the RotateAround function

The angle of view is rotated horizontally, with the character as the center, and the vertical upward direction vector as the rotation axis

The angle of view is rotated up and down, from the front of the character to the top of the character's head, with the character as the center, and the direction vector on the left side of the camera as the rotation axis. After this rotation, the camera may be tilted, so finally use the LookAt function to correct it to make the camera correct. towards the role

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

Limit rotation angle

Just limit the up and down rotation of the viewing angle.

Set the camera as C and the role as P, get an angle to be rotated ∠C'PC, calculate  the\underset{CP}{\rightarrow} angle between and , which is ∠PCD, then indirectly calculate ∠CPQ, and finally judge whether ∠C'PC+∠CPQ exceeds the limit \underset{CD}{\rightarrow}range is sufficient.

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

Rotate the camera with mouse drag speed

ideas

When the left button of the mouse is pressed, the position of the mouse is obtained in each frame, and the mouse speed between the two frames can be obtained by dividing the difference between the mouse positions between the two frames by the time between the two frames.

The mouse speed is used as the basis for viewing angle rotation, and the mouse speed is converted into the angle of viewing angle rotation. The conversion ratio can be set by yourself.

code

    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;
        }
    }

Inertial rotation based on velocity

Method to realize

Set the initial speed of inertial motion to the mouse movement speed when the left mouse button is released, and then implement it with a coroutine

Get the initial velocity of inertial motion

        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里并且在每一帧都被调用时才会继续执行

Inertial rotation of the viewing angle through coroutines

    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;
    }

Rotate the view by dragging the distance with the mouse

ideas

Get the mouse position every frame when the left mouse button is pressed.

The mouse drag distance between two frames is used as the basis for the rotation of the viewing angle, and the distance is converted into the angle of rotation of the viewing angle. The conversion ratio can be set by yourself.

code

    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;
        }
    }

Inertial rotation based on mouse drag distance

Press the mouse to record the drag start point, release the mouse to record the drag end point

Get the drag distance between two frames

        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");

Inertial rotation of the viewing angle through coroutines


    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;
    }

Effect

full code

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

    //相机旋转方案
    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;
    }

Guess you like

Origin blog.csdn.net/weixin_43673589/article/details/123297435