using UnityEngine; using UnityEngine.UI; using System.Collections; using System.Collections.Generic; public class ModelViewerWithUIPanel : MonoBehaviour { [Header("观察目标")] [SerializeField] private Transform target; // 要观察的主模型 [Header("控制开关")] [SerializeField] private bool enableClickToFocus = true; // 是否启用点击子物体聚焦功能 [SerializeField] private bool enableMainModelControls = true; // 是否启用在正常模式下对主模型的操作 [Header("主模型观察设置")] [SerializeField] private float rotationSpeed = 5.0f; [SerializeField] private float autoRotationSpeed = 0.5f; [SerializeField] private float rotationSmoothness = 5.0f; [SerializeField] private float zoomSpeed = 5.0f; [SerializeField] private float minZoomDistance = 1.0f; [SerializeField] private float maxZoomDistance = 20.0f; [SerializeField] private float defaultZoomDistance = 5.0f; [SerializeField] private float panSpeed = 0.5f; [SerializeField] private float maxPanDistanceX = 5.0f; [SerializeField] private float maxPanDistanceZ = 5.0f; [SerializeField] private Vector2 verticalAngleLimit = new Vector2(10f, 80f); [Header("UI面板设置")] [SerializeField] private RawImage modelDisplayImage; // 左侧UI面板用于显示模型 [SerializeField] private Camera uiModelCamera; // 专门用于渲染UI中模型的相机 [SerializeField] private RenderTexture modelRenderTexture; // 渲染纹理 [SerializeField] private Transform modelDisplayPosition; // UI中模型显示位置 [Header("UI面板模型控制")] [SerializeField] private float uiModelRotationSpeed = 2.0f; [SerializeField] private float uiModelZoomSpeed = 2.0f; [SerializeField] private float uiModelMinZoom = 0.5f; [SerializeField] private float uiModelMaxZoom = 5.0f; [SerializeField] private float uiModelDefaultZoom = 2.0f; [Header("点击检测设置")] [SerializeField] private LayerMask selectableLayers = -1; // 可选择图层 [SerializeField] private LayerMask excludedLayers = 0; // 要排除的图层 [SerializeField] private string[] excludeTags = { "MainCamera", "Light", "UI" }; [Header("模型高亮设置")] [SerializeField] private Color highlightColor = new Color(0f, 0.5f, 1f, 1f); // 蓝色高亮 [SerializeField] private float highlightIntensity = 2.0f; [SerializeField] private float highlightFadeSpeed = 2.0f; [Header("UI面板显示控制")] [SerializeField] private GameObject uiPanel; // 整个UI面板 [SerializeField] private Text modelNameText; // 模型名称文本 [SerializeField] private Text modelStatusText; // 状态文本 [SerializeField] private Button closePanelButton; // 关闭面板按钮 // 主模型观察相关变量 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; // UI面板模型相关变量 private bool isUIPanelActive = false; private GameObject clonedModel = null; private GameObject currentSelectedModel = null; private Vector3 uiModelRotation = Vector3.zero; private float uiModelCurrentZoom = 2.0f; private float uiModelTargetZoom = 2.0f; // 高亮相关 private Material[] originalMaterials = null; private Material highlightMaterial = null; private float highlightTimer = 0f; private bool isHighlighting = false; // 鼠标控制相关 private Vector2 lastMousePosition = Vector2.zero; private bool isDraggingUIModel = false; void Start() { if (target == null) { Debug.LogWarning("未指定观察目标,请在Inspector中指定要观察的主模型"); } // 初始化主相机观察参数 currentDistance = defaultZoomDistance; targetDistance = defaultZoomDistance; Vector3 angles = transform.eulerAngles; currentRotation = new Vector3(angles.y, angles.x, 0); targetRotation = currentRotation; // 初始化UI面板 InitializeUIPanel(); // 创建高亮材质 CreateHighlightMaterial(); // 更新相机位置 UpdateCameraPosition(); } private void InitializeUIPanel() { if (uiPanel != null) { uiPanel.SetActive(false); } if (closePanelButton != null) { closePanelButton.onClick.AddListener(CloseUIPanel); } // 设置UI模型相机 if (uiModelCamera != null) { uiModelCamera.enabled = false; if (modelRenderTexture != null) { uiModelCamera.targetTexture = modelRenderTexture; } if (modelDisplayImage != null && modelRenderTexture != null) { modelDisplayImage.texture = modelRenderTexture; } } } private void CreateHighlightMaterial() { highlightMaterial = new Material(Shader.Find("Standard")); highlightMaterial.color = highlightColor; highlightMaterial.EnableKeyword("_EMISSION"); highlightMaterial.SetColor("_EmissionColor", highlightColor * highlightIntensity); highlightMaterial.globalIlluminationFlags = MaterialGlobalIlluminationFlags.RealtimeEmissive; } void LateUpdate() { if (target == null) return; HandleInput(); UpdateCameraPosition(); UpdateUIPanelControls(); UpdateHighlightEffect(); } private void HandleInput() { if (isUIPanelActive) { // UI面板激活时,只处理UI面板相关输入 HandleUIPanelInput(); } else { // 正常模式下,处理主模型观察输入 HandleMainModelInput(); } } private void HandleMainModelInput() { if (!enableMainModelControls) return; // 旋转控制 if (Input.GetMouseButton(0)) { float horizontal = Input.GetAxis("Mouse X") * rotationSpeed; float vertical = -Input.GetAxis("Mouse Y") * rotationSpeed; targetRotation.x += horizontal; targetRotation.y += vertical; targetRotation.y = Mathf.Clamp(targetRotation.y, verticalAngleLimit.x, verticalAngleLimit.y); } // 平移控制 if (Input.GetMouseButton(2)) // 中键 { float horizontal = Input.GetAxis("Mouse X") * panSpeed * 0.1f; float vertical = Input.GetAxis("Mouse Y") * panSpeed * 0.1f; Vector3 panDelta = new Vector3(-horizontal, 0, -vertical); ApplyPanWithLimits(panDelta); } // 缩放控制 float scroll = Input.GetAxis("Mouse ScrollWheel"); if (Mathf.Abs(scroll) > 0.01f) { targetDistance -= scroll * zoomSpeed; targetDistance = Mathf.Clamp(targetDistance, minZoomDistance, maxZoomDistance); } // 点击选择子物体 if (enableClickToFocus && Input.GetMouseButtonDown(0)) { HandleObjectClick(); } // 重置按钮 if (Input.GetKeyDown(KeyCode.R)) { ResetMainView(); } // 自动旋转切换 if (Input.GetKeyDown(KeyCode.A)) { //ToggleAutoRotation(!autoRotationSpeed > 0); } // 聚焦到模型 if (Input.GetKeyDown(KeyCode.F)) { FocusOnMainModel(); } } private void HandleUIPanelInput() { // 检查鼠标是否在UI面板区域内 if (modelDisplayImage != null && IsMouseOverUIPanel()) { // 旋转UI中的模型 if (Input.GetMouseButtonDown(0)) { isDraggingUIModel = true; lastMousePosition = Input.mousePosition; } if (Input.GetMouseButton(0) && isDraggingUIModel) { Vector2 delta = (Vector2)Input.mousePosition - lastMousePosition; uiModelRotation.x += delta.x * uiModelRotationSpeed * 0.1f; uiModelRotation.y += delta.y * uiModelRotationSpeed * 0.1f; lastMousePosition = Input.mousePosition; } if (Input.GetMouseButtonUp(0)) { isDraggingUIModel = false; } // 缩放UI中的模型 float scroll = Input.GetAxis("Mouse ScrollWheel"); if (Mathf.Abs(scroll) > 0.01f) { uiModelTargetZoom -= scroll * uiModelZoomSpeed; uiModelTargetZoom = Mathf.Clamp(uiModelTargetZoom, uiModelMinZoom, uiModelMaxZoom); } } else { isDraggingUIModel = false; } // ESC键关闭面板 if (Input.GetKeyDown(KeyCode.Escape)) { CloseUIPanel(); } } private bool IsMouseOverUIPanel() { if (modelDisplayImage == null) return false; RectTransform rectTransform = modelDisplayImage.rectTransform; Vector2 localMousePosition = rectTransform.InverseTransformPoint(Input.mousePosition); return rectTransform.rect.Contains(localMousePosition); } private void HandleObjectClick() { 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)) { return; } // 检查是否是要排除的标签 if (ShouldExcludeObjectByTag(hitObject)) { return; } // 检查是否点击了目标或目标的子物体 if (hit.collider.transform == target || IsChildOfTarget(hit.collider.transform)) { SelectModelForUIPanel(hitObject); } } } private void SelectModelForUIPanel(GameObject selectedModel) { if (isUIPanelActive) { CloseUIPanel(); } currentSelectedModel = selectedModel; // 高亮显示被选中的模型 HighlightModel(selectedModel); // 克隆模型到UI面板 CloneModelToUIPanel(selectedModel); // 显示UI面板 ShowUIPanel(); // 更新UI面板信息 UpdateUIPanelInfo(selectedModel); } private void HighlightModel(GameObject model) { // 清除之前的高亮 ClearHighlight(); // 获取模型的渲染器 Renderer renderer = model.GetComponent(); if (renderer != null) { originalMaterials = renderer.materials; Material[] newMaterials = new Material[originalMaterials.Length]; for (int i = 0; i < newMaterials.Length; i++) { newMaterials[i] = highlightMaterial; } renderer.materials = newMaterials; isHighlighting = true; highlightTimer = 0f; } } private void ClearHighlight() { if (currentSelectedModel != null && originalMaterials != null) { Renderer renderer = currentSelectedModel.GetComponent(); if (renderer != null) { renderer.materials = originalMaterials; } } originalMaterials = null; isHighlighting = false; } private void UpdateHighlightEffect() { if (!isHighlighting || currentSelectedModel == null) return; highlightTimer += Time.deltaTime * highlightFadeSpeed; if (highlightTimer >= 1f) { ClearHighlight(); } } private void CloneModelToUIPanel(GameObject model) { // 删除之前的克隆模型 if (clonedModel != null) { Destroy(clonedModel); } // 克隆模型 clonedModel = Instantiate(model, modelDisplayPosition.position, Quaternion.identity, modelDisplayPosition); // 删除不需要的组件 RemoveUnwantedComponents(clonedModel); // 计算合适的缩放比例 Bounds bounds = CalculateBounds(clonedModel); float maxSize = Mathf.Max(bounds.size.x, bounds.size.y, bounds.size.z); float scaleFactor = 1f / maxSize; clonedModel.transform.localScale = Vector3.one * scaleFactor; clonedModel.transform.localPosition = Vector3.zero; // 重置旋转 clonedModel.transform.localRotation = Quaternion.identity; uiModelRotation = Vector3.zero; // 重置缩放 uiModelCurrentZoom = uiModelDefaultZoom; uiModelTargetZoom = uiModelDefaultZoom; // 启用UI相机 if (uiModelCamera != null) { uiModelCamera.enabled = true; } } private Bounds CalculateBounds(GameObject obj) { Renderer[] renderers = obj.GetComponentsInChildren(); 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 RemoveUnwantedComponents(GameObject obj) { // 移除脚本组件 MonoBehaviour[] scripts = obj.GetComponentsInChildren(); foreach (MonoBehaviour script in scripts) { Destroy(script); } // 移除碰撞体 Collider[] colliders = obj.GetComponentsInChildren(); foreach (Collider collider in colliders) { Destroy(collider); } // 移除刚体 Rigidbody[] rigidbodies = obj.GetComponentsInChildren(); foreach (Rigidbody rb in rigidbodies) { Destroy(rb); } } private void ShowUIPanel() { if (uiPanel != null) { uiPanel.SetActive(true); isUIPanelActive = true; } } private void CloseUIPanel() { if (uiPanel != null) { uiPanel.SetActive(false); isUIPanelActive = false; } // 禁用UI相机 if (uiModelCamera != null) { uiModelCamera.enabled = false; } // 删除克隆的模型 if (clonedModel != null) { Destroy(clonedModel); clonedModel = null; } // 清除高亮 ClearHighlight(); currentSelectedModel = null; } private void UpdateUIPanelInfo(GameObject model) { if (modelNameText != null) { modelNameText.text = model.name; } if (modelStatusText != null) { // 这里可以根据需要从模型获取更多信息 modelStatusText.text = "设备状态: 正常\n在线时长: 15小时30分钟"; } } private void UpdateUIPanelControls() { if (!isUIPanelActive || clonedModel == null) return; // 应用旋转 Quaternion targetRot = Quaternion.Euler(uiModelRotation.y, -uiModelRotation.x, 0); clonedModel.transform.localRotation = Quaternion.Slerp(clonedModel.transform.localRotation, targetRot, 10f * Time.deltaTime); // 应用缩放 uiModelCurrentZoom = Mathf.Lerp(uiModelCurrentZoom, uiModelTargetZoom, 5f * Time.deltaTime); clonedModel.transform.localScale = Vector3.one * uiModelCurrentZoom; } private void UpdateCameraPosition() { // 平滑旋转 currentRotation = Vector3.Lerp(currentRotation, targetRotation, rotationSmoothness * Time.deltaTime); // 平滑距离 currentDistance = Mathf.Lerp(currentDistance, targetDistance, rotationSmoothness * Time.deltaTime); // 平滑平移 currentTargetPositionOffset = Vector3.Lerp(currentTargetPositionOffset, targetPositionOffset, rotationSmoothness * Time.deltaTime); // 计算观察中心 Vector3 lookAtPoint = target.position + currentTargetPositionOffset; // 计算相机位置 Quaternion rotation = Quaternion.Euler(currentRotation.y, currentRotation.x, 0); Vector3 position = rotation * new Vector3(0, 0, -currentDistance) + lookAtPoint; // 更新相机位置 transform.position = position; transform.LookAt(lookAtPoint); } private bool IsInExcludedLayers(GameObject obj) { int layerMask = 1 << obj.layer; return (excludedLayers.value & layerMask) != 0; } private bool ShouldExcludeObjectByTag(GameObject obj) { 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 ApplyPanWithLimits(Vector3 panDelta) { Vector3 newOffset = targetPositionOffset - panDelta; newOffset.x = Mathf.Clamp(newOffset.x, -maxPanDistanceX, maxPanDistanceX); newOffset.z = Mathf.Clamp(newOffset.z, -maxPanDistanceZ, maxPanDistanceZ); targetPositionOffset = newOffset; } public void ResetMainView() { targetRotation = new Vector3(0, 45f, 0); targetDistance = defaultZoomDistance; targetPositionOffset = Vector3.zero; } public void FocusOnMainModel() { if (target != null) { targetPositionOffset = Vector3.zero; } } public void ToggleAutoRotation(bool enable) { autoRotationSpeed = enable ? 0.5f : 0f; } public void SetEnableClickToFocus(bool enable) { enableClickToFocus = enable; } public void SetEnableMainModelControls(bool enable) { enableMainModelControls = enable; } void OnDestroy() { // 清理资源 if (highlightMaterial != null) { Destroy(highlightMaterial); } if (clonedModel != null) { Destroy(clonedModel); } ClearHighlight(); } }