【Unity】CinemachineVirtualCamera:实现第一人称视角控制

相机视角的控制,利用CinemachineVirtualCamera插件(在packageManager中下载)实现键盘和鼠标控制第一人称视角。WASD前进后退向左向右,QE左右旋转;鼠标滚轮控制远近、俯仰和升降。

另外还支持鼠标靠近边缘移动、鼠标拖拽等控制方式。

成果展示

Scene部分

主相机增加CinemachineBrain组件;

增加空物体CinemachineVirtualCamera,并绑定CinemachineVirtualCamera组件,参数如下:

增加空物体CameraSystem,并绑定脚本,脚本的参数如下:

脚本部分

CameraSystem脚本内容

CinemachineVirtualCamera插件中可以改变的参数很多,不同的参数对应不同的视角改变效果,这里仅提及少数几个参数。

public class CameraSystem : MonoBehaviour
{
    [SerializeField] private CinemachineVirtualCamera cinemachineVirtualCamera;

    [SerializeField] private Transform inputTranform;
    private TextMeshProUGUI tipText;


    [SerializeField] private float moveSpeed = 20f;
    [SerializeField] private float rotateSpeed = 200f;
    [SerializeField] private float liftingSpeed = 20f;

    [SerializeField] private bool useEdgeScrolling = false;
    [SerializeField] private bool useDragPan = true;
    [SerializeField] private CameraZoomPara cameraZoomPara = CameraZoomPara.FieldOfView;


    [SerializeField] private float fieldOfViewMin = 10f;
    [SerializeField] private float fieldOfViewMax = 150f;
    [SerializeField] private float followOffsetMin = 5f;
    [SerializeField] private float followOffsetMax = 50f;
    [SerializeField] private float followOffsetMinY = 10f;
    [SerializeField] private float followOffsetMaxY = 50f;
    [SerializeField] private float followOffsetMinZ = 10f;
    [SerializeField] private float followOffsetMaxZ = 50f;
    [SerializeField] private float orthographicSizeMinY = 10f;
    [SerializeField] private float orthographicSizeMaxY = 50f;
    [SerializeField] private float cameraSystemMinY = 0f;
    [SerializeField] private float cameraSystemMaxY = 30f;

    private bool dragPanMoveAction;
    private Vector2 lastMousePosition;

    private float targetFieldOfView = 50f;
    private Vector3 followOffset;
    private float targetOrthographicSize = 20f;


    //可以改变的参数
    private enum CameraZoomPara
    {
        FieldOfView,
        MoveForward,
        LowerY,
        LowerZ,
        OrthographicSize,
        CameraSystemY
    }

    private void Awake()
    {
        followOffset = cinemachineVirtualCamera.GetCinemachineComponent<CinemachineTransposer>().m_FollowOffset;
        tipText = inputTranform.Find("tip").GetComponent<TextMeshProUGUI>();
        tipText.SetText("放缩");
    }

    private void Update()
    {
        HandleCameraMovement();
        //鼠标接近边缘控制前后左右
        if (useEdgeScrolling)
        {
            HandleCameraMovementEdgeScrolling();
        }
        //鼠标右键拖拽控制前后左右
        if (useDragPan)
        {
            HandleCameraMovementdragPan();
        }

        HandleCameraRotation();

        HandleCameraZoom();
    }

    private void GetNextCameraZoom(CameraZoomPara para)
    {
        string tip = "";
        switch (para)
        {
            case CameraZoomPara.FieldOfView:
                cameraZoomPara = CameraZoomPara.LowerY;
                tip = "useAngle";
                tipText.SetText("俯仰");
                break;
            case CameraZoomPara.LowerY:
                cameraZoomPara = CameraZoomPara.CameraSystemY;
                tip = "useLifting";
                tipText.SetText("升降");
                break;
            case CameraZoomPara.CameraSystemY:
                cameraZoomPara = CameraZoomPara.FieldOfView;
                tip = "useZoom";
                tipText.SetText("放缩");
                break;
        }
        UtilsClass.CreateWorldTextPopup(tip, Mouse3D.GetMouseWorldPosition());
    }

    private void HandleCameraZoom()
    {
        //空格键切换滚轮的控制模式
        if (Input.GetKeyUp(KeyCode.Space))
        {
            GetNextCameraZoom(cameraZoomPara);

        }
        switch (cameraZoomPara)
        {
            case CameraZoomPara.FieldOfView:
                HandleCameraZoom_FieldOfView();
                break;
            //case CameraZoomPara.MoveForward:
            //    HandleCameraZoom_MoveForward();
            //    break;
            case CameraZoomPara.LowerY:
                HandleCameraZoom_LowerY();
                break;
            case CameraZoomPara.CameraSystemY:
                HandleCameraZoom_CrmeraSystemY();
                break;
                //case CameraZoomPara.LowerZ:
                //    HandleCameraZoom_LowerZ();
                //    break;
                //case CameraZoomPara.OrthographicSize:
                //    HandleCameraZoom_OrthographicSize();
                //    break;
                //default:
                //    break;
        }

    }

    private void HandleCameraZoom_FieldOfView()
    {
        if (Input.mouseScrollDelta.y > 0)
        {
            targetFieldOfView -= 5;
        }
        if (Input.mouseScrollDelta.y < 0)
        {
            targetFieldOfView += 5;
        }

        targetFieldOfView = Mathf.Clamp(targetFieldOfView, fieldOfViewMin, fieldOfViewMax);
        float zoomSpeed = 10f;
        cinemachineVirtualCamera.m_Lens.FieldOfView = Mathf.Lerp(cinemachineVirtualCamera.m_Lens.FieldOfView, targetFieldOfView, Time.deltaTime * zoomSpeed);
    }

    private void HandleCameraZoom_MoveForward()
    {
        Vector3 zoomDir = followOffset.normalized;

        float zoomAmount = 3f;
        if (Input.mouseScrollDelta.y > 0)
        {
            followOffset -= zoomDir * zoomAmount;
        }
        if (Input.mouseScrollDelta.y < 0)
        {
            followOffset += zoomDir * zoomAmount;
        }

        if (followOffset.magnitude < followOffsetMin)
        {
            followOffset = zoomDir * followOffsetMin;
        }
        if (followOffset.magnitude > followOffsetMax)
        {
            followOffset = zoomDir * followOffsetMax;
        }
        float zoomSpeed = 10f;
        cinemachineVirtualCamera.GetCinemachineComponent<CinemachineTransposer>().m_FollowOffset
            = Vector3.Lerp(cinemachineVirtualCamera.GetCinemachineComponent<CinemachineTransposer>().m_FollowOffset, followOffset, Time.deltaTime * zoomSpeed);
    }

    private void HandleCameraZoom_LowerY()
    {
        float zoomAmount = 3f;
        if (Input.mouseScrollDelta.y > 0)
        {
            followOffset.y -= zoomAmount;
        }
        if (Input.mouseScrollDelta.y < 0)
        {
            followOffset.y += zoomAmount;
        }

        followOffset.y = Mathf.Clamp(followOffset.y, followOffsetMinY, followOffsetMaxY);

        float zoomSpeed = 10f;
        cinemachineVirtualCamera.GetCinemachineComponent<CinemachineTransposer>().m_FollowOffset
            = Vector3.Lerp(cinemachineVirtualCamera.GetCinemachineComponent<CinemachineTransposer>().m_FollowOffset, followOffset, Time.deltaTime * zoomSpeed);
    }
    private void HandleCameraZoom_LowerZ()
    {
        float zoomAmount = 3f;
        if (Input.mouseScrollDelta.y > 0)
        {
            followOffset.z -= zoomAmount;
        }
        if (Input.mouseScrollDelta.y < 0)
        {
            followOffset.z += zoomAmount;
        }

        followOffset.z = Mathf.Clamp(followOffset.z, followOffsetMinZ, followOffsetMaxZ);

        float zoomSpeed = 10f;
        cinemachineVirtualCamera.GetCinemachineComponent<CinemachineTransposer>().m_FollowOffset
            = Vector3.Lerp(cinemachineVirtualCamera.GetCinemachineComponent<CinemachineTransposer>().m_FollowOffset, followOffset, Time.deltaTime * zoomSpeed);
    }

    private void HandleCameraZoom_OrthographicSize()
    {
        if (Input.mouseScrollDelta.y > 0)
        {
            targetOrthographicSize -= 5;
        }
        if (Input.mouseScrollDelta.y < 0)
        {
            targetOrthographicSize += 5;
        }

        targetOrthographicSize = Mathf.Clamp(targetOrthographicSize, orthographicSizeMinY, orthographicSizeMaxY);

        float zoomSpeed = 10f;
        cinemachineVirtualCamera.m_Lens.OrthographicSize
            = Mathf.Lerp(cinemachineVirtualCamera.m_Lens.OrthographicSize, targetOrthographicSize, Time.deltaTime * zoomSpeed);
    }

    private void HandleCameraZoom_CrmeraSystemY()
    {
        Vector3 inputDir = new Vector3(0, 0, 0);
        if (Input.mouseScrollDelta.y > 0)
        {
            inputDir.y = -1f;
        }
        if (Input.mouseScrollDelta.y < 0)
        {
            inputDir.y = +1f;
        }

        Vector3 moveDir = transform.up * inputDir.y;
        Vector3 targetPosition = (transform.position + moveDir * liftingSpeed * Time.deltaTime);
        targetPosition.y = Mathf.Clamp(targetPosition.y, cameraSystemMinY, cameraSystemMaxY);
        transform.position = targetPosition;

    }

    private void HandleCameraMovementEdgeScrolling()
    {
        Vector3 inputDir = new Vector3(0, 0, 0);
        int edgeScrollSize = 20;
        if (Input.mousePosition.x < edgeScrollSize)
        {
            inputDir.x = -1f;
        }
        if (Input.mousePosition.y < edgeScrollSize)
        {
            inputDir.z = -1f;
        }
        if (Input.mousePosition.x > Screen.width - edgeScrollSize)
        {
            inputDir.x = +1f;
        }
        if (Input.mousePosition.y > Screen.height - edgeScrollSize)
        {
            inputDir.z = +1f;
        }

        Vector3 moveDir = transform.forward * inputDir.z + transform.right * inputDir.x;
        transform.position += moveDir * moveSpeed * Time.deltaTime;
    }

    private void HandleCameraMovementdragPan()
    {
        Vector3 inputDir = new Vector3(0, 0, 0);
        if (Input.GetMouseButtonDown(1))
        {
            dragPanMoveAction = true;
            lastMousePosition = Input.mousePosition;
        }
        if (Input.GetMouseButtonUp(1))
        {
            dragPanMoveAction = false;
        }

        if (dragPanMoveAction)
        {
            Vector2 mouseMovementDelta = (Vector2)Input.mousePosition - lastMousePosition;
            float dragPanSpeed = 2f;
            inputDir.x = mouseMovementDelta.x * dragPanSpeed;
            inputDir.z = mouseMovementDelta.y * dragPanSpeed;

            lastMousePosition = Input.mousePosition;
        }
        Vector3 moveDir = transform.forward * inputDir.z + transform.right * inputDir.x;
        float dragMoveSpeed = moveSpeed / 4;
        transform.position = Vector3.Lerp(transform.position, transform.position - moveDir * dragMoveSpeed * Time.deltaTime, dragMoveSpeed * Time.deltaTime);
    }

    private void HandleCameraRotation()
    {
        float rotateDir = 0f;
        if (Input.GetKey(KeyCode.Q)) rotateDir += +1f;
        if (Input.GetKey(KeyCode.E)) rotateDir += -1f;


        transform.eulerAngles -= new Vector3(0, rotateDir * rotateSpeed * Time.deltaTime, 0);
    }

    private void HandleCameraMovement()
    {
        Vector3 inputDir = new Vector3(0, 0, 0);
        if (Input.GetKey(KeyCode.W)) inputDir.z += +1f;
        if (Input.GetKey(KeyCode.S)) inputDir.z += -1f;
        if (Input.GetKey(KeyCode.A)) inputDir.x += -1f;
        if (Input.GetKey(KeyCode.D)) inputDir.x += +1f;


        Vector3 moveDir = transform.forward * inputDir.z + transform.right * inputDir.x;
        transform.position += moveDir * moveSpeed * Time.deltaTime;

    }
}
posted @ 2024-09-24 11:05  Sitar  阅读(29)  评论(0编辑  收藏  举报