unity 的Cinemachine组件运用

2022-08-27 10:15:30 浏览数 (1)

大家好,又见面了,我是你们的朋友全栈君。

1.第三人称视角控制

通过Package Manager 安装CineMachine 1) 最简单的方法使用freeLook虚拟相机

常用的调整为: 1.观察目标:

将要看的目标放在这里。 2输入控制:

把你想用来控制的虚拟轴(就是InputManager里的)的名字输入进去就行。默认是填mouse那个输入轴。 注意:似乎不支持New InputSystem。所以在用New InputSystem时要么用在projectSetting/player里改成both设置。

要么自己写脚本去调用这个组件中的Input Axis Value值

代码语言:javascript复制
//第三人称相机
public CinemachineFreeLook thridPersonVCam;
…
thridPersonVCam.m_XAxis.m_InputAxisValue = mov.x; //x轴旋转
thridPersonVCam.m_YAxis.m_InputAxisValue = mov.y; //y轴旋转
…

2) 是我在一个项目中实现的方法: 参考了unity官方视频:https://www.bilibili.com/video/BV1Xa4y1j7iP 就是先让虚拟摄像机看向角色身上的子物体,玩家通过控制子物体的旋转来控制虚拟摄像机的朝向。但是要解决一个问题,就是子物体会随着父物体一起旋转的问题。视频中的解决方法是在移动或射击时强制将角色转向视角方向,同时将视点子物体的yz轴local的旋转值置零。 但是我是想实现个能在移动是也能自由观察的相机,所以采用了一个更简单但可能更耗性能的方法,就是在脚本内部另外保存一个实际子物体应该的世界坐标下的旋转值。在每次的lateUpdate里将子物体的世界坐标的旋转值强制改为这个脚本中的值。实现效果如下:

实现的主要脚本如下:

代码语言:javascript复制
public class ViewController : MonoBehaviour
{ 
   
    [Tooltip("相机左右旋转速度")] public float rotSpeedLR = 100f;
    //用来给调整灵敏度的UI进行最大最小值的限制
    [Tooltip("相机左右旋转最大速度")] public float maxRotSpeedLR = 360f;
    [Tooltip("相机左右旋转最小速度")] public float minRotSpeedLR = 90f;
    [Tooltip("左右是否反向")] public bool LRInvert;
    [Tooltip("相机上下旋转速度")] public float rotSpeedUD = 50f;
    //用来给调整灵敏度的UI进行最大最小值的限制
    [Tooltip("相机上下旋转最大速度")] public float maxRotSpeedUD = 360f;
    [Tooltip("相机上下旋转最小速度")] public float minRotSpeedUD = 90f;
    [Tooltip("上下是否反向")] public bool UDInvert = true;
    [Tooltip("相机向上限值")] public float upRange = 40f;
    [Tooltip("相机向下限值")] public float downRange = -15f;
    //视点子物体
    public Transform viewPoint;
    //实际playerViewPoint的旋转
    [SerializeField]private Vector3 playerViewPointRotation;

    private void Start()
    { 
   
        //开始时将视角转向同角色方向
        playerViewPointRotation = transform.eulerAngles;
    }

    void LateUpdate()
    { 
   
        TrdViewControl();
    }

    /// <summary>
    /// 第三人称视角控制
    /// </summary>
    void TrdViewControl()
    { 
   
        //计算实际旋转分量
        var rotVector = new Vector3((UDInvert ? 1 : -1) * rotSpeedUD, (LRInvert ? 1 : -1) * rotSpeedLR, 0);
        Vector3 rotation = rotVector * viewInput * Time.deltaTime;
        playerViewPointRotation  = rotation;
        var angleX = playerViewPointRotation.x;
        //clamp限制垂直方向的角度
        if (angleX > upRange)
        { 
   
            angleX = upRange;
        }
        if (angleX < downRange)
        { 
   
            angleX = downRange;
        }
        playerViewPointRotation.x = angleX;
        //会在lateUpdate里实时地改变视点的方向来改变视角
        viewPoint.eulerAngles = playerViewPointRotation;
    }

    //視角改变量
    private Vector2 viewInput;
    /// <summary>
    /// 视角输入的控制函数
    /// </summary>
    /// <param name="context"></param>
    public void OnViewControl(InputAction.CallbackContext context)
    { 
   
        var tempInput = context.ReadValue<Vector2>();
        viewInput = new Vector2(tempInput.y, tempInput.x).normalized;
    }

}

与freelook虚拟相机相比:实现上麻烦了许多,但相应的修改自由度就比较高,可以应用于其他的跟随和朝向的算法。

2.锁定相机

是想做一个类似塞尔达旷野之息锁定视角。

首先想到cinemachine的Target Group Camera。 但是用了下感觉偏向于固定方向的多目标锁定,不能让玩家自己旋转视角(估计也可以实现,但没什么好的想法)。 关于Target Group Camera设置可以参考以下博客: https://www.pianshen.com/article/63141639747/ 锁定的相机似乎是绕着角色和目标做一个椭圆的轨道旋转。 于是用MixingCamera,将主角的相机和一个绕着目标的相机融合,形成一个偏椭圆的轨道。 效果如下:

黄色为最终混合的相机轨迹,蓝色为目标相机的轨道,可见有两个点要实现:

  1. 目标相机的轨道大小要随着角色和玩家的距离改变,targetOffset = playerOffset dis
  2. 目标相机的视角角度要同角色相机。 实现的步骤如下: 1.创建mixingCamera相机 2.删除默认子相机,保留一个Orbital transposer跟随算法的子相机作为目标相机。 注意:由于目标相机用的是Orbital transposer跟随算法,默认打开Recenter to target Heading。

我们要关掉它,否则目标相机会一直想回正,导致相机抖动。 3. 为mixingCamera添加一个脚本代码如下:

代码语言:javascript复制
using Cinemachine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class LockVcamController : MonoBehaviour
{ 
   
    private Transform target;
    private CinemachineMixingCamera lockVcam;
    //当前相机的组件
    private CinemachineVirtualCamera currentVcam;
    private CinemachineTransposer currentVcamTrans;
    //角色身上视点
    private Transform viewPoint;
    //目标相机的组件
    private CinemachineVirtualCamera targetVcam;
    private CinemachineOrbitalTransposer lockTargetObi;
    //判断是否锁定
    private bool _isLock;

    private void Awake()
    { 
   
        //获取当前混合相机控件
        lockVcam = GetComponent<CinemachineMixingCamera>();
        //克隆一个当前的玩家虚拟相机,并放在混合相机下
        currentVcam = Instantiate(GameObject.Find("PlayerVcam"), transform).GetComponent<CinemachineVirtualCamera>();
        currentVcamTrans = currentVcam.GetCinemachineComponent<CinemachineTransposer>();
        //获取viewPoint
        viewPoint = currentVcam.Follow;
        //获取锁定目标的圆形轨道相机组件
        targetVcam = lockVcam.ChildCameras[0] as CinemachineVirtualCamera;
        lockTargetObi = targetVcam.GetCinemachineComponent<CinemachineOrbitalTransposer>();
    }

    /// <summary>
    /// 开始锁定并设定锁定目标
    /// </summary>
    /// <param name="target">锁定目标</param>
    /// <returns></returns>
    public bool SetLock(Transform target,int priority)
    { 
   
        this.target = target;
        if (this.target == null)//如果没目标就返回失败
            return false;
        //将目标相机的跟随和朝向设为指定目标
        targetVcam.Follow = target;
        targetVcam.LookAt = target;
        lockVcam.Priority = priority   1;
        _isLock = true;
        return true;
    }

    /// <summary>
    /// 解锁
    /// </summary>
    public void Unlock()
    { 
   
        lockVcam.Priority = lockVcam.Priority - 2;
        _isLock = false;
    }

    private void LateUpdate()
    { 
   
        
        if (_isLock)
        { 
   
            if (target == null)
                Unlock();
            else
                SetObi();
        }
        
    }
    /// <summary>
    /// 设置目标相机的轨道
    /// </summary>
    private void SetObi()
    { 
   
        var tempPos = viewPoint.position;
        tempPos.y = target.position.y;
        var dis = Vector3.Distance(tempPos, target.position)   Mathf.Abs(currentVcamTrans.m_FollowOffset.z);
        lockTargetObi.m_FollowOffset.z = -dis;//都是从后向前看与玩家设置保持一致
        lockTargetObi.m_XAxis.Value = viewPoint.eulerAngles.y;//将target相机的方向同视点方向
        //如果是用freeLook相机,就要去获取freelook中组件也有个m_XAxis,lockTargetObi.m_XAxis = TPVcam.m_XAis;
    }
}

之后只要去调用脚本中的SetLock()方法,把目标和优先级传进去就可以了。

3. cinemachine的分屏

主要是翻译了cinemachine官方文档。 https://docs.unity3d.com/Packages/com.unity.cinemachine@2.6/manual/CinemachineMultipleCameras.html 分为4步: 1) 首先添加player层。有几个player就要分几个层。

2) 添加对应个数的unityCamera(不是虚拟相机),并添加各自的cinemachineBrain组件 3) 设置每个unityCamera的cullingMask,把除了本相机对应的层的其它之前添加的层取消。例如:P1Cam的话就把P2层取消。 4) 添加各个分屏对应的虚拟相机,虚拟相机要设置到相应的层里。 5) 修改各个unityCamera里的viewportRect,确定要在屏幕里显示的范围形成分屏效果。 最后效果如下:

其他使用上的注意:

  1. 注意cinemachine的调用顺序: 想我这样在脚本中有视角控制相关的脚本,如果出现相机抖动,主要是相同的update系列的函数cinemachine里的先调用。可以在projectSetting里设定同级的系统函数在不同脚本时的调用顺序。
  1. 其实跟Cinemachine没什么关系,人物用刚体移动时,刚体要用插值(interpolate)否则会造成相机抖动。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/145048.html原文链接:https://javaforall.cn

0 人点赞