EnergyEfficiencyManagement/Assets/SKFramework/Tools/Camera/RoamCameraController.cs

216 lines
5.5 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace SK.Framework
{
/// <summary>
/// 观察相机组件
/// </summary>
[DisallowMultipleComponent]
public class RoamCameraController : MonoBehaviour
{
#region Inspector
[Header("Target")]
[Tooltip("观察物体不可以为空")]
public Transform target;
public Vector3 targetOffset = Vector3.zero;
[Header("Distance")]
public float distance = 5f;
public float minDistance = 2f;
public float maxDistance = 15f;
public float zoomSpeed = 2f;
[Header("Rotation")]
public float xSpeed = 120f;
public float ySpeed = 80f;
public float minYAngle = -30f;
public float maxYAngle = 80f;
[Header("Damping")]
public float rotationDamping = 8f;
public float zoomDamping = 8f;
public float positionDamping = 10f;
[Header("Control")]
public bool enableControl = true;
[Header("鼠标的键值(0左键1右键)")]
public int rotateMouseButton = 0; // 0 = Left Mouse Button
#endregion
#region Runtime State
private float currentX;
private float currentY;
private float targetDistance;
private float smoothX;
private float smoothY;
private float smoothDistance;
private bool hasReportedNullTargetError = false;
#endregion
#region Unity Lifecycle
void Start()
{
Vector3 angles = transform.eulerAngles;
currentX = angles.y;
currentY = angles.x;
targetDistance = Mathf.Clamp(distance, minDistance, maxDistance);
smoothDistance = targetDistance;
}
void LateUpdate()
{
if (!enableControl)
return;
if (target == null)
{
ReportNullTargetErrorOnce();
return; //停止
}
HandleInput();
UpdateCamera();
}
#if UNITY_EDITOR
void OnValidate()
{
if (target == null)
{
Debug.LogError(
"[RoamCameraController] Target 没有被设置!",
this
);
}
if (minDistance > maxDistance)
{
minDistance = maxDistance;
}
}
#endif
#endregion
#region Core Logic
void HandleInput()
{
if (Input.GetMouseButton(rotateMouseButton))
{
currentX += Input.GetAxis("Mouse X") * xSpeed * 0.02f;
currentY -= Input.GetAxis("Mouse Y") * ySpeed * 0.02f;
}
currentY = ClampAngle(currentY, minYAngle, maxYAngle);
float scroll = Input.GetAxis("Mouse ScrollWheel");
if (Mathf.Abs(scroll) > 0.001f)
{
targetDistance -= scroll * zoomSpeed;
targetDistance = Mathf.Clamp(targetDistance, minDistance, maxDistance);
}
}
void UpdateCamera()
{
smoothX = Mathf.LerpAngle(smoothX, currentX, Time.deltaTime * rotationDamping);
smoothY = Mathf.LerpAngle(smoothY, currentY, Time.deltaTime * rotationDamping);
smoothDistance = Mathf.Lerp(
smoothDistance,
targetDistance,
Time.deltaTime * zoomDamping
);
Quaternion rotation = Quaternion.Euler(smoothY, smoothX, 0f);
Vector3 dir = rotation * Vector3.back * smoothDistance;
Vector3 targetPos = target.position + targetOffset;
Vector3 desiredPos = targetPos + dir;
transform.position = Vector3.Lerp(
transform.position,
desiredPos,
Time.deltaTime * positionDamping
);
transform.rotation = rotation;
}
#endregion
#region Public API
/// <summary>
/// Switch observed target.
/// </summary>
public void SetTarget(Transform newTarget, bool instant = true)
{
target = newTarget;
if (target == null)
{
ReportNullTargetErrorOnce();
return;
}
// 恢复正常状态
hasReportedNullTargetError = false;
Vector3 targetPos = target.position + targetOffset;
Quaternion rot = Quaternion.Euler(currentY, currentX, 0f);
Vector3 dir = rot * Vector3.back * targetDistance;
if (instant)
{
transform.position = targetPos + dir;
smoothX = currentX;
smoothY = currentY;
smoothDistance = targetDistance;
}
}
#endregion
#region Error Handling
void ReportNullTargetErrorOnce()
{
if (hasReportedNullTargetError)
return;
hasReportedNullTargetError = true;
Debug.LogError(
$"[RoamCameraController] Target为空相机停止运行 " +
$"GameObject: {gameObject.name}",
this
);
}
#endregion
#region Utility
static float ClampAngle(float angle, float min, float max)
{
angle = Mathf.Repeat(angle + 180f, 360f) - 180f;
return Mathf.Clamp(angle, min, max);
}
#endregion
}
}