首页 > 其他分享 >【Unity】CinemachineVirtualCamera:实现第一人称视角控制

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

时间:2024-09-24 11:03:36浏览次数:10  
标签:SerializeField float Unity private inputDir CinemachineVirtualCamera 第一人称 Input 

相机视角的控制,利用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;

    }
}

标签:SerializeField,float,Unity,private,inputDir,CinemachineVirtualCamera,第一人称,Input,
From: https://www.cnblogs.com/sitarblogs/p/18428715

相关文章

  • 【unity开发】以OPPO手机为例,如何连接安卓设备并调试unity程序
    1.有线调试下面全程以oppo手机为例:1.打开手机开发者模式设置->关于手机->版本信息->狂点版本号直到弹出提示打开开发者模式即可2.打开USB调试打开开发者模式之后在设置->其他设置->开发者选项->USB调试打开即可3.USB数据线连接使用USB数据线连接你的手机和电脑,并确保USB......
  • Android平台Unity3D下如何同时播放多路RTMP|RTSP流?
    技术背景好多开发者,提到希望在Unity的Android头显终端,播放2路以上RTMP或RTSP流,在设备性能一般的情况下,对Unity下的RTMP|RTSP播放器提出了更高的要求。实际上,我们在前几年发布Unity下直播播放模块的时候,就已经支持了Android多实例播放RTMP|RTSP,随着大家对这块的技术诉求和性能要求越......
  • 【Unity】UI、背景和3D的Camera和Canvas设置
    目前存在需求背景是指定的图片,该图片始终显示在页面中,不会因场景的视角操控发生尺寸等变化;UI内容显示在页面最上层,同样不会因场景的视角操控发生尺寸等变化,但是当软件整个尺寸发生变化时,会跟随变化,UI内容会覆盖3D物体;3D物体可以随着相机视角的变化而变近变远等,3D物体上可能存在......
  • 音视频生态下Unity3D和虚幻引擎(Unreal Engine)的区别
    技术背景好多开发者跟我们做技术交流的时候,会问我们,为什么有Unity3D的RTMP|RTSP播放模块,还有RTMP推送和轻量级RTSP服务模块,为什么不去支持虚幻引擎?二者区别在哪里?本文就Unity3D和虚幻引擎之间的差异,做个大概的分析,实际上,Unity3D和虚幻引擎(UnrealEngine)在游戏开发及其他相关领域都......
  • unity的移动方式
    关于unity获取外设的输出:Input.GetAxis 或Input.GetAxisRaw是Unity引擎中的一个方法,用于获取用户输入设备(如键盘、鼠标、游戏手柄等)的模拟值。它通常用于处理平滑的输入,比如移动、旋转和缩放。[GetAxis 返回一个介于-1和1之间的浮点数值,表示输入设备的状态。][如果......
  • Unity UI制作新的记录
    也是玩上unity了家人们。记录一下与ui界面斗智斗勇的半个月。TextMeshProtextMeshProUGUI.preferredHeight获取文本长度,便于修改滚动条长度或文本物体长度。TextMeshPro中的使用的文本asset可以在导入字体font文件之后右键->create->TextMeshPro->fontasset......
  • Unity编辑器扩展秘籍-实现一个可拖放的List
    usingSystem;usingSystem.Collections.Generic;usingUnityEditor;usingUnityEngine;usingObject=UnityEngine.Object;namespaceyaojz{publicclassDroppableListView{privateList<DroppableItem>_datas=newList<DroppableIt......
  • 一文详解Unity下RTMP推送|轻量级RTSP服务|RTSP|RTMP播放模块说明
    技术背景好多开发者,对Unity下的模块,不甚了解,实际上,除了Windows/Linux/Android/iOSNativeSDK,大牛直播SDK发布了Unity环境下的RTMP推流|轻量级RTSP服务(Windows平台+Linux平台+Android平台)和RTMP|RTSP直播播放(Windows、Linux、Android和iOS平台全覆盖)低延迟的解决方案。目前,大牛直播......
  • unity调用java静态方法
    在uni-app中使用NFC功能,可以通过调用微信小程序的NFCAPI(目前只在微信小程序中有效)。uni-app是多平台框架,而NFC功能的具体实现依赖于微信小程序的NFCAPI。以下是如何在uni-app中使用NFC功能,包括发现NFC设备、连接以及写入数据的详细步骤。​前置条件确保已经......
  • 【春秋招必看】Unity相关笔试面试题(内有完整答案)第二期
    欢迎来到光光的奇妙冒险,我是你们的煎饼光子老师。今天是我们的第二期笔试面试题总结。C#部分:1、请说说你认为C#中==和Equals的区别是什么?答案:(1).==是运算符,Equals是万物之父Object中的虚方法,子类可重写(2).Equals一般在子类中重写后用于比较两个对象中内容是否相同......