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

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 两种方式),控制相机的显示效果,演示效果图如下:

Cinemachine API 第三人称跟随案例与参数调节_第1张图片
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);
            }
        }
    }
}

你可能感兴趣的:(c#,Unity,Cinemachine,unity3d,游戏开发,unity)