using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
namespace CustomTool
{
public class CameraController : MonoBehaviour
{
#region 私有变量
///
/// 是否可穿碰撞器
///
[Header("是否开启穿透检测")]
[SerializeField]
private bool m_IsCouldCrossCollider;
///
/// 是否限定相机的移动区域
///
[Header("是否开启区域限定")]
[SerializeField]
private bool m_IsLimitArea = false;
///
/// 是否绘制区域效果
///
[Header("是否绘制限制区域效果")]
[SerializeField]
private bool m_IsDragAreaEffect = false;
///
/// 全场景区域限定最小点
///
[Header("大范围限制区域")]
[SerializeField]
private Transform m_SceneMinPoint;
///
/// 全场景区域限定最大点
///
[SerializeField]
private Transform m_SceneMaxPoint;
///
/// 相机移动速度
///
[Header("相机相关参数")]
[SerializeField]
private float m_MoveSpeed = 10f;
///
/// X轴视角旋转速度
///
[SerializeField]
private float m_XSpeed = 250.0f;
///
/// Y轴视角旋转速度
///
[SerializeField]
private float m_YSpeed = 120.0f;
///
/// 鼠标中间滚轮的拉近拉远速度
///
[SerializeField]
private float m_MouseSpeed = 60f;
///
/// 视角旋转向下最小限制
///
[SerializeField]
private float m_YMinLimit = -20;
///
/// 视角旋转向上最大限制
///
[SerializeField]
private float m_YMaxLimit = 80;
///
/// 鼠标中键移动速度限制
///
[SerializeField]
public float m_SpeedLimit = 0.2f;
private float xMin, xMax, yMin, yMax, zMin, zMax; // 相机的移动范围
private float m_MoveX;
private float m_MoveY;
private float m_MoveZ = 0;
private float m_AngleX = 0.0f;
private float m_AngleY = 0.0f;
#endregion
[Serializable]
public struct LimitArea
{
///
/// 房屋区域的最小点
///
public Transform minPoint;
///
/// 房屋区域的最大点
///
public Transform maxPoint;
}
///
/// 房屋区域的数组
///
[FormerlySerializedAs("houseAreas")]
[Header("不可穿透区域")]
[SerializeField]
private LimitArea[] m_LimitAreas = null;
private void Awake()
{
ResetInfo();
SetCameraInitData();
SetCameraPosLimite(m_SceneMinPoint.position, m_SceneMaxPoint.position);
}
void Update()
{
HandleKeyboardInput();
HandleMouseInput();
RotateView();
ViewScale();
}
///
/// 重置位置信息
///
public void ResetInfo()
{
var angles = transform.eulerAngles;
m_AngleX = angles.y;
m_AngleY = angles.x;
}
///
/// 相机移动
///
private void HandleKeyboardInput()
{
// 键盘输入
m_MoveX = Input.GetAxis("Horizontal") * m_MoveSpeed * Time.deltaTime;
m_MoveY = Input.GetAxis("Vertical") * m_MoveSpeed * Time.deltaTime;
float moveZ = 0;
// 上移和下移键
if (Input.GetKey(KeyCode.E))
{
moveZ = m_MoveSpeed * Time.deltaTime;
}
else if (Input.GetKey(KeyCode.Q))
{
moveZ = -m_MoveSpeed * Time.deltaTime;
}
Vector3 move = new Vector3(m_MoveX, moveZ, m_MoveY);
Vector3 movePos = transform.position + transform.TransformDirection(move);
if (m_IsLimitArea)
{
// 限制相机在指定区域内移动
movePos.x = Mathf.Clamp(movePos.x, xMin, xMax);
movePos.y = Mathf.Clamp(movePos.y, yMin, yMax);
movePos.z = Mathf.Clamp(movePos.z, zMin, zMax);
}
if (m_IsCouldCrossCollider)
{
// 检查并调整相机位置
foreach (LimitArea item in m_LimitAreas)
{
if (IsInsideHouse(movePos, item))
{
movePos = AdjustPositionOutsideHouse(movePos, item);
}
}
}
transform.position = movePos;
}
///
/// 鼠标中键控制相机移动
///
private void HandleMouseInput()
{
// 鼠标中键控制移动
if (Input.GetMouseButton(2))
{
float mouseX = Input.GetAxis("Mouse X") * m_MoveSpeed * Time.deltaTime;
float mouseY = Input.GetAxis("Mouse Y") * m_MoveSpeed * Time.deltaTime;
Vector3 move = new Vector3(mouseX, 0, mouseY);
Vector3 movePos = transform.position + transform.TransformDirection(move);
if (m_IsLimitArea)
{
// 限制相机在指定区域内移动
movePos.x = Mathf.Clamp(movePos.x, xMin, xMax);
movePos.y = Mathf.Clamp(movePos.y, yMin, yMax);
movePos.z = Mathf.Clamp(movePos.z, zMin, zMax);
}
if (m_IsCouldCrossCollider)
{
// 检查并调整相机位置
foreach (LimitArea item in m_LimitAreas)
{
if (IsInsideHouse(movePos, item))
{
movePos = AdjustPositionOutsideHouse(movePos, item);
}
}
}
transform.position = movePos;
}
}
///
/// 鼠标右键控制视角旋转
///
private void RotateView()
{
if (Input.GetMouseButton(1))
{
if (Input.GetAxis("Mouse X") != 0 || Input.GetAxis("Mouse Y") != 0)
{
m_AngleX += Input.GetAxis("Mouse X") * m_XSpeed * 0.02f;
m_AngleY -= Input.GetAxis("Mouse Y") * m_YSpeed * 0.02f;
m_AngleY = ClampAngle(m_AngleY, m_YMinLimit, m_YMaxLimit);
var rotation = Quaternion.Euler(m_AngleY, m_AngleX, 0);
transform.rotation = rotation;
}
}
}
///
/// 滑轮控制视角移动
///
private void ViewScale()
{
if (Input.GetAxis("Mouse ScrollWheel") != 0)
{
// 鼠标滚轮控制相机距离
Vector3 scrollMove = transform.forward * (Input.GetAxis("Mouse ScrollWheel") * m_MouseSpeed * Time.deltaTime);
Vector3 movePos = transform.position + scrollMove;
if (m_IsLimitArea)
{
movePos.x = Mathf.Clamp(movePos.x, xMin, xMax);
movePos.y = Mathf.Clamp(movePos.y, yMin, yMax);
movePos.z = Mathf.Clamp(movePos.z, zMin, zMax);
}
if (m_IsCouldCrossCollider)
{
// 检查并调整相机位置
foreach (LimitArea item in m_LimitAreas)
{
if (IsInsideHouse(movePos, item))
{
movePos = AdjustPositionOutsideHouse(movePos, item);
}
}
}
transform.position = movePos;
}
}
///
/// 跳转到对应内部区域
///
/// 目标位置
/// 房间最小点
/// 房间最大点
public void JumpCorrespondingPosition(Transform _targetPosition, Transform _minPoint, Transform _maxPoint)
{
// 设置相机移动范围为房间范围
SetCameraPosLimite(_minPoint.position, _maxPoint.position);
// 确保目标点在指定房屋区域内
Vector3 clampedTargetPoint = _targetPosition.position;
clampedTargetPoint.x = Mathf.Clamp(clampedTargetPoint.x, _minPoint.position.x, _maxPoint.position.x);
clampedTargetPoint.y = Mathf.Clamp(clampedTargetPoint.y, _minPoint.position.y, _maxPoint.position.y);
clampedTargetPoint.z = Mathf.Clamp(clampedTargetPoint.z, _minPoint.position.z, _maxPoint.position.z);
// 更新相机位置到目标点
transform.position = clampedTargetPoint;
}
///
/// 跳转到对应内部区域
///
/// 目标位置
/// 房间最小点
/// 房间最大点
public void JumpCorrespondingPosition(Vector3 _targetPosition, Transform _minPoint, Transform _maxPoint)
{
// 设置相机移动范围为房间范围
SetCameraPosLimite(_minPoint.position, _maxPoint.position);
// 确保目标点在指定房屋区域内
Vector3 clampedTargetPoint = _targetPosition;
clampedTargetPoint.x = Mathf.Clamp(clampedTargetPoint.x, _minPoint.position.x, _maxPoint.position.x);
clampedTargetPoint.y = Mathf.Clamp(clampedTargetPoint.y, _minPoint.position.y, _maxPoint.position.y);
clampedTargetPoint.z = Mathf.Clamp(clampedTargetPoint.z, _minPoint.position.z, _maxPoint.position.z);
// 更新相机位置到目标点
transform.position = clampedTargetPoint;
}
///
/// 重置限定区域
///
/// 跳转目标点
/// 是否处于大场景限定范围,默认是true
/// 如果_isLargeScene为默认值时,不需要填值
/// 如果_isLargeScene为默认值时,不需要填值
public void ResetLimit(Transform _targetPosition, bool _isLargeScene = true, Transform _minPoint = null, Transform _maxPoint = null)
{
if (_isLargeScene)
{
JumpCorrespondingPosition(_targetPosition, m_SceneMinPoint, m_SceneMaxPoint);
}
else
{
JumpCorrespondingPosition(_targetPosition, _minPoint, _maxPoint);
}
}
///
/// 设置限定区域的状态
///
///
public void SetStatusOfRestrictedArea(bool _isActive)
{
m_IsCouldCrossCollider = _isActive;
}
///
/// 检测相机是否在物体内
///
///
///
///
private bool IsInsideHouse(Vector3 _position, LimitArea _limit)
{
Vector3 minPoint = _limit.minPoint.position;
Vector3 maxPoint = _limit.maxPoint.position;
return _position.x > minPoint.x && _position.x < maxPoint.x &&
_position.y > minPoint.y && _position.y < maxPoint.y &&
_position.z > minPoint.z && _position.z < maxPoint.z;
}
///
/// 判断相机的位置,并做出修改
///
///
///
///
private Vector3 AdjustPositionOutsideHouse(Vector3 _position, LimitArea _limit)
{
Vector3 minPoint = _limit.minPoint.position;
Vector3 maxPoint = _limit.maxPoint.position;
// 计算相机当前位置与房屋边界的距离
float xDistToMin = Mathf.Abs(_position.x - minPoint.x);
float xDistToMax = Mathf.Abs(_position.x - maxPoint.x);
float zDistToMin = Mathf.Abs(_position.z - minPoint.z);
float zDistToMax = Mathf.Abs(_position.z - maxPoint.z);
// 找到距离最近的边界
float minDist = Mathf.Min(xDistToMin, xDistToMax, zDistToMin, zDistToMax);
// 根据最近的边界调整相机位置
if (minDist == xDistToMin)
{
// 相机靠近最小X边界
_position.x = minPoint.x - 0.1f;
}
else if (minDist == xDistToMax)
{
// 相机靠近最大X边界
_position.x = maxPoint.x + 0.1f;
}
else if (minDist == zDistToMin)
{
// 相机靠近最小Z边界
_position.z = minPoint.z - 0.1f;
}
else if (minDist == zDistToMax)
{
// 相机靠近最大Z边界
_position.z = maxPoint.z + 0.1f;
}
return _position;
}
///
/// 设置相机的移动范围
///
///
///
private void SetCameraPosLimite(Vector3 _minPoint, Vector3 _maxPoint)
{
xMin = _minPoint.x;
xMax = _maxPoint.x;
yMin = _minPoint.y;
yMax = _maxPoint.y;
zMin = _minPoint.z;
zMax = _maxPoint.z;
}
///
/// 控制摄像机的旋转角度大小
///
///
///
///
///
private float ClampAngle(float _angle, float _min, float _max)
{
if (_angle < -360)
{
_angle += 360;
}
if (_angle > 360)
{
_angle -= 360;
}
return Mathf.Clamp(_angle, _min, _max);
}
private void OnDrawGizmos()
{
if (m_IsDragAreaEffect)
{
Gizmos.color = Color.green;
Gizmos.DrawWireCube(new Vector3((xMin + xMax) / 2, (yMin + yMax) / 2, (zMin + zMax) / 2),
new Vector3(xMax - xMin, yMax - yMin, zMax - zMin));
Gizmos.color = Color.red;
if (m_LimitAreas != null)
{
foreach (LimitArea house in m_LimitAreas)
{
Vector3 minPoint = house.minPoint.position;
Vector3 maxPoint = house.maxPoint.position;
Vector3 center = (minPoint + maxPoint) / 2;
Vector3 size = maxPoint - minPoint;
Gizmos.DrawWireCube(center, size);
}
}
}
}
///
/// 设置相机的初始数据
///
private void SetCameraInitData()
{
CameraConfig cameraConfig = GetCameraConfigData();
if (cameraConfig == null)
{
return;
}
m_MoveSpeed = cameraConfig.MoveSpeed;
m_XSpeed = cameraConfig.XRotateSpeed;
m_YSpeed = cameraConfig.YRotateSpeed;
m_MouseSpeed = cameraConfig.MiddleMouseZoomSpeed;
m_YMinLimit = cameraConfig.YMinLimit;
m_YMaxLimit = cameraConfig.YMaxLimit;
m_SpeedLimit = cameraConfig.MiddleMouseMoveSpeed;
}
///
/// 获取相机配置数据
///
///
private CameraConfig GetCameraConfigData()
{
// 拼接文件基本路径
string filePath = PathHelper.CombineFilePath(PathHelper.m_ReadOnlyPath,
"Json",
"CameraConfig.json");
string fileData = FileHelper.GetAllDataInfo(filePath);
CameraConfig config = JsonUtility.FromJson(fileData);
return config == null ? null : config;
}
}
}