【Unity】CinemachineVirtualCamera:实现第一人称视角控制
相机视角的控制,利用CinemachineVirtualCamera插件(在packageManager中下载)实现键盘和鼠标控制第一人称视角。WASD前进后退向左向右,QE左右旋转;鼠标滚轮控制远近、俯仰和升降。
另外还支持鼠标靠近边缘移动、鼠标拖拽等控制方式。
成果展示
Scene部分
主相机增加CinemachineBrain
组件;
增加空物体CinemachineVirtualCamera
,并绑定CinemachineVirtualCamera
组件,参数如下:
增加空物体CameraSystem,并绑定脚本,脚本的参数如下:
脚本部分
CameraSystem脚本内容
CinemachineVirtualCamera
插件中可以改变的参数很多,不同的参数对应不同的视角改变效果,这里仅提及少数几个参数。
public class CameraSystem : MonoBehaviour
{
[SerializeField] private CinemachineVirtualCamera cinemachineVirtualCamera;
[SerializeField] private Transform inputTranform;
private TextMeshProUGUI tipText;
[SerializeField] private float moveSpeed = 20f;
[SerializeField] private float rotateSpeed = 200f;
[SerializeField] private float liftingSpeed = 20f;
[SerializeField] private bool useEdgeScrolling = false;
[SerializeField] private bool useDragPan = true;
[SerializeField] private CameraZoomPara cameraZoomPara = CameraZoomPara.FieldOfView;
[SerializeField] private float fieldOfViewMin = 10f;
[SerializeField] private float fieldOfViewMax = 150f;
[SerializeField] private float followOffsetMin = 5f;
[SerializeField] private float followOffsetMax = 50f;
[SerializeField] private float followOffsetMinY = 10f;
[SerializeField] private float followOffsetMaxY = 50f;
[SerializeField] private float followOffsetMinZ = 10f;
[SerializeField] private float followOffsetMaxZ = 50f;
[SerializeField] private float orthographicSizeMinY = 10f;
[SerializeField] private float orthographicSizeMaxY = 50f;
[SerializeField] private float cameraSystemMinY = 0f;
[SerializeField] private float cameraSystemMaxY = 30f;
private bool dragPanMoveAction;
private Vector2 lastMousePosition;
private float targetFieldOfView = 50f;
private Vector3 followOffset;
private float targetOrthographicSize = 20f;
//可以改变的参数
private enum CameraZoomPara
{
FieldOfView,
MoveForward,
LowerY,
LowerZ,
OrthographicSize,
CameraSystemY
}
private void Awake()
{
followOffset = cinemachineVirtualCamera.GetCinemachineComponent<CinemachineTransposer>().m_FollowOffset;
tipText = inputTranform.Find("tip").GetComponent<TextMeshProUGUI>();
tipText.SetText("放缩");
}
private void Update()
{
HandleCameraMovement();
//鼠标接近边缘控制前后左右
if (useEdgeScrolling)
{
HandleCameraMovementEdgeScrolling();
}
//鼠标右键拖拽控制前后左右
if (useDragPan)
{
HandleCameraMovementdragPan();
}
HandleCameraRotation();
HandleCameraZoom();
}
private void GetNextCameraZoom(CameraZoomPara para)
{
string tip = "";
switch (para)
{
case CameraZoomPara.FieldOfView:
cameraZoomPara = CameraZoomPara.LowerY;
tip = "useAngle";
tipText.SetText("俯仰");
break;
case CameraZoomPara.LowerY:
cameraZoomPara = CameraZoomPara.CameraSystemY;
tip = "useLifting";
tipText.SetText("升降");
break;
case CameraZoomPara.CameraSystemY:
cameraZoomPara = CameraZoomPara.FieldOfView;
tip = "useZoom";
tipText.SetText("放缩");
break;
}
UtilsClass.CreateWorldTextPopup(tip, Mouse3D.GetMouseWorldPosition());
}
private void HandleCameraZoom()
{
//空格键切换滚轮的控制模式
if (Input.GetKeyUp(KeyCode.Space))
{
GetNextCameraZoom(cameraZoomPara);
}
switch (cameraZoomPara)
{
case CameraZoomPara.FieldOfView:
HandleCameraZoom_FieldOfView();
break;
//case CameraZoomPara.MoveForward:
// HandleCameraZoom_MoveForward();
// break;
case CameraZoomPara.LowerY:
HandleCameraZoom_LowerY();
break;
case CameraZoomPara.CameraSystemY:
HandleCameraZoom_CrmeraSystemY();
break;
//case CameraZoomPara.LowerZ:
// HandleCameraZoom_LowerZ();
// break;
//case CameraZoomPara.OrthographicSize:
// HandleCameraZoom_OrthographicSize();
// break;
//default:
// break;
}
}
private void HandleCameraZoom_FieldOfView()
{
if (Input.mouseScrollDelta.y > 0)
{
targetFieldOfView -= 5;
}
if (Input.mouseScrollDelta.y < 0)
{
targetFieldOfView += 5;
}
targetFieldOfView = Mathf.Clamp(targetFieldOfView, fieldOfViewMin, fieldOfViewMax);
float zoomSpeed = 10f;
cinemachineVirtualCamera.m_Lens.FieldOfView = Mathf.Lerp(cinemachineVirtualCamera.m_Lens.FieldOfView, targetFieldOfView, Time.deltaTime * zoomSpeed);
}
private void HandleCameraZoom_MoveForward()
{
Vector3 zoomDir = followOffset.normalized;
float zoomAmount = 3f;
if (Input.mouseScrollDelta.y > 0)
{
followOffset -= zoomDir * zoomAmount;
}
if (Input.mouseScrollDelta.y < 0)
{
followOffset += zoomDir * zoomAmount;
}
if (followOffset.magnitude < followOffsetMin)
{
followOffset = zoomDir * followOffsetMin;
}
if (followOffset.magnitude > followOffsetMax)
{
followOffset = zoomDir * followOffsetMax;
}
float zoomSpeed = 10f;
cinemachineVirtualCamera.GetCinemachineComponent<CinemachineTransposer>().m_FollowOffset
= Vector3.Lerp(cinemachineVirtualCamera.GetCinemachineComponent<CinemachineTransposer>().m_FollowOffset, followOffset, Time.deltaTime * zoomSpeed);
}
private void HandleCameraZoom_LowerY()
{
float zoomAmount = 3f;
if (Input.mouseScrollDelta.y > 0)
{
followOffset.y -= zoomAmount;
}
if (Input.mouseScrollDelta.y < 0)
{
followOffset.y += zoomAmount;
}
followOffset.y = Mathf.Clamp(followOffset.y, followOffsetMinY, followOffsetMaxY);
float zoomSpeed = 10f;
cinemachineVirtualCamera.GetCinemachineComponent<CinemachineTransposer>().m_FollowOffset
= Vector3.Lerp(cinemachineVirtualCamera.GetCinemachineComponent<CinemachineTransposer>().m_FollowOffset, followOffset, Time.deltaTime * zoomSpeed);
}
private void HandleCameraZoom_LowerZ()
{
float zoomAmount = 3f;
if (Input.mouseScrollDelta.y > 0)
{
followOffset.z -= zoomAmount;
}
if (Input.mouseScrollDelta.y < 0)
{
followOffset.z += zoomAmount;
}
followOffset.z = Mathf.Clamp(followOffset.z, followOffsetMinZ, followOffsetMaxZ);
float zoomSpeed = 10f;
cinemachineVirtualCamera.GetCinemachineComponent<CinemachineTransposer>().m_FollowOffset
= Vector3.Lerp(cinemachineVirtualCamera.GetCinemachineComponent<CinemachineTransposer>().m_FollowOffset, followOffset, Time.deltaTime * zoomSpeed);
}
private void HandleCameraZoom_OrthographicSize()
{
if (Input.mouseScrollDelta.y > 0)
{
targetOrthographicSize -= 5;
}
if (Input.mouseScrollDelta.y < 0)
{
targetOrthographicSize += 5;
}
targetOrthographicSize = Mathf.Clamp(targetOrthographicSize, orthographicSizeMinY, orthographicSizeMaxY);
float zoomSpeed = 10f;
cinemachineVirtualCamera.m_Lens.OrthographicSize
= Mathf.Lerp(cinemachineVirtualCamera.m_Lens.OrthographicSize, targetOrthographicSize, Time.deltaTime * zoomSpeed);
}
private void HandleCameraZoom_CrmeraSystemY()
{
Vector3 inputDir = new Vector3(0, 0, 0);
if (Input.mouseScrollDelta.y > 0)
{
inputDir.y = -1f;
}
if (Input.mouseScrollDelta.y < 0)
{
inputDir.y = +1f;
}
Vector3 moveDir = transform.up * inputDir.y;
Vector3 targetPosition = (transform.position + moveDir * liftingSpeed * Time.deltaTime);
targetPosition.y = Mathf.Clamp(targetPosition.y, cameraSystemMinY, cameraSystemMaxY);
transform.position = targetPosition;
}
private void HandleCameraMovementEdgeScrolling()
{
Vector3 inputDir = new Vector3(0, 0, 0);
int edgeScrollSize = 20;
if (Input.mousePosition.x < edgeScrollSize)
{
inputDir.x = -1f;
}
if (Input.mousePosition.y < edgeScrollSize)
{
inputDir.z = -1f;
}
if (Input.mousePosition.x > Screen.width - edgeScrollSize)
{
inputDir.x = +1f;
}
if (Input.mousePosition.y > Screen.height - edgeScrollSize)
{
inputDir.z = +1f;
}
Vector3 moveDir = transform.forward * inputDir.z + transform.right * inputDir.x;
transform.position += moveDir * moveSpeed * Time.deltaTime;
}
private void HandleCameraMovementdragPan()
{
Vector3 inputDir = new Vector3(0, 0, 0);
if (Input.GetMouseButtonDown(1))
{
dragPanMoveAction = true;
lastMousePosition = Input.mousePosition;
}
if (Input.GetMouseButtonUp(1))
{
dragPanMoveAction = false;
}
if (dragPanMoveAction)
{
Vector2 mouseMovementDelta = (Vector2)Input.mousePosition - lastMousePosition;
float dragPanSpeed = 2f;
inputDir.x = mouseMovementDelta.x * dragPanSpeed;
inputDir.z = mouseMovementDelta.y * dragPanSpeed;
lastMousePosition = Input.mousePosition;
}
Vector3 moveDir = transform.forward * inputDir.z + transform.right * inputDir.x;
float dragMoveSpeed = moveSpeed / 4;
transform.position = Vector3.Lerp(transform.position, transform.position - moveDir * dragMoveSpeed * Time.deltaTime, dragMoveSpeed * Time.deltaTime);
}
private void HandleCameraRotation()
{
float rotateDir = 0f;
if (Input.GetKey(KeyCode.Q)) rotateDir += +1f;
if (Input.GetKey(KeyCode.E)) rotateDir += -1f;
transform.eulerAngles -= new Vector3(0, rotateDir * rotateSpeed * Time.deltaTime, 0);
}
private void HandleCameraMovement()
{
Vector3 inputDir = new Vector3(0, 0, 0);
if (Input.GetKey(KeyCode.W)) inputDir.z += +1f;
if (Input.GetKey(KeyCode.S)) inputDir.z += -1f;
if (Input.GetKey(KeyCode.A)) inputDir.x += -1f;
if (Input.GetKey(KeyCode.D)) inputDir.x += +1f;
Vector3 moveDir = transform.forward * inputDir.z + transform.right * inputDir.x;
transform.position += moveDir * moveSpeed * Time.deltaTime;
}
}