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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • python中plot实现即时数据动态显示方法[通俗易懂]

    python中plot实现即时数据动态显示方法[通俗易懂]在Matlab使用Plot函数实现数据动态显示方法总结中介绍了两种实现即时数据动态显示的方法。考虑到使用python的人群日益增多,再加上本人最近想使用python动态显示即时的数据,网上方法很少,固总结于此。示例代码importmatplotlib.pyplotaspltimportnumpyasnpimporttimefrommathimport*plt.ion()

    2022年6月26日
    37
  • BZOJ3503:[CQOI2014]和谐矩阵(高斯消元,bitset)

    BZOJ3503:[CQOI2014]和谐矩阵(高斯消元,bitset)

    2022年4月2日
    47
  • 字符串匹配的kmp算法_多字符串匹配

    字符串匹配的kmp算法_多字符串匹配一、背景  给定一个主串(以S代替)和模式串(以P代替),要求找出P在S中出现的位置,此即串的模式匹配问题。  Knuth-Morris-Pratt算法(简称KMP)是解决这一问题的常用算法之一,这个算法是由高德纳(DonaldErvinKnuth)和沃恩·普拉特在1974年构思,同年詹姆斯·H·莫里斯也独立地设计出该算法,最终三人于1977年联合发表。  在继…

    2022年8月21日
    9
  • linux(10)linux vi/vim

    linux(10)linux vi/vim前言所有的UnixLike系统都会内建vi文书编辑器,其他的文书编辑器则不一定会存在。但是目前我们使用比较多的是vim编辑器。vim具有程序编辑的能力,可以主动的以字体颜色辨别语法的

    2022年8月6日
    3
  • java接口详解

    java接口详解Java基础——接口一、接口的概述:官方解释:Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。我的解释:接口可以理解为一种特殊的类,里面全部是由全局常量和公共的抽象方法所组成。接口是解决Java无法使用多继承的一种手段,但是接口在实际中更多的作用是制定标准的。或者我们可以直接把接口理解为100%的抽象类,既接口中的方法必须全部是抽象方法。(JDK1.8之前可以这样理解)二、接

    2022年7月7日
    30
  • swift uiwebview 用cookie 来实现自动登录

    swift uiwebview 用cookie 来实现自动登录

    2022年3月12日
    42

发表回复

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

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