这几天简单研究了一下 Cinemachine Free Look,在网上看了不少相关文章,但是关于通过脚本使用 Free Look 的参考所见不多。因此,通过整理学习,简单实现了一下第三人称跟随效果和通过 UI 调整相机参数的功能,一些参考和用到的实际代码如下。
CinemachineFreeLook属性详解
Cinemachine free look使用建议
官方文档
Cinemachine 论坛
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); //设置看向偏移
}
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轴的旋转角度
}
}
场景中有一个Cube(挂载MoveController脚本进行移动控制),一个默认的Camera,一个挂载了GameManager的空物体。
通过Slider实现参数调整(调整界面有 OnGUI 和 UGUI 两种方式),控制相机的显示效果,演示效果图如下:
GameManager完整脚本如下,关于Cinemachine 参数的说明请参考上方的连接,这里主要说明一下相机调节用到的参数,解释如下:
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);
}
}
}
}