unity 的Cinemachine组件运用

unity 的Cinemachine组件运用1.第三人称视角控制通过PackageManager安装CineMachine1) 最简单的方法使用freeLook虚拟相机常用的调整为:1.观察目标:将要看的目标放在这里。2输入控制:把你想用来控制的虚拟轴(就是InputManager里的)的名字输入进去就行。默认是填mouse那个输入轴。注意:似乎不支持NewInputSystem。所以在用NewInputSystem时要么用在projectSetting/player里改成both设置。要么自己写脚本去调用这个组件中的

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

1.第三人称视角控制

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

常用的调整为:
1.观察目标:
在这里插入图片描述

将要看的目标放在这里。
2输入控制:
在这里插入图片描述
把你想用来控制的虚拟轴(就是InputManager里的)的名字输入进去就行。默认是填mouse那个输入轴。
注意:似乎不支持New InputSystem。所以在用New InputSystem时要么用在projectSetting/player里改成both设置。
在这里插入图片描述
要么自己写脚本去调用这个组件中的Input Axis Value值

//第三人称相机
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里将子物体的世界坐标的旋转值强制改为这个脚本中的值。实现效果如下:
在这里插入图片描述
实现的主要脚本如下:

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添加一个脚本代码如下:

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里设定同级的系统函数在不同脚本时的调用顺序。
    在这里插入图片描述

  2. 其实跟Cinemachine没什么关系,人物用刚体移动时,刚体要用插值(interpolate)否则会造成相机抖动。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

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

(0)
上一篇 2022年5月12日 下午9:00
下一篇 2022年5月12日 下午9:00


相关推荐

  • ramdisk tmpfs_RAMDISK

    ramdisk tmpfs_RAMDISK原文地址:http://blog.csdn.net/michaelwubo/article/details/47418639为了自己方便记忆学习,将自己重点地方进行标注,感谢作者!目录第一部分:ramfs、tmpfs、rootfs、ramdisk一、什么是ramfs二、什么是tmpfs三、什么是rootfs四、什么是ramdisk第二部分:initrd、in

    2026年4月14日
    6
  • python清理浏览器缓存_以编程方式清除边缘浏览器缓存

    python清理浏览器缓存_以编程方式清除边缘浏览器缓存我已经尝试这么做了好几天,并在堆栈溢出和许多其他网站没有运气。在基本上,我需要清除MicrosoftEdgeBrowser的缓存和Internet临时文件。我已经在我自己的电脑上找到了这个文件夹的位置,但是路径在每台电脑上都会动态变化。我不能每次都让程序清除相同的路径,因为路径会随每次安装而变化。在以下是缓存中的文件示例:C:\Users\patdj\AppData\Local\Package…

    2022年7月18日
    20
  • 基于卷积神经网络的手写数字识别系统_python 卷积神经网络

    基于卷积神经网络的手写数字识别系统_python 卷积神经网络前面讲解了使用纯numpy实现数值微分和误差反向传播法的手写数字识别,这两种网络都是使用全连接层的结构。全连接层存在什么问题呢?那就是数据的形状被“忽视”了。比如,输入数据是图像时,图像通常是高、长、通道方向上的3维形状。但是,向全连接层输入时,需要将3维数据拉平为1维数据。实际上,前面提到的使用了MNIST数据集的例子中,输入图像就是1通道、高28像素、长28像素的(1,28,28)形状,但却被排成1列,以784个数据的形式输入到最开始的Affine层。图像是3维形状,这个形状中应该含有重要的空间信

    2025年11月17日
    6
  • Override ListView getAdapter造成的后果

    Override ListView getAdapter造成的后果

    2021年11月23日
    47
  • Java核心技术之什么是泛型

    Java核心技术之什么是泛型没看过官网,不知道类型擦除会产生的问题还敢说自己了解泛型?

    2022年6月16日
    26
  • 免费的天气预报API–谷歌,雅虎,中央气象台

    免费的天气预报API–谷歌,雅虎,中央气象台

    2021年12月4日
    74

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号