using UnityEngine; using System.Collections; using System.Collections.Generic; public class LineShowModel : MonoBehaviour { public enum DrawingState { Idle, // 空闲状态 SelectingStart, // 选择起点 SelectingEnd // 选择终点 } [Header("连线设置")] public Material wireMaterial; public Color wireColor = Color.red; public float wireWidth = 0.02f; [Header("吸附设置")] public bool enableSnapping = true; // 启用吸附功能 public float snapDistance = 1.0f; // 吸附距离 public LayerMask snapLayers = -1; // 可吸附的层 public bool snapToColliderCenter = true; // 吸附到碰撞体中心 public Color snapHighlightColor = Color.cyan; // 吸附高亮颜色 [Header("圆柱体连线设置")] public bool useCylinderWire = true; // 启用圆柱体连线 public Material cylinderWireMaterial; public float cylinderWireDiameter = 0.02f; public int cylinderSegments = 8; // 圆柱体面数,影响性能 [Header("预览设置")] public Material previewMaterial; // 预览线材质 public Color previewColor = new Color(1, 1, 1, 0.5f); // 预览线颜色(半透明) public float previewWireDiameter = 0.015f; // 预览线直径 [Header("删除设置")] public KeyCode deleteKey = KeyCode.Mouse2; // 默认为鼠标右键 public float deleteDetectionRadius = 0.5f; // 删除检测半径 [Header("状态显示")] public DrawingState currentState = DrawingState.Idle; // 连线数据 private Vector3 startPoint; private Vector3 endPoint; private GameObject currentWireObject; private GameObject previewWireObject; // 预览连线对象 // 吸附相关 private GameObject snapTarget; // 当前吸附目标 private Renderer snapTargetRenderer; // 吸附目标的渲染器 private Color snapTargetOriginalColor; // 吸附目标原始颜色 private bool isSnapTargetHighlighted = false; // 存储所有已创建的连线 private List allWires = new List(); // 起点和终点的碰撞体 private GameObject startColliderObject; private GameObject endColliderObject; // 单例模式,便于外部调用 public static LineShowModel Instance { get; private set; } void Awake() { // 设置单例 if (Instance == null) { Instance = this; } else { Destroy(gameObject); } } void Update() { HandleInput(); if (currentState == DrawingState.SelectingEnd) { UpdateWirePreview(); } // 在选择终点状态时检查吸附 if (currentState == DrawingState.SelectingEnd && enableSnapping) { CheckForSnapTargets(); } else { ClearSnapHighlight(); } } void HandleInput() { if (Input.GetMouseButtonDown(0)) // 左键点击 { HandleMouseClick(); } if (Input.GetKeyDown(KeyCode.Escape)) // 取消当前操作 { CancelDrawing(); } if (Input.GetKeyDown(deleteKey)) // 删除操作 { HandleDelete(); } } void HandleMouseClick() { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; // 首先检查是否有吸附目标 if (snapTarget != null) { HandleSnapTargetClick(); } else if (Physics.Raycast(ray, out hit, Mathf.Infinity, snapLayers)) { HandleColliderClick(hit.collider.gameObject, hit.point); } else { // 如果没有击中任何物体,且不在选择起点状态,则忽略 if (currentState == DrawingState.Idle) { Debug.Log("请点击一个碰撞体作为连线起点"); } } } void HandleSnapTargetClick() { if (snapTarget == null) return; switch (currentState) { case DrawingState.Idle: // 选择起点 StartNewWire(snapTarget); break; case DrawingState.SelectingEnd: // 选择终点 CompleteWire(snapTarget); break; } } void HandleColliderClick(GameObject colliderObject, Vector3 hitPoint) { switch (currentState) { case DrawingState.Idle: // 选择起点 StartNewWire(colliderObject); break; case DrawingState.SelectingEnd: // 选择终点 CompleteWire(colliderObject); break; } } void StartNewWire(GameObject startCollider) { if (startCollider == null) return; startColliderObject = startCollider; startPoint = GetObjectCenter(startCollider); currentState = DrawingState.SelectingEnd; // 创建预览连线 CreatePreviewWire(); // 高亮起点碰撞体 HighlightObject(startCollider); Debug.Log($"选择起点: {startCollider.name}"); } void CompleteWire(GameObject endCollider) { if (endCollider == null || startColliderObject == null) return; // 检查是否连接到自身 if (endCollider == startColliderObject) { Debug.LogWarning("不能连接到自身!"); CancelDrawing(); return; } endColliderObject = endCollider; endPoint = GetObjectCenter(endCollider); // 创建最终的连线 GameObject newWire = CreateFinalWire(); currentState = DrawingState.Idle; // 清除预览和高亮 ClearPreviewWire(); ClearSnapHighlight(); Debug.Log($"选择终点: {endCollider.name}, 连线完成"); // 返回新创建的连线对象,供外部使用 OnWireCreated?.Invoke(newWire, startColliderObject, endColliderObject); } void CreatePreviewWire() { // 清除之前的预览 ClearPreviewWire(); previewWireObject = new GameObject("PreviewWire"); if (useCylinderWire) { CreateCylinderPreviewWire(); } else { CreateLineRendererPreviewWire(); } } void CreateCylinderPreviewWire() { // 创建预览圆柱体 GameObject cylinder = GameObject.CreatePrimitive(PrimitiveType.Cylinder); cylinder.name = "PreviewCylinder"; cylinder.transform.SetParent(previewWireObject.transform); // 设置预览材质 Renderer renderer = cylinder.GetComponent(); if (previewMaterial != null) { renderer.material = previewMaterial; } else { // 创建默认预览材质 Material defaultPreviewMaterial = new Material(Shader.Find("Standard")); defaultPreviewMaterial.color = previewColor; renderer.material = defaultPreviewMaterial; } // 初始位置和缩放 cylinder.transform.position = startPoint; cylinder.transform.localScale = new Vector3( previewWireDiameter, 0.001f, // 初始高度很小 previewWireDiameter ); // 移除碰撞器,避免干扰 Destroy(cylinder.GetComponent()); } void CreateLineRendererPreviewWire() { // 使用LineRenderer作为备选预览 LineRenderer lr = previewWireObject.AddComponent(); lr.positionCount = 2; lr.SetPosition(0, startPoint); lr.SetPosition(1, startPoint); // 配置LineRenderer if (previewMaterial != null) { lr.material = previewMaterial; } else { lr.material = new Material(Shader.Find("Sprites/Default")); } lr.startColor = previewColor; lr.endColor = previewColor; lr.startWidth = previewWireDiameter; lr.endWidth = previewWireDiameter; lr.useWorldSpace = true; } void UpdateWirePreview() { if (currentState == DrawingState.SelectingEnd && previewWireObject != null) { Vector3 targetPoint; if (snapTarget != null) { targetPoint = GetObjectCenter(snapTarget); } else { targetPoint = GetMouseWorldPosition(); } if (useCylinderWire) { UpdateCylinderPreviewWire(startPoint, targetPoint); } else { UpdateLineRendererPreviewWire(startPoint, targetPoint); } } } void UpdateCylinderPreviewWire(Vector3 start, Vector3 end) { Transform cylinderTransform = previewWireObject.transform.Find("PreviewCylinder"); if (cylinderTransform == null) return; GameObject cylinder = cylinderTransform.gameObject; // 计算连线的方向、长度和中点 Vector3 direction = end - start; float distance = direction.magnitude; if (distance < 0.001f) { // 距离太短,隐藏圆柱体 cylinder.transform.localScale = new Vector3(previewWireDiameter, 0.001f, previewWireDiameter); return; } Vector3 midPoint = (start + end) / 2f; // 设置圆柱体的位置和旋转 cylinder.transform.position = midPoint; cylinder.transform.up = direction.normalized; // 设置圆柱体的缩放 cylinder.transform.localScale = new Vector3( previewWireDiameter, distance / 2f, previewWireDiameter ); } void UpdateLineRendererPreviewWire(Vector3 start, Vector3 end) { LineRenderer lr = previewWireObject.GetComponent(); if (lr != null) { lr.SetPosition(0, start); lr.SetPosition(1, end); } } GameObject CreateFinalWire() { if (startColliderObject == null || endColliderObject == null) return null; GameObject wireObject = new GameObject($"Wire_{System.DateTime.Now:yyyyMMddHHmmss}"); if (useCylinderWire) { CreateCylinderWire(wireObject, startPoint, endPoint); } else { CreateLineRendererWire(wireObject, startPoint, endPoint); } // 添加连线数据组件 LineShowModelCylinderWireData wireData = wireObject.AddComponent(); wireData.startPoint = startPoint; wireData.endPoint = endPoint; wireData.startCollider = startColliderObject; wireData.endCollider = endColliderObject; wireData.wireDiameter = cylinderWireDiameter; wireData.creationTime = System.DateTime.Now; // 添加到连线列表 allWires.Add(wireObject); StartCoroutine(LineManager.instance.Checkconnection(startColliderObject.transform, endColliderObject.transform)); // 重置起点和终点 startColliderObject = null; endColliderObject = null; return wireObject; } void CreateCylinderWire(GameObject wireObject, Vector3 start, Vector3 end) { GameObject cylinder = GameObject.CreatePrimitive(PrimitiveType.Cylinder); cylinder.name = "WireCylinder"; cylinder.transform.SetParent(wireObject.transform); // 设置材质 if (cylinderWireMaterial != null) { Renderer renderer = cylinder.GetComponent(); renderer.material = cylinderWireMaterial; } else if (wireMaterial != null) { Renderer renderer = cylinder.GetComponent(); renderer.material = wireMaterial; } // 计算连线的方向、长度和中点 Vector3 direction = end - start; float distance = direction.magnitude; Vector3 midPoint = (start + end) / 2f; // 设置圆柱体的位置和旋转 cylinder.transform.position = midPoint; cylinder.transform.up = direction.normalized; // 设置圆柱体的缩放 cylinder.transform.localScale = new Vector3( cylinderWireDiameter, distance / 2f, cylinderWireDiameter ); // 可选:添加碰撞器 if (cylinder.GetComponent() != null) { Destroy(cylinder.GetComponent()); } } void CreateLineRendererWire(GameObject wireObject, Vector3 start, Vector3 end) { LineRenderer lr = wireObject.AddComponent(); lr.positionCount = 2; lr.SetPosition(0, start); lr.SetPosition(1, end); // 配置LineRenderer if (wireMaterial != null) { lr.material = wireMaterial; } else { lr.material = new Material(Shader.Find("Sprites/Default")); } lr.startColor = wireColor; lr.endColor = wireColor; lr.startWidth = wireWidth; lr.endWidth = wireWidth; lr.useWorldSpace = true; } void ClearPreviewWire() { if (previewWireObject != null) { Destroy(previewWireObject); previewWireObject = null; } } void CheckForSnapTargets() { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; // 清除之前的吸附高亮 ClearSnapHighlight(); // 检查鼠标位置附近是否有可吸附的物体 if (Physics.Raycast(ray, out hit, Mathf.Infinity, snapLayers)) { GameObject hitObject = hit.collider.gameObject; // 跳过起点碰撞体 if (hitObject == startColliderObject) { return; } // 计算鼠标位置与物体中心的距离 Vector3 objectCenter = GetObjectCenter(hitObject); float distance = Vector3.Distance(GetMouseWorldPosition(), objectCenter); if (distance <= snapDistance) { // 设置吸附目标 snapTarget = hitObject; // 高亮显示可吸附的物体 HighlightSnapTarget(snapTarget); } } } Vector3 GetObjectCenter(GameObject obj) { if (obj == null) return Vector3.zero; if (snapToColliderCenter) { Collider collider = obj.GetComponent(); if (collider != null) { return collider.bounds.center; } } // 默认使用变换位置 return obj.transform.position; } void HighlightSnapTarget(GameObject target) { if (target == null) return; snapTargetRenderer = target.GetComponent(); if (snapTargetRenderer != null) { // 记录原始颜色 snapTargetOriginalColor = snapTargetRenderer.material.color; // 设置高亮颜色 snapTargetRenderer.material.color = snapHighlightColor; isSnapTargetHighlighted = true; } } void HighlightObject(GameObject obj) { if (obj == null) return; Renderer renderer = obj.GetComponent(); if (renderer != null) { // 临时高亮物体 Color originalColor = renderer.material.color; renderer.material.color = Color.green; // 可以在这里添加一个协程来在一段时间后恢复颜色 StartCoroutine(RestoreColorAfterDelay(renderer, originalColor, 1.0f)); } } IEnumerator RestoreColorAfterDelay(Renderer renderer, Color originalColor, float delay) { yield return new WaitForSeconds(delay); if (renderer != null) { renderer.material.color = originalColor; } } void ClearSnapHighlight() { if (snapTargetRenderer != null && isSnapTargetHighlighted) { snapTargetRenderer.material.color = snapTargetOriginalColor; snapTargetRenderer = null; isSnapTargetHighlighted = false; } snapTarget = null; } public void CancelDrawing() { ClearPreviewWire(); ClearSnapHighlight(); currentState = DrawingState.Idle; // 重置起点和终点 startColliderObject = null; endColliderObject = null; Debug.Log("取消连线操作"); } Vector3 GetMouseWorldPosition() { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; // 优先使用吸附目标的位置 if (snapTarget != null) { return GetObjectCenter(snapTarget); } // 其次使用射线击中的位置 if (Physics.Raycast(ray, out hit, Mathf.Infinity, snapLayers)) { return hit.point; } // 如果没有击中任何物体,使用默认平面 Plane defaultPlane = new Plane(Vector3.up, Vector3.zero); float enter; if (defaultPlane.Raycast(ray, out enter)) { return ray.GetPoint(enter); } // 最后使用射线上的一个点 return ray.origin + ray.direction * 10f; } // 公共方法:开始新连线 public void StartNewWire() { if (currentState != DrawingState.Idle) { CancelDrawing(); } currentState = DrawingState.SelectingStart; } // 公共方法:删除所有连线 public void ClearAllWires() { foreach (GameObject wire in allWires) { if (wire != null) Destroy(wire); } allWires.Clear(); Debug.Log("已清除所有连线"); // 触发事件 OnAllWiresDeleted?.Invoke(); } // 公共方法:删除最后一个连线 public void DeleteLastWire() { if (allWires.Count > 0) { GameObject lastWire = allWires[allWires.Count - 1]; DeleteWire(lastWire); } else { Debug.Log("没有可删除的连线"); } } // 公共方法:删除指定连线 public void DeleteWire(GameObject wireObject) { if (wireObject == null) return; // 获取连线数据 LineShowModelCylinderWireData wireData = wireObject.GetComponent(); GameObject startCollider = wireData?.startCollider; GameObject endCollider = wireData?.endCollider; // 从列表中移除 allWires.Remove(wireObject); // 销毁连线对象 Destroy(wireObject); Debug.Log($"删除连线: {wireObject.name}"); // 触发事件 OnWireDeleted?.Invoke(wireObject, startCollider, endCollider); } // 公共方法:根据碰撞体删除连线 public void DeleteWiresByCollider(GameObject colliderObject) { List wiresToDelete = new List(); // 查找所有连接到该碰撞体的连线 foreach (GameObject wire in allWires) { LineShowModelCylinderWireData wireData = wire?.GetComponent(); if (wireData != null && (wireData.startCollider == colliderObject || wireData.endCollider == colliderObject)) { wiresToDelete.Add(wire); } } // 删除找到的连线 foreach (GameObject wire in wiresToDelete) { DeleteWire(wire); } if (wiresToDelete.Count > 0) { Debug.Log($"删除了 {wiresToDelete.Count} 条连接到 {colliderObject.name} 的连线"); } } // 公共方法:根据两个碰撞体删除特定连线 public void DeleteWireByColliders(GameObject startCollider, GameObject endCollider) { foreach (GameObject wire in allWires) { LineShowModelCylinderWireData wireData = wire?.GetComponent(); if (wireData != null && ((wireData.startCollider == startCollider && wireData.endCollider == endCollider) || (wireData.startCollider == endCollider && wireData.endCollider == startCollider))) { DeleteWire(wire); Debug.Log($"删除了 {startCollider.name} 和 {endCollider.name} 之间的连线"); return; } } Debug.Log($"未找到 {startCollider.name} 和 {endCollider.name} 之间的连线"); } // 公共方法:设置吸附目标(用于编程控制) public void SetSnapTarget(GameObject target) { if (enableSnapping && target != null) { snapTarget = target; HighlightSnapTarget(target); } } // 公共方法:清除吸附目标 public void ClearSnapTarget() { ClearSnapHighlight(); } // 公共方法:检查物体是否可以吸附 public bool CanSnapToObject(GameObject obj) { if (obj == null) return false; // 检查物体是否在可吸附层 if (((1 << obj.layer) & snapLayers) == 0) return false; // 检查物体是否有碰撞体 Collider collider = obj.GetComponent(); if (collider == null) return false; return true; } // 公共方法:获取所有连线 public List GetAllWires() { return new List(allWires); } // 公共方法:获取连接到特定碰撞体的所有连线 public List GetWiresByCollider(GameObject colliderObject) { List connectedWires = new List(); foreach (GameObject wire in allWires) { LineShowModelCylinderWireData wireData = wire?.GetComponent(); if (wireData != null && (wireData.startCollider == colliderObject || wireData.endCollider == colliderObject)) { connectedWires.Add(wire); } } return connectedWires; } // 公共方法:检查两个碰撞体之间是否有连线 public bool AreCollidersConnected(GameObject collider1, GameObject collider2) { foreach (GameObject wire in allWires) { LineShowModelCylinderWireData wireData = wire?.GetComponent(); if (wireData != null && ((wireData.startCollider == collider1 && wireData.endCollider == collider2) || (wireData.startCollider == collider2 && wireData.endCollider == collider1))) { return true; } } return false; } // 处理删除操作 void HandleDelete() { // 如果正在绘制连线,先取消绘制 if (currentState != DrawingState.Idle) { CancelDrawing(); return; } // 查找鼠标位置附近的连线 Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(ray, out hit, Mathf.Infinity)) { GameObject hitObject = hit.collider.gameObject; // 检查是否点击到了连线 if (IsWire(hitObject)) { DeleteWire(hitObject); return; } // 检查是否是连线的子物体 if (hitObject.transform.parent != null) { GameObject parent = hitObject.transform.parent.gameObject; if (IsWire(parent)) { DeleteWire(parent); return; } } } // 如果没有找到可删除的对象,尝试删除最后一个连线 DeleteLastWire(); } // 检查对象是否是连线 bool IsWire(GameObject obj) { if (obj == null) return false; // 检查是否是连线 if (obj.name.StartsWith("Wire_") || obj.GetComponent() != null) { return true; } return false; } // 事件定义 public delegate void WireCreatedHandler(GameObject wire, GameObject startCollider, GameObject endCollider); public delegate void WireDeletedHandler(GameObject wire, GameObject startCollider, GameObject endCollider); public delegate void AllWiresDeletedHandler(); // 事件 public static event WireCreatedHandler OnWireCreated; public static event WireDeletedHandler OnWireDeleted; public static event AllWiresDeletedHandler OnAllWiresDeleted; }