Cinemachine API 第三人称跟随案例与参数调节

3 篇文章 0 订阅
1 篇文章 0 订阅

Cinemachine API

这几天简单研究了一下 Cinemachine Free Look,在网上看了不少相关文章,但是关于通过脚本使用 Free Look 的参考所见不多。因此,通过整理学习,简单实现了一下第三人称跟随效果和通过 UI 调整相机参数的功能,一些参考和用到的实际代码如下。

CinemachineFreeLook属性详解
Cinemachine free look使用建议
官方文档
Cinemachine 论坛

CinemachineVirtualCamera

private CinemachineVirtualCamera  virtualCamera;
public void Init()
{
	Camera.main.gameObject.AddComponent<CinemachineBrain>(); //主相机需要添加CinemachineBrain组件对虚拟相机进行管理
	GameObject virtualCameraObj = new GameObject("[VirtualCamera]");
	CinemachineVirtualCamera  virtualCamera = virtualCameraObj.AddComponent<CinemachineVirtualCamera>(); //创建一个虚拟相机
}
public void SetCinemachineTarget(Transform unitTrans)
{
	virtualCamera.transform.position = unitTrans.position; 
	virtualCamera.Follow = unitTrans; //设置虚拟相机的跟随对象
	virtualCamera.LookAt = unitTrans; //设置虚拟相机的看向对象
	virtualCamera.m_Lens.FieldOfView = 40; //设置虚拟相机的FOV
	var transposer =  virtualCamera.AddCinemachineComponent<CinemachineTransposer>(); //添加Transposer组件,用于目标跟随
	var composer = virtualCamera.AddCinemachineComponent<CinemachineComposer>(); //添加Composer组件,用于看向目标
	transposer.m_FollowOffset = new Vector3(8,1,0); //设置跟随偏移
	composer.m_TrackedObjectOffset = new Vector3(1,1,1); //设置看向偏移
}

CinemachineFreeLook

public CinemachineFreeLook frelookCamera; //第三人称跟随相机
public int rotateDelayID; //延时ID
public int xRecoverSpeed = 250;
public void Init()
{
	Camera.main.gameObject.AddComponent<CinemachineBrain>();
	GameObject virtualCameraObj = new GameObject("[VirtualCamera]");
	frelookCamera = virtualCameraObj.AddComponent<CinemachineFreeLook>(); //添加第三人称相机组件
}
public void SetCinemachineTarget(Transform unitTrans)
{
	frelookCamera.LookAt = unitTrans;
	frelookCamera.Follow = unitTrans;
	frelookCamera.m_Lens.FieldOfView = 70;
	frelookCamera.m_YAxis.m_InvertInput = true; //y轴移动方向跟输入相反,即从后方跟踪目标
	frelookCamera.m_YAxis.m_Recentering.m_enabled = false;
	frelookCamera.m_XAxis.m_InvertInput = false;
	frelookCamera.m_BindingMode = CinemachineTransposer.BindingMode.SimpleFollowWithWorldUp; //设置跟踪模式
	frelookCamera.m_YAxis.m_MaxSpeed = 0; //设置Y轴最大移动速度为0,即不通过默认(移动鼠标)控制相机
	frelookCamera.m_XAxis.m_MaxSpeed = 0; //设置X轴最大移动速度为0,即不通过默认(移动鼠标)控制相机
	for(int i = 0; i <= 2; i++)
	{
		frelookCamera.m_Orbits[i] =  new CinemachineFreeLook.Orbit(5,5); //设置相机的三个Obirt,控制相机与目标的距离
		//去除TopRig,MidRig,BottomRig上OrbitalTransposer的Damping(阻尼)属性,使相机能够瞬间跟随目标,避免抖动
		frelookCamera.GetRig(i).GetCinemachineComponent<CinemachineOrbitalTransposer>().m_XDamping = 0; 
		frelookCamera.GetRig(i).GetCinemachineComponent<CinemachineOrbitalTransposer>().m_YDamping = 0;
		frelookCamera.GetRig(i).GetCinemachineComponent<CinemachineOrbitalTransposer>().m_ZDamping = 0;
		frelookCamera.GetRig(i).GetCinemachineComponent<CinemachineComposer>().m_ScreenY = yAxisSlider1; //设置y轴的旋转角度
	}
}

完整Demo

场景中有一个Cube(挂载MoveController脚本进行移动控制),一个默认的Camera,一个挂载了GameManager的空物体。
通过Slider实现参数调整(调整界面有 OnGUI 和 UGUI 两种方式),控制相机的显示效果,演示效果图如下:

在这里插入图片描述
GameManager完整脚本如下,关于Cinemachine 参数的说明请参考上方的连接,这里主要说明一下相机调节用到的参数,解释如下:

  • 由于 YAxis 的 Value 会影响 TopRig, MiddleRig, BottomRig 的选择,因此统一设置三个相机的参数,以实现在任意 YAxis 值情况下的调整。
  • 镜头效果的缩放:
    • 设置镜头的 FOV ;
    • 设置 Rig 的 radius 和 height ;
  • 角度的调整:
    • 设置 CinemachineComposer 的 ScreenY(类似远近调整);
    • 添加 CinemachineCameraOffset 组件并调整 yOffset,能在保证镜头看向目标的情况下调整角度;
  • 旋转速度的调整:
    • 设置 XAxis 的 maxValue;
using System;
using Cinemachine;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;

public class GameManager : MonoBehaviour
{
    //private CinemachineVirtualCamera virtualCamera; //虚拟相机
    public CinemachineFreeLook frelookCamera; //第三人称跟随相机
    public float xRecoverSpeed = 300f;
    private int rotateDelayID;
    public CinemachineCameraOffset cameraOffset;
    private GameObject cameraTestCanvasObj;
    private bool hasRegister = false;

    void Start()
    {
        //Cinemachine
        Camera.main.gameObject.AddComponent<CinemachineBrain>();
        var virtualCameraObj = new GameObject("[VirtualCamera]");
        frelookCamera = virtualCameraObj.AddComponent<CinemachineFreeLook>();
        cameraOffset = frelookCamera.gameObject.AddComponent<CinemachineCameraOffset>();
        SetCinemachineTarget(GameObject.Find("Cube").transform);
        GameObject cameraTestCanvas =
            AssetDatabase.LoadAssetAtPath<GameObject>("Assets/Prefabs/CameraTestCanvas.prefab");
        cameraTestCanvasObj = Instantiate(cameraTestCanvas);
    }

    public void Update()
    {
        SetCameraTestCanvas();
        UpdateText();

        //Cinemachine拖拽效果实现
        if (UnityEngine.EventSystems.EventSystem.current.IsPointerOverGameObject())
        {
            frelookCamera.m_YAxis.m_MaxSpeed = 0;
            frelookCamera.m_XAxis.m_MaxSpeed = 0;
            return;
        }

        if (Input.GetMouseButtonDown(0))
        {
            if (rotateDelayID > 0)
            {
                TimeManager.Instance.CancelDelayCall(rotateDelayID);
            }

            frelookCamera.m_YAxis.m_MaxSpeed = 2;
            frelookCamera.m_XAxis.m_MaxSpeed = xRecoverSpeed;
        }

        if (Input.GetMouseButtonUp(0))
        {
            rotateDelayID = TimeManager.Instance.DelayCall(500, () =>
            {
                frelookCamera.m_YAxis.m_MaxSpeed = 0;
                frelookCamera.m_XAxis.m_MaxSpeed = 0;
            });
        }
    }

    public void SetCinemachineTarget(Transform unitTrans)
    {
        if (frelookCamera == null)
        {
            return;
        }
        frelookCamera.LookAt = unitTrans;
        frelookCamera.Follow = unitTrans;
        frelookCamera.m_Lens.FieldOfView = 70;
        frelookCamera.m_YAxis.m_InvertInput = true;
        frelookCamera.m_YAxis.m_Recentering.m_enabled = false;
        frelookCamera.m_XAxis.m_InvertInput = false;
        frelookCamera.m_BindingMode = CinemachineTransposer.BindingMode.SimpleFollowWithWorldUp;
        frelookCamera.m_YAxis.m_MaxSpeed = 0;
        frelookCamera.m_XAxis.m_MaxSpeed = 0;
        for (int i = 0; i <= 2; i++)
        {
            frelookCamera.m_Orbits[i] = new CinemachineFreeLook.Orbit(5, 5);
            frelookCamera.GetRig(i).GetCinemachineComponent<CinemachineOrbitalTransposer>().m_XDamping = 0;
            frelookCamera.GetRig(i).GetCinemachineComponent<CinemachineOrbitalTransposer>().m_YDamping = 0;
            frelookCamera.GetRig(i).GetCinemachineComponent<CinemachineOrbitalTransposer>().m_ZDamping = 0;
        }

        cameraOffset.m_ApplyAfter = CinemachineCore.Stage.Body;
    }

    private Slider[] sliders = new Slider[5];
    private Text[] texts = new Text[5];

    private void SetCameraTestCanvas()
    {
        if (frelookCamera == null)
            return;
        if (!hasRegister)
        {
            hasRegister = true;
            float[] maxValue = new float[] {80, 10, 1, 1.6f, 300};
            float[] minValue = new float[] {30, 2, 0, -1.8f, 100};
            for (int i = 0; i < sliders.Length; i++)
            {
                sliders[i] = cameraTestCanvasObj.transform.Find((i + 1).ToString()).GetComponent<Slider>();
                sliders[i].maxValue = maxValue[i];
                sliders[i].minValue = minValue[i];
                texts[i] = sliders[i].transform.Find("Text").GetComponent<Text>();
            }

            sliders[0].onValueChanged.AddListener(fovValue => frelookCamera.m_Lens.FieldOfView = fovValue);
            sliders[1].onValueChanged.AddListener(orbitValue =>
            {
                for (int i = 0; i <= 2; i++)
                {
                    frelookCamera.m_Orbits[i] = new CinemachineFreeLook.Orbit(orbitValue, orbitValue);
                }
            });
            sliders[2].onValueChanged.AddListener(yValue =>
            {
                for (int i = 0; i <= 2; i++)
                {
                    frelookCamera.GetRig(i).GetCinemachineComponent<CinemachineComposer>().m_ScreenY = yValue;
                }
            });
            sliders[3].onValueChanged
                .AddListener(xOffsetValue => cameraOffset.m_Offset = new Vector3(0, xOffsetValue, 0));
            sliders[4].onValueChanged.AddListener(recoverValue => xRecoverSpeed = recoverValue);
        }
    }

    private void UpdateText()
    {
        if (hasRegister)
        {
            texts[0].text = "范围:" + (int) sliders[0].value;
            texts[1].text = "缩放:" + (int) sliders[1].value;
            texts[2].text = "角度1:" + String.Format("{0:F1}", sliders[2].value);
            texts[3].text = "角度2:" + String.Format("{0:F1}", sliders[3].value);
            texts[4].text = "转速:" + (int) sliders[4].value;
        }
    }

    private float fovSlider = 50;
    private float orbitSlider = 3;
    private float yAxisSlider = 0.5f;
    private float xOffsetSlider = 0;
    private float xRecoverSpeed2 = 200;
    private bool isShowMenu;
    private void OnGUI()
    {
        if (!isShowMenu)
        {
            if (GUI.Button(new Rect(425, 425, 80, 20), "ShowMenu"))
            {
                isShowMenu = true;
            }
        }
        else
        {
            if (GUI.Button(new Rect(425, 425, 80, 20), "HideMenu"))
            {
                isShowMenu = false;
            }
        }

        if (isShowMenu)
        {
            GUIStyle style = new GUIStyle();
            style.fontSize = 15;
            style.fontStyle = FontStyle.Bold;
            style.normal.textColor = Color.red;
            style.normal.background = Texture2D.grayTexture;
            style.alignment = TextAnchor.MiddleCenter;

            if (frelookCamera != null)
            {
                GUI.Label(new Rect(630, 40, 80, 20), "范围:" + (int) orbitSlider, style);
                GUI.Label(new Rect(630, 140, 80, 20), "缩放:" + (int) fovSlider, style);
                GUI.Label(new Rect(630, 240, 80, 20), "角度1:" + String.Format("{0:F1}", yAxisSlider), style);
                GUI.Label(new Rect(630, 340, 80, 20), "角度2:" + String.Format("{0:F1}", xOffsetSlider), style);
                GUI.Label(new Rect(630, 440, 80, 20), "转速:" + (int) xRecoverSpeed2, style);
                orbitSlider = GUI.HorizontalSlider(new Rect(725, 40, 100, 30), orbitSlider, 2, 10);
                fovSlider = GUI.HorizontalSlider(new Rect(725, 140, 100, 30), fovSlider, 30, 80);
                yAxisSlider = GUI.HorizontalSlider(new Rect(725, 240, 100, 30), yAxisSlider, 0, 1);
                xOffsetSlider = GUI.HorizontalSlider(new Rect(725, 340, 100, 30), xOffsetSlider, -1.8f, 1.6f);
                xRecoverSpeed2 = GUI.HorizontalSlider(new Rect(725, 440, 100, 30), xRecoverSpeed2, 100, 300);
                frelookCamera.m_Lens.FieldOfView = fovSlider;
                for (int i = 0; i <= 2; i++)
                {
                    frelookCamera.GetRig(i).GetCinemachineComponent<CinemachineComposer>().m_ScreenY = yAxisSlider;
                    frelookCamera.m_Orbits[i] = new CinemachineFreeLook.Orbit(orbitSlider, orbitSlider);
                }

                xRecoverSpeed = xRecoverSpeed2;
                cameraOffset.m_Offset = new Vector3(0, xOffsetSlider, 0);
            }
        }
    }
}
  • 10
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Unity Cinemachine 是一个在 Unity 引擎中使用的强大的摄像机控制和管理工具。它可以帮助游戏开发者轻松地创建高质量的摄像机动画,并对摄像机进行复杂的控制和调整。下面我将针对 Unity Cinemachine 的使用案例进行介绍。 首先,Unity Cinemachine 可以用于创建多种类型的摄像机效果。比如在第三人射击游戏中,我们可以使用 Cinemachine 来实现自由的跟随和旋转功能,让玩家可以在角色周围自由观察环境。此外,在平台游戏中,我们可以利用 Cinemachine 实现镜头跟随和平滑的镜头移动,并且可以在不同的场景中切换不同的摄像机角度,增加游戏的视觉效果和沉浸感。 其次,Unity Cinemachine 还可以帮助游戏开发者在过渡和剪辑摄像机之间创建流畅的过渡效果。通过 Cinemachine 的轨迹系统,开发者可以轻松地创建复杂的镜头移动路径,并且可以在不同的路径之间无缝切换,实现视角的流畅转换和视差效果。 最后,Unity Cinemachine 还可以与其他 Unity 引擎中的组件集成,比如与动画系统、碰撞检测和触发器等进行结合,实现更加复杂的摄像机行为和交互效果。这样,开发者可以更加灵活地控制游戏中的摄像机系统,实现更加丰富的游戏体验。 总之,Unity Cinemachine 是一个非常强大的摄像机控制工具,可以帮助游戏开发者轻松地创建高品质的摄像机动画和效果,为游戏增加更多的视觉和沉浸体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值