using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace SK.Framework
{
///
/// 观察相机组件
///
[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
///
/// Switch observed target.
///
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
}
}