1023 lines
32 KiB
C#
1023 lines
32 KiB
C#
using UnityEngine;
|
||
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using UnityEngine.UI;
|
||
using UnityEngine.EventSystems;
|
||
public class EnhancedModelViewerOrbitCamera : MonoBehaviour
|
||
{
|
||
[Header("观察目标")]
|
||
[SerializeField] private Transform target; // 要观察的模型
|
||
public Transform GetTarget()
|
||
{
|
||
return target;
|
||
}
|
||
|
||
[Header("基础控制开关")]
|
||
[SerializeField] private bool enableClickToFocus = true; // 是否启用点击子物体聚焦功能
|
||
[SerializeField] private bool enableRotation = true; // 是否启用旋转功能
|
||
[SerializeField] private bool enablePan = true; // 是否启用平移功能
|
||
[SerializeField] private bool enableZoom = true; // 是否启用缩放功能
|
||
[SerializeField] private bool enableAutoRotation = false; // 是否启用自动旋转
|
||
|
||
[Header("旋转设置")]
|
||
[SerializeField] private float rotationSpeed = 5.0f;
|
||
[SerializeField] private float autoRotationSpeed = 0.5f;
|
||
[SerializeField] private float rotationSmoothness = 5.0f;
|
||
|
||
[Header("缩放设置")]
|
||
[SerializeField] private float zoomSpeed = 5.0f;
|
||
[SerializeField] private float minZoomDistance = 1.0f;
|
||
[SerializeField] private float maxZoomDistance = 20.0f;
|
||
[SerializeField] private float defaultZoomDistance = 5.0f;
|
||
|
||
[Header("平移设置")]
|
||
[SerializeField] private float panSpeed = 0.5f;
|
||
|
||
[Header("平移距离限制")]
|
||
[SerializeField] private float maxPanDistanceX = 5.0f; // X轴最大平移距离
|
||
[SerializeField] private float maxPanDistanceZ = 5.0f; // Z轴最大平移距离
|
||
[SerializeField] private bool showPanBoundary = true; // 是否显示平移边界
|
||
|
||
[Header("子物体复制体父级")]
|
||
[SerializeField] private Transform childInstanceParent; // 子物体复制体的父级
|
||
[Header("子物体观察模式 - 层级设置")]
|
||
[SerializeField] private LayerMask selectableLayers = -1; // 可选择图层
|
||
[SerializeField] private LayerMask excludedLayers = 0; // 要排除的图层
|
||
[Header("子物体观察模式 - 物体类型排除")]
|
||
[SerializeField] private bool excludeCameras = true; // 是否排除摄像机
|
||
[SerializeField] private bool excludeLights = true; // 是否排除灯光
|
||
[SerializeField] private bool excludeUI = true; // 是否排除UI元素
|
||
[SerializeField] private string[] excludeTags = { "MainCamera", "Light", "UI" }; // 要排除的标签
|
||
[SerializeField] private float childMinZoomDistance = 0.5f; // 观察子物体时的最小缩放距离
|
||
[SerializeField] private float childMaxZoomDistance = 10.0f; // 观察子物体时的最大缩放距离
|
||
[SerializeField] private Color highlightColor = new Color(1f, 0.5f, 0f, 0.3f); // 高亮颜色
|
||
[SerializeField] private float highlightFadeSpeed = 5.0f; // 高亮淡出速度
|
||
|
||
[Header("鼠标控制")]
|
||
[SerializeField] private MouseButton rotateButton = MouseButton.Left;
|
||
[SerializeField] private MouseButton panButton = MouseButton.Middle;
|
||
[SerializeField] private float mouseSensitivity = 1.0f;
|
||
|
||
[Header("触摸屏控制")]
|
||
[SerializeField] private float touchSensitivity = 0.5f;
|
||
|
||
[Header("边界限制")]
|
||
[SerializeField] private Vector2 verticalAngleLimit = new Vector2(10f, 80f);
|
||
|
||
private Vector3 currentRotation = Vector3.zero;
|
||
private Vector3 targetRotation = Vector3.zero;
|
||
|
||
private float currentDistance = 5.0f;
|
||
private float targetDistance = 5.0f;
|
||
|
||
private Vector3 targetPositionOffset = Vector3.zero;
|
||
private Vector3 currentTargetPositionOffset = Vector3.zero;
|
||
|
||
private Vector3 lastMousePosition = Vector3.zero;
|
||
private Vector3 lastPanPosition = Vector3.zero;
|
||
|
||
// 子物体观察模式相关变量
|
||
private bool isObservingChild = false;
|
||
private GameObject currentObservedChild = null;
|
||
private GameObject childInstance = null;
|
||
|
||
// 子物体观察模式下的缩放距离
|
||
private float childCurrentDistance = 3.0f;
|
||
private float childTargetDistance = 3.0f;
|
||
|
||
// 高亮相关
|
||
private Renderer[] highlightedRenderers = null;
|
||
private List<Material[]> originalMaterials = new List<Material[]>();
|
||
private List<Material[]> highlightMaterials = new List<Material[]>();
|
||
private float highlightTimer = 0f;
|
||
|
||
// 保存原始状态
|
||
private Vector3 savedTargetPositionOffset = Vector3.zero;
|
||
private Vector3 savedTargetRotation = Vector3.zero;
|
||
private float savedTargetDistance = 5.0f;
|
||
private float savedCurrentDistance = 5.0f;
|
||
private bool savedEnableAutoRotation = false;
|
||
|
||
public bool isOpenOnGUI = false;
|
||
|
||
/// <summary>
|
||
/// 当开始观察子物体时触发。参数1: 克隆体, 参数2: 原始物体
|
||
/// </summary>
|
||
public System.Action<GameObject, GameObject> OnChildObservationStarted;
|
||
/// <summary>
|
||
/// 当结束观察子物体时触发。参数1: 克隆体 (可能为null), 参数2: 原始物体
|
||
/// </summary>
|
||
public System.Action<GameObject, GameObject> OnChildObservationEnded;
|
||
|
||
private enum MouseButton
|
||
{
|
||
Left = 0,
|
||
Right = 1,
|
||
Middle = 2
|
||
}
|
||
|
||
|
||
void LateUpdate()
|
||
{
|
||
if (target == null) return;
|
||
if (EventSystem.current.IsPointerOverGameObject()) return;
|
||
HandleInput();
|
||
UpdateCameraPosition();
|
||
|
||
// 更新高亮效果
|
||
UpdateHighlight();
|
||
}
|
||
|
||
private void HandleInput()
|
||
{
|
||
HandleDesktopInput();
|
||
HandleTouchInput();
|
||
|
||
// 处理子物体观察模式切换
|
||
HandleChildObservationMode();
|
||
}
|
||
|
||
private void HandleChildObservationMode()
|
||
{
|
||
// 按左Shift键退出子物体观察模式
|
||
if (Input.GetKeyDown(KeyCode.LeftShift) || Input.GetKeyDown(KeyCode.RightShift))
|
||
{
|
||
ExitChildObservationMode();
|
||
}
|
||
}
|
||
|
||
private void HandleDesktopInput()
|
||
{
|
||
// 点击选择子物体
|
||
if (enableClickToFocus && Input.GetMouseButtonDown(0) && !isObservingChild)
|
||
{
|
||
HandleObjectClick();
|
||
}
|
||
|
||
// 在子物体观察模式下,允许旋转和缩放
|
||
if (isObservingChild)
|
||
{
|
||
if (enableRotation) HandleRotationInput();
|
||
if (enableZoom) HandleZoomInput();
|
||
}
|
||
else
|
||
{
|
||
// 正常模式下的所有输入
|
||
if (enableRotation) HandleRotationInput();
|
||
if (enablePan) HandlePanInput();
|
||
if (enableZoom) HandleZoomInput();
|
||
}
|
||
|
||
// 重置按钮
|
||
if (Input.GetKeyDown(KeyCode.R))
|
||
{
|
||
ResetView();
|
||
}
|
||
/*
|
||
// 自动旋转切换
|
||
if (Input.GetKeyDown(KeyCode.A))
|
||
{
|
||
ToggleAutoRotation(!enableAutoRotation);
|
||
}*/
|
||
|
||
// 聚焦到模型
|
||
if (Input.GetKeyDown(KeyCode.F))
|
||
{
|
||
FocusOnModel();
|
||
}
|
||
}
|
||
|
||
private void HandleObjectClick()
|
||
{
|
||
if (!enableClickToFocus) return;
|
||
|
||
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
|
||
RaycastHit hit;
|
||
|
||
// 执行射线检测,使用可选择图层
|
||
if (Physics.Raycast(ray, out hit, Mathf.Infinity, selectableLayers))
|
||
{
|
||
GameObject hitObject = hit.collider.gameObject;
|
||
|
||
// 检查是否在排除的图层中
|
||
if (IsInExcludedLayers(hitObject))
|
||
{
|
||
Debug.Log($"跳过:物体 {hitObject.name} 在排除的图层中");
|
||
return;
|
||
}
|
||
|
||
// 检查是否是要排除的物体类型
|
||
if (ShouldExcludeObject(hitObject))
|
||
{
|
||
Debug.Log($"跳过:物体 {hitObject.name} 是需要排除的类型");
|
||
return;
|
||
}
|
||
|
||
// 检查是否点击了目标或目标的子物体
|
||
if (hit.collider.transform == target || IsChildOfTarget(hit.collider.transform))
|
||
{
|
||
StartObservingChild(hitObject);
|
||
}
|
||
}
|
||
}
|
||
|
||
private bool IsInExcludedLayers(GameObject obj)
|
||
{
|
||
// 检查物体是否在排除的图层中
|
||
int layerMask = 1 << obj.layer;
|
||
return (excludedLayers.value & layerMask) != 0;
|
||
}
|
||
|
||
private bool ShouldExcludeObject(GameObject obj)
|
||
{
|
||
// 检查是否需要排除摄像机
|
||
if (excludeCameras && obj.GetComponent<Camera>() != null)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
// 检查是否需要排除灯光
|
||
if (excludeLights && obj.GetComponent<Light>() != null)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
// 检查是否需要排除UI元素
|
||
if (excludeUI && (obj.GetComponent<UnityEngine.UI.Graphic>() != null ||
|
||
obj.GetComponent<UnityEngine.UI.Selectable>() != null))
|
||
{
|
||
return true;
|
||
}
|
||
|
||
// 检查是否有要排除的标签
|
||
foreach (string tag in excludeTags)
|
||
{
|
||
if (obj.CompareTag(tag))
|
||
{
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
private bool IsChildOfTarget(Transform child)
|
||
{
|
||
Transform parent = child.parent;
|
||
while (parent != null)
|
||
{
|
||
if (parent == target)
|
||
{
|
||
return true;
|
||
}
|
||
parent = parent.parent;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
private void HandleRotationInput()
|
||
{
|
||
if (!enableRotation) return;
|
||
|
||
if (Input.GetMouseButton((int)rotateButton))
|
||
{
|
||
float horizontal = Input.GetAxis("Mouse X") * rotationSpeed * mouseSensitivity;
|
||
float vertical = -Input.GetAxis("Mouse Y") * rotationSpeed * mouseSensitivity;
|
||
|
||
if (isObservingChild)
|
||
{
|
||
// 子物体观察模式:旋转相机
|
||
targetRotation.x += horizontal;
|
||
targetRotation.y += vertical;
|
||
}
|
||
else
|
||
{
|
||
// 正常模式:旋转相机
|
||
targetRotation.x += horizontal;
|
||
targetRotation.y += vertical;
|
||
}
|
||
|
||
targetRotation.y = Mathf.Clamp(targetRotation.y, verticalAngleLimit.x, verticalAngleLimit.y);
|
||
|
||
enableAutoRotation = false;
|
||
}
|
||
}
|
||
|
||
private void HandlePanInput()
|
||
{
|
||
if (!enablePan) return;
|
||
|
||
if (!isObservingChild && Input.GetMouseButton((int)panButton))
|
||
{
|
||
if (Input.GetMouseButtonDown((int)panButton))
|
||
{
|
||
lastPanPosition = Input.mousePosition;
|
||
}
|
||
|
||
Vector3 delta = Input.mousePosition - lastPanPosition;
|
||
|
||
Vector3 right = transform.right;
|
||
Vector3 up = transform.up;
|
||
right.y = 0;
|
||
up.y = 0;
|
||
|
||
Vector3 panDelta = (right * delta.x + up * delta.y) * panSpeed * 0.01f;
|
||
|
||
ApplyPanWithLimits(panDelta);
|
||
|
||
lastPanPosition = Input.mousePosition;
|
||
}
|
||
}
|
||
|
||
private void HandleZoomInput()
|
||
{
|
||
if (!enableZoom) return;
|
||
|
||
float scroll = Input.GetAxis("Mouse ScrollWheel");
|
||
if (Mathf.Abs(scroll) > 0.01f)
|
||
{
|
||
if (isObservingChild)
|
||
{
|
||
// 子物体观察模式:调整子物体观察距离
|
||
childTargetDistance -= scroll * zoomSpeed;
|
||
childTargetDistance = Mathf.Clamp(childTargetDistance, childMinZoomDistance, childMaxZoomDistance);
|
||
}
|
||
else
|
||
{
|
||
// 正常模式:调整模型观察距离
|
||
targetDistance -= scroll * zoomSpeed;
|
||
targetDistance = Mathf.Clamp(targetDistance, minZoomDistance, maxZoomDistance);
|
||
}
|
||
}
|
||
}
|
||
|
||
private void ApplyPanWithLimits(Vector3 panDelta)
|
||
{
|
||
Vector3 newOffset = targetPositionOffset - panDelta;
|
||
|
||
newOffset.x = Mathf.Clamp(newOffset.x, -maxPanDistanceX, maxPanDistanceZ);
|
||
newOffset.z = Mathf.Clamp(newOffset.z, -maxPanDistanceZ, maxPanDistanceZ);
|
||
|
||
targetPositionOffset = newOffset;
|
||
}
|
||
|
||
private void HandleTouchInput()
|
||
{
|
||
#if UNITY_IOS || UNITY_ANDROID || UNITY_WEBGL
|
||
if (Input.touchCount == 1)
|
||
{
|
||
Touch touch = Input.GetTouch(0);
|
||
|
||
if (touch.phase == TouchPhase.Moved)
|
||
{
|
||
Vector2 delta = touch.deltaPosition;
|
||
|
||
targetRotation.x += delta.x * rotationSpeed * touchSensitivity * 0.1f;
|
||
targetRotation.y -= delta.y * rotationSpeed * touchSensitivity * 0.1f;
|
||
|
||
targetRotation.y = Mathf.Clamp(targetRotation.y, verticalAngleLimit.x, verticalAngleLimit.y);
|
||
}
|
||
}
|
||
else if (Input.touchCount == 2)
|
||
{
|
||
Touch touchZero = Input.GetTouch(0);
|
||
Touch touchOne = Input.GetTouch(1);
|
||
|
||
Vector2 touchZeroPrevPos = touchZero.position - touchZero.deltaPosition;
|
||
Vector2 touchOnePrevPos = touchOne.position - touchOne.deltaPosition;
|
||
|
||
float prevTouchDeltaMag = (touchZeroPrevPos - touchOnePrevPos).magnitude;
|
||
float touchDeltaMag = (touchZero.position - touchOne.position).magnitude;
|
||
|
||
float deltaMagnitudeDiff = prevTouchDeltaMag - touchDeltaMag;
|
||
|
||
if (isObservingChild)
|
||
{
|
||
childTargetDistance += deltaMagnitudeDiff * zoomSpeed * 0.01f;
|
||
childTargetDistance = Mathf.Clamp(childTargetDistance, childMinZoomDistance, childMaxZoomDistance);
|
||
}
|
||
else
|
||
{
|
||
targetDistance += deltaMagnitudeDiff * zoomSpeed * 0.01f;
|
||
targetDistance = Mathf.Clamp(targetDistance, minZoomDistance, maxZoomDistance);
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
|
||
private void StartObservingChild(GameObject child)
|
||
{
|
||
if (isObservingChild || !enableClickToFocus) return;
|
||
|
||
/* target.gameObject.SetActive(false);
|
||
|
||
// 保存当前状态
|
||
savedTargetPositionOffset = targetPositionOffset;
|
||
savedTargetRotation = targetRotation;
|
||
savedTargetDistance = targetDistance;
|
||
savedCurrentDistance = currentDistance;
|
||
savedEnableAutoRotation = enableAutoRotation;
|
||
|
||
currentObservedChild = child;
|
||
originalTargetPosition = target.position;
|
||
originalChildPosition = child.transform.position;
|
||
originalChildRotation = child.transform.rotation;
|
||
originalParent = child.transform.parent;
|
||
|
||
// 计算子物体的包围盒
|
||
Bounds bounds = CalculateBounds(child);
|
||
Vector3 childCenter = bounds.center;
|
||
// 复制子物体
|
||
childInstance = Instantiate(child, childCenter, child.transform.rotation);
|
||
|
||
//childInstance.transform.SetLocalPositionAndRotation(default, default);
|
||
|
||
|
||
// 删除所有子物体上的脚本,避免干扰
|
||
MonoBehaviour[] scripts = childInstance.GetComponentsInChildren<MonoBehaviour>();
|
||
foreach (MonoBehaviour script in scripts)
|
||
{
|
||
Destroy(script);
|
||
}
|
||
|
||
// 隐藏原来的子物体
|
||
//child.SetActive(false);
|
||
|
||
// 高亮显示复制出的物体
|
||
HighlightObject(childInstance);
|
||
|
||
// 计算适合观察的距离
|
||
float objectSize = Mathf.Max(bounds.size.x, bounds.size.y, bounds.size.z);
|
||
childTargetDistance = objectSize * 1.5f;
|
||
childTargetDistance = Mathf.Clamp(childTargetDistance, childMinZoomDistance, childMaxZoomDistance);
|
||
childCurrentDistance = childTargetDistance;
|
||
|
||
// 设置子物体观察模式参数
|
||
isObservingChild = true;
|
||
targetDistance = childTargetDistance;
|
||
currentDistance = childTargetDistance;
|
||
targetPositionOffset = Vector3.zero;
|
||
enableAutoRotation = false;
|
||
|
||
// 将相机对准子物体中心
|
||
Vector3 toCamera = (transform.position - childCenter).normalized;
|
||
targetRotation.x = Mathf.Atan2(toCamera.x, toCamera.z) * Mathf.Rad2Deg;
|
||
targetRotation.y = Mathf.Asin(toCamera.y) * Mathf.Rad2Deg;
|
||
|
||
Debug.Log($"开始观察子物体: {child.name}");
|
||
|
||
// --- 新增:触发开始观察事件 ---
|
||
if (OnChildObservationStarted != null)
|
||
{
|
||
OnChildObservationStarted(childInstance, child);
|
||
}
|
||
*/
|
||
if (SK.Framework.UIView.Get<EquipmentChildStructureView>())
|
||
{
|
||
SK.Framework.UIView.Show<EquipmentChildStructureView>().ShowDeviceInformation(child);
|
||
}
|
||
else
|
||
{
|
||
SK.Framework.UIView.Load<EquipmentChildStructureView>(SK.Framework.ViewLevel.POP).ShowDeviceInformation(child);
|
||
}
|
||
}
|
||
|
||
private Bounds CalculateBounds(GameObject obj)
|
||
{
|
||
Renderer[] renderers = obj.GetComponentsInChildren<Renderer>();
|
||
if (renderers.Length == 0)
|
||
{
|
||
return new Bounds(obj.transform.position, Vector3.one);
|
||
}
|
||
|
||
Bounds bounds = renderers[0].bounds;
|
||
foreach (Renderer renderer in renderers)
|
||
{
|
||
bounds.Encapsulate(renderer.bounds);
|
||
}
|
||
|
||
return bounds;
|
||
}
|
||
|
||
private void HighlightObject(GameObject obj)
|
||
{
|
||
// 获取所有渲染器
|
||
highlightedRenderers = obj.GetComponentsInChildren<Renderer>();
|
||
|
||
// 保存原始材质并创建高亮材质
|
||
originalMaterials.Clear();
|
||
highlightMaterials.Clear();
|
||
|
||
foreach (Renderer renderer in highlightedRenderers)
|
||
{
|
||
Material[] originalMats = renderer.materials;
|
||
originalMaterials.Add(originalMats);
|
||
|
||
Material[] highlightMats = new Material[originalMats.Length];
|
||
for (int i = 0; i < originalMats.Length; i++)
|
||
{
|
||
Material highlightMat = new Material(originalMats[i]);
|
||
highlightMat.EnableKeyword("_EMISSION");
|
||
highlightMat.SetColor("_EmissionColor", highlightColor);
|
||
highlightMats[i] = highlightMat;
|
||
}
|
||
|
||
highlightMaterials.Add(highlightMats);
|
||
renderer.materials = highlightMats;
|
||
}
|
||
|
||
highlightTimer = 0f;
|
||
}
|
||
|
||
private void UpdateHighlight()
|
||
{
|
||
if (highlightedRenderers == null || highlightTimer >= 1f) return;
|
||
|
||
highlightTimer += Time.deltaTime * highlightFadeSpeed;
|
||
|
||
// 渐变高亮效果
|
||
for (int i = 0; i < highlightedRenderers.Length; i++)
|
||
{
|
||
Renderer renderer = highlightedRenderers[i];
|
||
Material[] highlightMats = highlightMaterials[i];
|
||
|
||
for (int j = 0; j < highlightMats.Length; j++)
|
||
{
|
||
if (highlightMats[j] != null)
|
||
{
|
||
Color emissionColor = Color.Lerp(highlightColor, Color.black, highlightTimer);
|
||
highlightMats[j].SetColor("_EmissionColor", emissionColor);
|
||
}
|
||
}
|
||
|
||
renderer.materials = highlightMats;
|
||
}
|
||
|
||
// 高亮结束后清理
|
||
if (highlightTimer >= 1f)
|
||
{
|
||
RestoreOriginalMaterials();
|
||
}
|
||
}
|
||
|
||
private void RestoreOriginalMaterials()
|
||
{
|
||
if (highlightedRenderers != null)
|
||
{
|
||
for (int i = 0; i < highlightedRenderers.Length; i++)
|
||
{
|
||
if (highlightedRenderers[i] != null && i < originalMaterials.Count)
|
||
{
|
||
highlightedRenderers[i].materials = originalMaterials[i];
|
||
}
|
||
}
|
||
}
|
||
|
||
highlightedRenderers = null;
|
||
originalMaterials.Clear();
|
||
highlightMaterials.Clear();
|
||
}
|
||
|
||
private void ExitChildObservationMode()
|
||
{
|
||
if (!isObservingChild) return;
|
||
|
||
// 删除复制的物体
|
||
if (childInstance != null)
|
||
{
|
||
Destroy(childInstance);
|
||
childInstance = null;
|
||
}
|
||
|
||
// 恢复原子物体的显示
|
||
if (currentObservedChild != null)
|
||
{
|
||
currentObservedChild.SetActive(true);
|
||
}
|
||
|
||
// 恢复原始状态
|
||
targetPositionOffset = savedTargetPositionOffset;
|
||
targetRotation = savedTargetRotation;
|
||
targetDistance = savedTargetDistance;
|
||
currentDistance = savedCurrentDistance;
|
||
enableAutoRotation = savedEnableAutoRotation;
|
||
|
||
// 触发结束观察事件 (在清理引用前)
|
||
if (OnChildObservationEnded != null)
|
||
{
|
||
OnChildObservationEnded(childInstance, currentObservedChild);
|
||
}
|
||
|
||
// 重置状态
|
||
isObservingChild = false;
|
||
currentObservedChild = null;
|
||
|
||
// 恢复原始高亮材质
|
||
RestoreOriginalMaterials();
|
||
|
||
target.gameObject.SetActive(true);
|
||
|
||
Debug.Log("退出子物体观察模式");
|
||
}
|
||
|
||
private void UpdateCameraPosition()
|
||
{
|
||
if (enableAutoRotation && !isObservingChild)
|
||
{
|
||
targetRotation.x += autoRotationSpeed;
|
||
}
|
||
|
||
// 平滑旋转
|
||
currentRotation = Vector3.Lerp(currentRotation, targetRotation, rotationSmoothness * Time.deltaTime);
|
||
|
||
if (isObservingChild)
|
||
{
|
||
// 子物体观察模式:平滑子物体观察距离
|
||
childCurrentDistance = Mathf.Lerp(childCurrentDistance, childTargetDistance, rotationSmoothness * Time.deltaTime);
|
||
currentDistance = childCurrentDistance;
|
||
}
|
||
else
|
||
{
|
||
// 正常模式:平滑模型观察距离
|
||
currentDistance = Mathf.Lerp(currentDistance, targetDistance, rotationSmoothness * Time.deltaTime);
|
||
}
|
||
|
||
// 在子物体观察模式下,不更新平移偏移
|
||
if (!isObservingChild)
|
||
{
|
||
currentTargetPositionOffset = Vector3.Lerp(currentTargetPositionOffset, targetPositionOffset, rotationSmoothness * Time.deltaTime);
|
||
}
|
||
|
||
Quaternion rotation = Quaternion.Euler(currentRotation.y, currentRotation.x, 0);
|
||
|
||
// 计算观察中心
|
||
Vector3 lookAtPoint = target.position;
|
||
if (isObservingChild && childInstance != null)
|
||
{
|
||
lookAtPoint = childInstance.transform.position;
|
||
}
|
||
else
|
||
{
|
||
lookAtPoint += currentTargetPositionOffset;
|
||
}
|
||
|
||
Vector3 position = rotation * new Vector3(0, 0, -currentDistance) + lookAtPoint;
|
||
|
||
transform.position = position;
|
||
transform.LookAt(lookAtPoint);
|
||
}
|
||
|
||
public void ResetView()
|
||
{
|
||
if (isObservingChild)
|
||
{
|
||
ExitChildObservationMode();
|
||
}
|
||
|
||
targetRotation = new Vector3(0, 45f, 0);
|
||
targetDistance = defaultZoomDistance;
|
||
targetPositionOffset = Vector3.zero;
|
||
enableAutoRotation = false;
|
||
}
|
||
|
||
public void FocusOnModel()
|
||
{
|
||
if (target != null)
|
||
{
|
||
targetPositionOffset = Vector3.zero;
|
||
}
|
||
}
|
||
|
||
public void SetTarget(Transform newTarget)
|
||
{
|
||
target = newTarget;
|
||
ResetView();
|
||
}
|
||
|
||
public void SetTarget(GameObject newTarget)
|
||
{
|
||
if (newTarget != null)
|
||
{
|
||
target = newTarget.transform;
|
||
ResetView();
|
||
}
|
||
}
|
||
|
||
public void SetTargetOffset(Vector3 offset)
|
||
{
|
||
targetPositionOffset = new Vector3(
|
||
Mathf.Clamp(offset.x, -maxPanDistanceX, maxPanDistanceZ),
|
||
offset.y,
|
||
Mathf.Clamp(offset.z, -maxPanDistanceZ, maxPanDistanceZ)
|
||
);
|
||
}
|
||
|
||
public void SetZoomDistance(float distance)
|
||
{
|
||
if (isObservingChild)
|
||
{
|
||
childTargetDistance = Mathf.Clamp(distance, childMinZoomDistance, childMaxZoomDistance);
|
||
}
|
||
else
|
||
{
|
||
targetDistance = Mathf.Clamp(distance, minZoomDistance, maxZoomDistance);
|
||
}
|
||
}
|
||
|
||
public void SetRotation(float horizontal, float vertical)
|
||
{
|
||
targetRotation = new Vector3(horizontal, Mathf.Clamp(vertical, verticalAngleLimit.x, verticalAngleLimit.y), 0);
|
||
}
|
||
|
||
public void ToggleAutoRotation(bool enable)
|
||
{
|
||
enableAutoRotation = enable;
|
||
}
|
||
|
||
public void SetAutoRotationSpeed(float speed)
|
||
{
|
||
autoRotationSpeed = speed;
|
||
}
|
||
|
||
public void SetPanLimits(float maxX, float maxZ)
|
||
{
|
||
maxPanDistanceX = Mathf.Max(0, maxX);
|
||
maxPanDistanceZ = Mathf.Max(0, maxZ);
|
||
}
|
||
|
||
public float GetCurrentZoomDistance()
|
||
{
|
||
return isObservingChild ? childCurrentDistance : currentDistance;
|
||
}
|
||
|
||
public Vector3 GetCurrentPanOffset()
|
||
{
|
||
return currentTargetPositionOffset;
|
||
}
|
||
|
||
public bool IsObservingChild()
|
||
{
|
||
return isObservingChild;
|
||
}
|
||
|
||
public GameObject GetCurrentObservedChild()
|
||
{
|
||
return currentObservedChild;
|
||
}
|
||
|
||
public void AddExcludedTag(string tag)
|
||
{
|
||
if (!System.Array.Exists(excludeTags, t => t == tag))
|
||
{
|
||
System.Array.Resize(ref excludeTags, excludeTags.Length + 1);
|
||
excludeTags[excludeTags.Length - 1] = tag;
|
||
}
|
||
}
|
||
|
||
public void RemoveExcludedTag(string tag)
|
||
{
|
||
int index = System.Array.IndexOf(excludeTags, tag);
|
||
if (index >= 0)
|
||
{
|
||
for (int i = index; i < excludeTags.Length - 1; i++)
|
||
{
|
||
excludeTags[i] = excludeTags[i + 1];
|
||
}
|
||
System.Array.Resize(ref excludeTags, excludeTags.Length - 1);
|
||
}
|
||
}
|
||
|
||
// 新增的公共方法,用于外部控制功能开关
|
||
public void SetEnableClickToFocus(bool enable)
|
||
{
|
||
enableClickToFocus = enable;
|
||
if (!enable && isObservingChild)
|
||
{
|
||
ExitChildObservationMode();
|
||
}
|
||
}
|
||
|
||
public bool GetEnableClickToFocus()
|
||
{
|
||
return enableClickToFocus;
|
||
}
|
||
|
||
public void SetEnableRotation(bool enable)
|
||
{
|
||
enableRotation = enable;
|
||
}
|
||
|
||
public bool GetEnableRotation()
|
||
{
|
||
return enableRotation;
|
||
}
|
||
|
||
public void SetEnablePan(bool enable)
|
||
{
|
||
enablePan = enable;
|
||
}
|
||
|
||
public bool GetEnablePan()
|
||
{
|
||
return enablePan;
|
||
}
|
||
|
||
public void SetEnableZoom(bool enable)
|
||
{
|
||
enableZoom = enable;
|
||
}
|
||
|
||
public bool GetEnableZoom()
|
||
{
|
||
return enableZoom;
|
||
}
|
||
|
||
void OnGUI()
|
||
{
|
||
if (Application.isEditor && isOpenOnGUI)
|
||
{
|
||
int yOffset = 10;
|
||
|
||
GUI.Label(new Rect(10, yOffset, 300, 20),
|
||
$"上帝视角观察器 - 模型查看器");
|
||
yOffset += 20;
|
||
|
||
if (target != null)
|
||
{
|
||
GUI.Label(new Rect(10, yOffset, 300, 20),
|
||
$"目标: {target.name}");
|
||
}
|
||
else
|
||
{
|
||
GUI.Label(new Rect(10, yOffset, 300, 20),
|
||
"目标: 未设置 (请在Inspector中指定)");
|
||
}
|
||
yOffset += 20;
|
||
|
||
if (isObservingChild && currentObservedChild != null)
|
||
{
|
||
GUI.Label(new Rect(10, yOffset, 300, 20),
|
||
$"正在观察: {currentObservedChild.name} (子物体模式)");
|
||
yOffset += 20;
|
||
}
|
||
|
||
GUI.Label(new Rect(10, yOffset, 300, 20),
|
||
$"旋转: ({currentRotation.x:F1}, {currentRotation.y:F1})");
|
||
yOffset += 20;
|
||
|
||
GUI.Label(new Rect(10, yOffset, 300, 20),
|
||
$"距离: {GetCurrentZoomDistance():F1} (范围: {minZoomDistance:F1}-{maxZoomDistance:F1})");
|
||
yOffset += 20;
|
||
|
||
if (!isObservingChild)
|
||
{
|
||
GUI.Label(new Rect(10, yOffset, 300, 20),
|
||
$"平移偏移: X={currentTargetPositionOffset.x:F2}, Z={currentTargetPositionOffset.z:F2}");
|
||
yOffset += 20;
|
||
|
||
GUI.Label(new Rect(10, yOffset, 300, 20),
|
||
$"平移限制: X=±{maxPanDistanceX:F1}, Z=±{maxPanDistanceZ:F1}");
|
||
yOffset += 20;
|
||
}
|
||
|
||
GUI.Label(new Rect(10, yOffset, 300, 20),
|
||
$"自动旋转: {(enableAutoRotation ? "开" : "关")}");
|
||
yOffset += 20;
|
||
|
||
GUI.Label(new Rect(10, yOffset, 300, 20),
|
||
$"点击聚焦: {(enableClickToFocus ? "开" : "关")}");
|
||
yOffset += 20;
|
||
|
||
GUI.Label(new Rect(10, yOffset, 300, 20),
|
||
$"旋转功能: {(enableRotation ? "开" : "关")}");
|
||
yOffset += 20;
|
||
|
||
GUI.Label(new Rect(10, yOffset, 300, 20),
|
||
$"平移功能: {(enablePan ? "开" : "关")}");
|
||
yOffset += 20;
|
||
|
||
GUI.Label(new Rect(10, yOffset, 300, 20),
|
||
$"缩放功能: {(enableZoom ? "开" : "关")}");
|
||
yOffset += 20;
|
||
|
||
yOffset += 10;
|
||
|
||
GUI.Label(new Rect(10, yOffset, 400, 20),
|
||
"控制说明:");
|
||
yOffset += 20;
|
||
|
||
if (enableClickToFocus)
|
||
{
|
||
GUI.Label(new Rect(10, yOffset, 400, 20),
|
||
"左键点击: 选择子物体观察 (已排除UI/摄像机/灯光)");
|
||
yOffset += 20;
|
||
}
|
||
|
||
if (isObservingChild)
|
||
{
|
||
if (enableRotation)
|
||
{
|
||
GUI.Label(new Rect(10, yOffset, 400, 20),
|
||
"左键拖拽: 旋转子物体");
|
||
yOffset += 20;
|
||
}
|
||
|
||
if (enableZoom)
|
||
{
|
||
GUI.Label(new Rect(10, yOffset, 400, 20),
|
||
"滚轮: 缩小子物体观察距离");
|
||
yOffset += 20;
|
||
}
|
||
|
||
GUI.Label(new Rect(10, yOffset, 400, 20),
|
||
"Shift键: 退出子物体观察模式");
|
||
yOffset += 20;
|
||
}
|
||
else
|
||
{
|
||
if (enableRotation)
|
||
{
|
||
GUI.Label(new Rect(10, yOffset, 400, 20),
|
||
"左键拖拽: 旋转视角");
|
||
yOffset += 20;
|
||
}
|
||
|
||
if (enablePan)
|
||
{
|
||
GUI.Label(new Rect(10, yOffset, 400, 20),
|
||
"中键拖拽: 平移视角 (有距离限制)");
|
||
yOffset += 20;
|
||
}
|
||
|
||
if (enableZoom)
|
||
{
|
||
GUI.Label(new Rect(10, yOffset, 400, 20),
|
||
"滚轮: 缩放 (有距离限制)");
|
||
yOffset += 20;
|
||
}
|
||
}
|
||
|
||
GUI.Label(new Rect(10, yOffset, 400, 20),
|
||
"R键: 重置视角");
|
||
yOffset += 20;
|
||
|
||
GUI.Label(new Rect(10, yOffset, 400, 20),
|
||
"A键: 切换自动旋转");
|
||
yOffset += 20;
|
||
|
||
GUI.Label(new Rect(10, yOffset, 400, 20),
|
||
"F键: 聚焦到模型中心");
|
||
yOffset += 20;
|
||
}
|
||
}
|
||
|
||
void OnDrawGizmosSelected()
|
||
{
|
||
if (target != null && showPanBoundary && !isObservingChild)
|
||
{
|
||
Vector3 center = target.position;
|
||
|
||
Gizmos.color = Color.yellow;
|
||
Gizmos.DrawWireSphere(center, 0.5f);
|
||
|
||
Gizmos.color = new Color(1f, 0.5f, 0f, 0.3f);
|
||
Vector3 size = new Vector3(maxPanDistanceX * 2, 0.1f, maxPanDistanceZ * 2);
|
||
Gizmos.DrawWireCube(center, size);
|
||
|
||
Gizmos.color = new Color(0f, 1f, 0f, 0.5f);
|
||
Vector3 currentOffset = target.position + currentTargetPositionOffset;
|
||
Gizmos.DrawWireSphere(currentOffset, 0.3f);
|
||
Gizmos.DrawLine(center, currentOffset);
|
||
|
||
Gizmos.color = Color.red;
|
||
Vector3 cameraPos = transform.position;
|
||
Gizmos.DrawLine(currentOffset, cameraPos);
|
||
|
||
DrawBoundaryCorners(center);
|
||
}
|
||
}
|
||
|
||
private void DrawBoundaryCorners(Vector3 center)
|
||
{
|
||
Gizmos.color = Color.cyan;
|
||
|
||
Vector3[] corners = new Vector3[4];
|
||
corners[0] = center + new Vector3(-maxPanDistanceX, 0, -maxPanDistanceZ);
|
||
corners[1] = center + new Vector3(maxPanDistanceX, 0, -maxPanDistanceZ);
|
||
corners[2] = center + new Vector3(maxPanDistanceX, 0, maxPanDistanceZ);
|
||
corners[3] = center + new Vector3(-maxPanDistanceX, 0, maxPanDistanceZ);
|
||
|
||
for (int i = 0; i < 4; i++)
|
||
{
|
||
Gizmos.DrawSphere(corners[i], 0.2f);
|
||
Gizmos.DrawLine(corners[i], corners[(i + 1) % 4]);
|
||
}
|
||
}
|
||
|
||
void OnDestroy()
|
||
{
|
||
// 清理高亮材质
|
||
RestoreOriginalMaterials();
|
||
}
|
||
}
|