using UnityEngine; using System.Collections; using System.Collections.Generic; // 连线连接数据结构 [System.Serializable] public class WireConnectionData { public GameObject startInterface; public GameObject endInterface; public GameObject startConnectionPoint; public GameObject endConnectionPoint; } public class WireDrawingSystem : 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 bool snapToTransformCenter = false; // 吸附到变换组件中心 public Color snapHighlightColor = Color.cyan; // 吸附高亮颜色 [Header("高亮设置")] public bool enableHighlight = true; // 启用点击物体高亮效果 public Color clickHighlightColor = Color.green; // 点击高亮颜色 public float highlightDuration = 1.0f; // 高亮持续时间 [Header("碰撞设置")] public bool requireColliderForConnection = true; // 要求物体必须有碰撞体才能连线 public bool addColliderToWires = false; // 是否为连线添加碰撞器 [Header("状态显示")] public DrawingState currentState = DrawingState.Idle; // 连线数据 private Vector3 startPoint; private Vector3 endPoint; private LineRenderer currentWire; private GameObject currentWireObject; // 吸附相关 private GameObject snapTarget; // 当前吸附目标 private Renderer snapTargetRenderer; // 吸附目标的渲染器 public Color snapTargetOriginalColor = Color.blue; // 吸附目标原始颜色 private bool isSnapTargetHighlighted = false; // 新增:标记是否正在高亮 // 修复问题:记录鼠标点击的实际位置 private Vector3 actualMousePosition; /// /// 接线头样式 /// [Header("接线头样式")] public GameObject LineModel; [Header("连接点模型设置")] public GameObject connectionPointPrefab; // 连接点模型预制体 public float connectionPointScale = 0.1f; // 连接点模型缩放 public Material connectionPointMaterial; // 连接点材质 [Header("吸附点预览")] public GameObject snapPreviewPrefab; // 吸附点预览预制体 private GameObject currentSnapPreview; // 当前吸附点预览 private List connectionPoints = new List(); // 存储所有连接点 [Header("删除设置")] public KeyCode deleteKey = KeyCode.Mouse1; // 默认为鼠标右键 public float deleteDetectionRadius = 0.5f; // 删除检测半径 // 存储所有已创建的连线和连接点 private List allWires = new List(); private List allConnectionPoints = new List(); [Header("模型堆叠设置")] public float verticalOffset = 0.2f; // 模型垂直偏移量 public bool enableStacking = true; // 启用模型堆叠 public int maxStackCount = 5; // 最大堆叠数量 (0表示无限制) public Color maxStackWarningColor = Color.yellow; // 达到最大堆叠数量时的警告颜色 public Color selfConnectionWarningColor = Color.magenta; // 自连接警告颜色 // 存储每个接口上的模型堆叠信息 private Dictionary> interfaceStacks = new Dictionary>(); // 用于临时存储当前操作的目标接口 private GameObject currentStartInterface; private GameObject currentEndInterface; // 防止自连接 private GameObject startInterfaceObject; private GameObject endInterfaceObject; private bool isSnapTargetHighlighteds = false; // 新增:标记是否正在高亮 // 存储所有被高亮的接口,确保都能恢复颜色 private Dictionary highlightedInterfaces = new Dictionary(); // 连线数据存储 private Dictionary wireConnectionData = new Dictionary(); //存储接口的原始材质,防止材质实例化问题 private Dictionary originalMaterials = new Dictionary(); //存储当前高亮的接口,确保不会重复高亮 private GameObject currentlyHighlightedInterface = null; // 高亮相关 private Coroutine currentHighlightCoroutine; // 当前高亮协程 private GameObject lastClickedObject; // 最后点击的物体 void Update() { HandleInput(); UpdateWirePreview(); // 在选择终点状态时检查吸附 if (currentState == DrawingState.SelectingEnd && enableSnapping) { CheckForSnapTargets(); UpdateSnapPreview(); } else { // 不在选择终点状态时,清除吸附点预览 ClearSnapPreview(); // 同时清除吸附高亮 if (currentState != DrawingState.SelectingEnd) { ClearAllHighlights(); } } } /// /// 处理点击物体高亮效果 /// void HandleClickHighlight(GameObject clickedObject) { if (!enableHighlight || clickedObject == null) return; // 停止之前的高亮协程 if (currentHighlightCoroutine != null) { StopCoroutine(currentHighlightCoroutine); } // 开始新的高亮协程 currentHighlightCoroutine = StartCoroutine(HighlightObject(clickedObject)); } /// /// 高亮物体协程 /// IEnumerator HighlightObject(GameObject targetObject) { if (targetObject == null) yield break; Renderer renderer = targetObject.GetComponent(); if (renderer == null) yield break; // 确保使用材质实例 EnsureMaterialInstance(targetObject); // 记录原始颜色 Color originalColor = renderer.material.color; if (!highlightedInterfaces.ContainsKey(targetObject)) { highlightedInterfaces[targetObject] = originalColor; } // 设置高亮颜色 renderer.material.color = clickHighlightColor; // 等待指定时间 yield return new WaitForSeconds(highlightDuration); // 恢复原始颜色 if (renderer != null && targetObject != null) { if (highlightedInterfaces.ContainsKey(targetObject)) { renderer.material.color = highlightedInterfaces[targetObject]; highlightedInterfaces.Remove(targetObject); } else { renderer.material.color = originalColor; } } currentHighlightCoroutine = null; } /// /// 在指定位置创建连接点模型,支持在已有模型上方堆叠 /// /// 基础位置 /// 是否为起点 /// 目标接口物体 bool CreateConnectionPoint(Vector3 position, bool isStartPoint, GameObject targetInterface = null) { if (connectionPointPrefab == null) { Debug.LogWarning("连接点预制体未设置,无法创建连接点模型"); return false; } // 检查堆叠数量限制 if (targetInterface != null && enableStacking && IsStackLimitReached(targetInterface)) { Debug.LogWarning($"接口 {targetInterface.name} 已达到最大堆叠数量限制 ({maxStackCount}),无法创建更多连接点"); ShowStackLimitWarning(targetInterface); return false; } // 计算最终位置 Vector3 finalPosition = CalculateStackedPosition(position, targetInterface); GameObject connectionPoint = Instantiate(connectionPointPrefab); connectionPoint.name = $"ConnectionPoint_{(isStartPoint ? "Start" : "End")}_{System.DateTime.Now:HHmmss}"; connectionPoint.transform.position = finalPosition; connectionPoint.transform.localScale = Vector3.one * connectionPointScale; // 设置材质 if (connectionPointMaterial != null) { Renderer renderer = connectionPoint.GetComponent(); if (renderer != null) { renderer.material = connectionPointMaterial; } } // 记录堆叠信息 if (targetInterface != null && enableStacking) { RegisterConnectionPointWithInterface(connectionPoint, targetInterface); } // 添加到连接点列表 allConnectionPoints.Add(connectionPoint); Debug.Log($"创建连接点: {finalPosition}, 类型: {(isStartPoint ? "起点" : "终点")}, 目标接口: {targetInterface?.name ?? "无"}, 堆叠高度: {GetStackCountForInterface(targetInterface)}"); return true; } /// /// 检查接口是否达到堆叠数量限制 /// bool IsStackLimitReached(GameObject targetInterface) { if (targetInterface == null || maxStackCount <= 0) return false; int currentCount = GetStackCountForInterface(targetInterface); return currentCount >= maxStackCount; } /// /// 检查是否尝试自连接 /// bool IsSelfConnection(GameObject targetInterface) { if (targetInterface == null || startInterfaceObject == null) return false; return targetInterface == startInterfaceObject; } /// /// 显示堆叠数量限制警告 /// void ShowStackLimitWarning(GameObject targetInterface) { // 临时高亮显示达到限制的接口 Renderer renderer = targetInterface.GetComponent(); if (renderer != null) { // 确保使用材质实例 EnsureMaterialInstance(targetInterface); // 记录原始颜色 if (!highlightedInterfaces.ContainsKey(targetInterface)) { highlightedInterfaces[targetInterface] = renderer.material.color; Debug.Log($"记录接口 {targetInterface.name} 的原始颜色(堆叠警告): {renderer.material.color}"); } // StartCoroutine(FlashWarningColor(renderer, maxStackWarningColor, targetInterface)); } } /// /// 显示自连接警告 /// void ShowSelfConnectionWarning(GameObject targetInterface) { // 临时高亮显示自连接警告 Renderer renderer = targetInterface.GetComponent(); if (renderer != null) { // 确保使用材质实例 EnsureMaterialInstance(targetInterface); // 记录原始颜色 if (!highlightedInterfaces.ContainsKey(targetInterface)) { highlightedInterfaces[targetInterface] = renderer.material.color; Debug.Log($"记录接口 {targetInterface.name} 的原始颜色(自连接警告): {renderer.material.color}"); } //StartCoroutine(FlashWarningColor(renderer, selfConnectionWarningColor, targetInterface)); } } /// /// 闪烁警告颜色 /// IEnumerator FlashWarningColor(Renderer targetRenderer, Color warningColor, GameObject targetInterface = null) { if (targetRenderer == null) yield break; // 确保使用材质实例 if (targetInterface != null) { EnsureMaterialInstance(targetInterface); } // 保存当前颜色 Color currentColor = targetRenderer.material.color; // 设置警告颜色 targetRenderer.material.color = warningColor; Debug.Log($"设置接口 {targetInterface?.name} 为闪烁警告颜色: {warningColor}"); yield return new WaitForSeconds(0.5f); if (targetRenderer != null && targetInterface != null) { // 恢复原始颜色 if (highlightedInterfaces.ContainsKey(targetInterface)) { targetRenderer.material.color = highlightedInterfaces[targetInterface]; Debug.Log($"恢复接口 {targetInterface.name} 的原始颜色: {highlightedInterfaces[targetInterface]}"); } else { targetRenderer.material.color = currentColor; Debug.Log($"恢复接口 {targetInterface.name} 的当前颜色: {currentColor}"); } } } /// /// 注册连接点到接口堆叠系统 /// void RegisterConnectionPointWithInterface(GameObject connectionPoint, GameObject targetInterface) { if (targetInterface == null) return; if (!interfaceStacks.ContainsKey(targetInterface)) { interfaceStacks[targetInterface] = new List(); Debug.Log($"为接口 {targetInterface.name} 创建新堆叠列表"); } interfaceStacks[targetInterface].Add(connectionPoint); Debug.Log($"连接点 {connectionPoint.name} 已注册到接口 {targetInterface.name},当前总数: {interfaceStacks[targetInterface].Count}"); // 添加接口引用组件 ConnectionPointInterfaceReference refComponent = connectionPoint.GetComponent(); if (refComponent == null) { refComponent = connectionPoint.AddComponent(); } refComponent.targetInterface = targetInterface; } /// /// 从接口堆叠系统中移除连接点 /// void UnregisterConnectionPointFromInterface(GameObject connectionPoint) { ConnectionPointInterfaceReference refComponent = connectionPoint.GetComponent(); if (refComponent != null && refComponent.targetInterface != null) { GameObject targetInterface = refComponent.targetInterface; if (interfaceStacks.ContainsKey(targetInterface)) { interfaceStacks[targetInterface].Remove(connectionPoint); // 如果接口没有更多连接点,移除接口条目 if (interfaceStacks[targetInterface].Count == 0) { interfaceStacks.Remove(targetInterface); } // Debug.Log($"连接点 {connectionPoint.name} 已从接口 {targetInterface.name} 移除,剩余数量: {interfaceStacks.ContainsKey(targetInterface) ? interfaceStacks[targetInterface].Count : 0}"); } } } /// /// 计算堆叠位置 /// Vector3 CalculateStackedPosition(Vector3 basePosition, GameObject targetInterface) { if (targetInterface == null || !enableStacking) { Debug.Log("没有目标接口或堆叠功能已禁用,使用基础位置"); return basePosition; } int stackCount = GetStackCountForInterface(targetInterface); Vector3 offset = Vector3.forward * (stackCount * verticalOffset); Vector3 finalPosition = GetObjectCenter(targetInterface) + offset; Debug.Log($"接口: {targetInterface.name}, 堆叠数量: {stackCount}, 基础位置: {basePosition}, 最终位置: {finalPosition}"); return finalPosition; } /// /// 获取接口上的模型堆叠数量 /// int GetStackCountForInterface(GameObject targetInterface) { if (targetInterface == null || !interfaceStacks.ContainsKey(targetInterface)) { return 0; } // 清理已销毁的模型引用 interfaceStacks[targetInterface].RemoveAll(item => item == null); return interfaceStacks[targetInterface].Count; } /// /// 删除未完成的连接点(用于取消操作时) /// void RemoveUnfinishedConnectionPoints() { // 删除最近创建但未完成的连接点 if (allConnectionPoints.Count > 0) { // 查找未附加到连线的连接点 List toRemove = new List(); foreach (GameObject point in allConnectionPoints) { if (point != null && point.transform.parent == null) { toRemove.Add(point); } } // 删除这些连接点 foreach (GameObject point in toRemove) { // 从接口堆叠系统中移除 UnregisterConnectionPointFromInterface(point); allConnectionPoints.Remove(point); Destroy(point); } } } /// /// 清除所有连接点 /// public void ClearAllConnectionPoints() { foreach (GameObject point in connectionPoints) { if (point != null) Destroy(point); } connectionPoints.Clear(); } /// /// 删除所有连线和连接点 /// public void ClearAll() { // 创建连线列表的副本,避免在遍历时修改集合 List wiresToDelete = new List(allWires); foreach (GameObject wire in wiresToDelete) { if (wire != null) DeleteWire(wire); } allWires.Clear(); // 创建连接点列表的副本 List pointsToDelete = new List(allConnectionPoints); foreach (GameObject point in pointsToDelete) { if (point != null) DeleteConnectionPoint(point); } allConnectionPoints.Clear(); // 清除堆叠信息 interfaceStacks.Clear(); // 清除连线数据 wireConnectionData.Clear(); // 清除吸附点预览 ClearSnapPreview(); // 清除所有高亮 ClearAllHighlights(); // 恢复所有接口的原始材质 List interfacesToRestore = new List(originalMaterials.Keys); foreach (GameObject interfaceObj in interfacesToRestore) { if (interfaceObj != null) { RestoreOriginalMaterial(interfaceObj); } } originalMaterials.Clear(); Debug.Log("已清除所有连线和连接点"); } /// /// 将连接点附加到连线对象上 /// /// 连线对象 void AttachConnectionPointsToWire(GameObject wireObject) { // 查找最近创建的两个未附加的连接点 List unattachedPoints = new List(); foreach (GameObject point in allConnectionPoints) { if (point != null && point.transform.parent == null) { unattachedPoints.Add(point); } } // 将最近的两个连接点附加到连线 if (unattachedPoints.Count >= 2) { for (int i = unattachedPoints.Count - 1; i >= Mathf.Max(0, unattachedPoints.Count - 2); i--) { unattachedPoints[i].transform.SetParent(wireObject.transform); } } } void HandleInput() { if (Input.GetMouseButtonDown(0)) // 左键点击 { HandleMouseClick(); } if (Input.GetKeyDown(KeyCode.Escape))// 取消当前操作 { CancelDrawing(); } if (Input.GetMouseButtonDown(1)) { HandleDelete(); } } /// /// 处理删除操作 /// void HandleDelete() { // 首先清除所有高亮,确保颜色恢复 ClearAllHighlights(); // 如果正在绘制连线,先取消绘制 if (currentState != DrawingState.Idle) { CancelDrawing(); return; } // 清除吸附点预览 ClearSnapPreview(); // 查找鼠标位置附近的连线和连接点 Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; bool foundObjectToDelete = false; if (Physics.Raycast(ray, out hit, Mathf.Infinity)) { // 检查是否点击到了连线或连接点 GameObject hitObject = hit.collider.gameObject; if (IsWireOrConnection(hitObject)) { DeleteObjectIfWireOrConnection(hitObject); foundObjectToDelete = true; } } // 如果没有找到可删除的对象,尝试删除最后一个连线 if (!foundObjectToDelete) { DeleteLastWire(); } // 删除后再次确保清除所有高亮 ClearAllHighlights(); } /// /// 检查对象是否是连线或连接点 /// bool IsWireOrConnection(GameObject obj) { if (obj == null) return false; // 检查是否是连线 if (obj.name.StartsWith("Wire_") || obj.GetComponent() != null) { return true; } // 检查是否是连接点 if (obj.name.StartsWith("ConnectionPoint_")) { return true; } // 检查是否是连线的一部分(子物体) if (obj.transform.parent != null) { GameObject parent = obj.transform.parent.gameObject; if (parent.name.StartsWith("Wire_") || parent.GetComponent() != null) { return true; } } return false; } /// /// 清除所有高亮接口的颜色 /// void ClearAllHighlights() { // 恢复所有被高亮接口的原始颜色 List interfacesToRestore = new List(highlightedInterfaces.Keys); foreach (GameObject interfaceObj in interfacesToRestore) { if (interfaceObj != null) { RestoreInterfaceColor(interfaceObj); } } highlightedInterfaces.Clear(); // 清除当前吸附目标的高亮 if (snapTargetRenderer != null && isSnapTargetHighlighted) { // 如果当前吸附目标不在字典中,使用保存的原始颜色 if (!highlightedInterfaces.ContainsKey(snapTarget)) { // snapTargetRenderer.material.color = snapTargetOriginalColor; Debug.Log($"恢复吸附目标 {snapTarget?.name} 的原始颜色: {snapTargetOriginalColor}"); } snapTargetRenderer = null; isSnapTargetHighlighted = false; } // 停止点击高亮协程 if (currentHighlightCoroutine != null) { StopCoroutine(currentHighlightCoroutine); currentHighlightCoroutine = null; } // 重置当前高亮接口 currentlyHighlightedInterface = null; snapTarget = null; } /// /// 恢复单个接口的颜色 /// void RestoreInterfaceColor(GameObject interfaceObject) { if (interfaceObject == null) return; Renderer renderer = interfaceObject.GetComponent(); if (renderer != null) { // 如果这个接口在记录中,使用记录的颜色 if (highlightedInterfaces.ContainsKey(interfaceObject)) { Color originalColor = highlightedInterfaces[interfaceObject]; renderer.material.color = originalColor; Debug.Log($"恢复接口 {interfaceObject.name} 的原始颜色: {originalColor}"); // 从字典中移除,避免重复恢复 highlightedInterfaces.Remove(interfaceObject); } //else //{ // // 如果不在字典中,尝试使用默认颜色 // renderer.material.color = new Color(0.85f, 0.71f, 0.4f); // Debug.Log($"设置接口 {interfaceObject.name} 为默认白色"); //} // 恢复原始材质 RestoreOriginalMaterial(interfaceObject); } } /// /// 删除指定的连线或连接点 /// void DeleteObjectIfWireOrConnection(GameObject obj) { if (obj == null) return; // 检查是否是吸附预览 if (obj.name == "SnapPreview") { ClearSnapPreview(); return; } // 检查是否是连线 if (obj.name.StartsWith("Wire_") || obj.GetComponent() != null) { DeleteWire(obj); return; } // 检查是否是连接点 if (obj.name.StartsWith("ConnectionPoint_")) { // 如果是连接点,找到其父连线并删除 if (obj.transform.parent != null) { GameObject parent = obj.transform.parent.gameObject; if (parent.name.StartsWith("Wire_") || parent.GetComponent() != null) { DeleteWire(parent); return; } } // 如果没有父连线,单独删除连接点 DeleteConnectionPoint(obj); return; } // 检查是否是连线的一部分(子物体) if (obj.transform.parent != null) { GameObject parent = obj.transform.parent.gameObject; if (parent.name.StartsWith("Wire_") || parent.GetComponent() != null) { DeleteWire(parent); return; } } // 如果没有匹配的类型,尝试删除最后一个连线 Debug.Log("未找到可删除的对象,尝试删除最后一个连线"); DeleteLastWire(); } /// /// 删除指定的连线 /// void DeleteWire(GameObject wireObject) { if (wireObject == null) { Debug.LogWarning("尝试删除空的连线对象"); return; } Debug.Log($"开始删除连线: {wireObject.name}"); // 获取连线数据 WireData wireData = wireObject.GetComponent(); GameObject startInterface = null; GameObject endInterface = null; if (wireData != null) { startInterface = wireData.snapStartObject; endInterface = wireData.snapEndObject; } else { // 尝试从存储的数据中获取 if (wireConnectionData.ContainsKey(wireObject)) { startInterface = wireConnectionData[wireObject].startInterface; endInterface = wireConnectionData[wireObject].endInterface; } } // 删除连线的所有连接点(子物体) foreach (Transform child in wireObject.transform) { if (child.name.StartsWith("ConnectionPoint_")) { Debug.Log($"删除连接点: {child.name}"); // 从接口堆叠系统中移除 UnregisterConnectionPointFromInterface(child.gameObject); allConnectionPoints.Remove(child.gameObject); Destroy(child.gameObject); } } // 从列表中移除并销毁连线 bool removedFromList = allWires.Remove(wireObject); if (removedFromList) { Debug.Log($"从连线列表中移除: {wireObject.name}"); } else { Debug.LogWarning($"连线 {wireObject.name} 不在连线列表中"); } // 从连线数据存储中移除 if (wireConnectionData.ContainsKey(wireObject)) { wireConnectionData.Remove(wireObject); } Destroy(wireObject); Debug.Log($"销毁连线对象: {wireObject.name}"); // 恢复连线两端接口的颜色和材质 if (startInterface != null) { RestoreInterfaceColor(startInterface); RestoreOriginalMaterial(startInterface); Debug.Log($"恢复起点接口颜色和材质: {startInterface.name}"); } if (endInterface != null) { RestoreInterfaceColor(endInterface); RestoreOriginalMaterial(endInterface); Debug.Log($"恢复终点接口颜色和材质: {endInterface.name}"); } // 清除吸附点预览和高亮 ClearSnapPreview(); ClearAllHighlights(); Debug.Log($"连线删除完成: {wireObject.name}"); } /// /// 删除指定的连接点 /// void DeleteConnectionPoint(GameObject connectionPoint) { if (connectionPoint == null) { Debug.LogWarning("尝试删除空的连接点对象"); return; } Debug.Log($"开始删除连接点: {connectionPoint.name}"); // 获取连接点对应的接口 ConnectionPointInterfaceReference refComponent = connectionPoint.GetComponent(); GameObject targetInterface = refComponent?.targetInterface; // 从接口堆叠系统中移除 UnregisterConnectionPointFromInterface(connectionPoint); // 从列表中移除并销毁连接点 bool removedFromList = allConnectionPoints.Remove(connectionPoint); if (removedFromList) { Debug.Log($"从连接点列表中移除: {connectionPoint.name}"); } else { Debug.LogWarning($"连接点 {connectionPoint.name} 不在连接点列表中"); } Destroy(connectionPoint); Debug.Log($"销毁连接点对象: {connectionPoint.name}"); // 恢复对应接口的颜色和材质 if (targetInterface != null) { RestoreInterfaceColor(targetInterface); RestoreOriginalMaterial(targetInterface); Debug.Log($"恢复接口颜色和材质: {targetInterface.name}"); } // 清除吸附点预览和高亮 ClearSnapPreview(); ClearAllHighlights(); Debug.Log($"连接点删除完成: {connectionPoint.name}"); } /// /// 删除最后一个创建的连线 /// void DeleteLastWire() { if (allWires.Count > 0) { GameObject lastWire = allWires[allWires.Count - 1]; Debug.Log($"删除最后一个连线: {lastWire.name}"); DeleteWire(lastWire); } else { Debug.Log("没有可删除的连线"); } } /// /// 删除最后一个连接点 /// void DeleteLastConnectionPoint() { if (allConnectionPoints.Count > 0) { GameObject lastPoint = allConnectionPoints[allConnectionPoints.Count - 1]; DeleteConnectionPoint(lastPoint); } else { Debug.Log("没有可删除的连接点"); } } void HandleMouseClick() { // 首先清除所有可能的高亮状态 ClearAllHighlights(); Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; // 获取鼠标的实际世界位置 actualMousePosition = GetMouseWorldPosition(); // 重置当前接口 currentStartInterface = null; currentEndInterface = null; // 首先检查是否有吸附目标 if (snapTarget != null) { // 检查吸附目标是否有碰撞体 if (requireColliderForConnection && !HasCollider(snapTarget)) { Debug.LogWarning($"目标物体 {snapTarget.name} 没有碰撞体,无法连线"); return; } // 检查是否尝试自连接 if (currentState == DrawingState.SelectingEnd && IsSelfConnection(snapTarget)) { Debug.LogWarning("不能连接到自身接口!"); ShowSelfConnectionWarning(snapTarget); return; } // 应用点击高亮效果 HandleClickHighlight(snapTarget); // 如果有吸附目标,使用吸附点的位置 Vector3 targetPoint = GetObjectCenter(snapTarget); // 设置当前接口 if (currentState == DrawingState.Idle) { currentStartInterface = snapTarget; startInterfaceObject = snapTarget; // 记录起点接口 } else if (currentState == DrawingState.SelectingEnd) { currentEndInterface = snapTarget; } switch (currentState) { case DrawingState.Idle: StartNewWire(targetPoint); break; case DrawingState.SelectingStart: break; case DrawingState.SelectingEnd: CompleteWire(targetPoint); break; } } else if (Physics.Raycast(ray, out hit, Mathf.Infinity, snapLayers)) { GameObject hitObject = hit.collider.gameObject; // 检查击中的物体是否有碰撞体 if (requireColliderForConnection && !HasCollider(hitObject)) { Debug.LogWarning($"目标物体 {hitObject.name} 没有碰撞体,无法连线"); return; } // 检查是否尝试自连接 if (currentState == DrawingState.SelectingEnd && IsSelfConnection(hitObject)) { Debug.LogWarning("不能连接到自身接口!"); ShowSelfConnectionWarning(hitObject); return; } // 应用点击高亮效果 HandleClickHighlight(hitObject); // 如果有击中物体,使用击中点的位置 Vector3 targetPoint = GetSnapPoint(hit); // 检查是否在吸附距离内,如果是则设置当前接口 Vector3 objectCenter = GetObjectCenter(hitObject); float distance = Vector3.Distance(hit.point, objectCenter); if (distance <= snapDistance) { if (currentState == DrawingState.Idle) { currentStartInterface = hitObject; startInterfaceObject = hitObject; // 记录起点接口 } else if (currentState == DrawingState.SelectingEnd) { currentEndInterface = hitObject; } } switch (currentState) { case DrawingState.Idle: StartNewWire(targetPoint); break; case DrawingState.SelectingStart: break; case DrawingState.SelectingEnd: CompleteWire(targetPoint); break; } } else { // 如果没有击中任何物体,且要求必须有碰撞体,则不允许连线 if (requireColliderForConnection) { Debug.LogWarning("没有找到带有碰撞体的物体,无法连线"); return; } // 如果没有击中任何物体,使用实际鼠标位置 switch (currentState) { case DrawingState.Idle: StartNewWire(actualMousePosition); break; case DrawingState.SelectingStart: break; case DrawingState.SelectingEnd: CompleteWire(actualMousePosition); break; } } } // 检查物体是否有碰撞体 bool HasCollider(GameObject obj) { if (obj == null) return false; // 检查所有类型的碰撞器 Collider collider3D = obj.GetComponent(); Collider2D collider2D = obj.GetComponent(); return (collider3D != null || collider2D != null); } // 修改 StartNewWire 方法,传递目标接口 void StartNewWire(Vector3 point) { startPoint = point; currentState = DrawingState.SelectingEnd; // 记录起点接口 startInterfaceObject = currentStartInterface; // 创建起点连接点模型,传递当前接口 bool created = CreateConnectionPoint(point, true, startInterfaceObject); if (!created) { // 如果创建失败,取消连线操作 currentState = DrawingState.Idle; startInterfaceObject = null; return; } CreateWirePreview(); Debug.Log($"选择起点: {point}, 接口: {startInterfaceObject?.name ?? "无"}"); } // 修改 CompleteWire 方法,传递目标接口 void CompleteWire(Vector3 point) { // 记录终点接口 endInterfaceObject = currentEndInterface; // 最终检查是否尝试自连接 if (endInterfaceObject != null && IsSelfConnection(endInterfaceObject)) { Debug.LogWarning("不能连接到自身接口!"); ShowSelfConnectionWarning(endInterfaceObject); CancelDrawing(); return; } // 检查终点堆叠限制 if (endInterfaceObject != null && enableStacking && IsStackLimitReached(endInterfaceObject)) { Debug.LogWarning($"终点接口 {endInterfaceObject.name} 已达到堆叠限制,无法连接"); ShowStackLimitWarning(endInterfaceObject); CancelDrawing(); return; } endPoint = point; currentState = DrawingState.Idle; // 创建终点连接点模型,传递当前接口 bool created = CreateConnectionPoint(point, false, endInterfaceObject); if (!created) { // 如果创建失败,删除起点连接点并取消连线 RemoveUnfinishedConnectionPoints(); startInterfaceObject = null; endInterfaceObject = null; return; } FinalizeWire(); Debug.Log($"选择终点: {point}, 接口: {endInterfaceObject?.name ?? "无"}"); // 清除所有高亮,确保颜色恢复 ClearAllHighlights(); // 重置当前接口 currentStartInterface = null; currentEndInterface = null; startInterfaceObject = null; endInterfaceObject = null; } void CreateWirePreview() { currentWireObject = new GameObject("WirePreview"); currentWire = currentWireObject.AddComponent(); ConfigureLineRenderer(currentWire); currentWire.positionCount = 2; currentWire.SetPosition(0, startPoint); currentWire.SetPosition(1, startPoint); // 初始时终点与起点相同 } void UpdateWirePreview() { if (currentState == DrawingState.SelectingEnd && currentWire != null) { Vector3 targetPoint; // 如果有吸附目标,使用吸附点 if (snapTarget != null) { targetPoint = GetObjectCenter(snapTarget); } else { // 否则使用实际鼠标位置 targetPoint = GetMouseWorldPosition(); } currentWire.SetPosition(1, targetPoint); } } 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 (requireColliderForConnection && !HasCollider(hitObject)) { return; } // 检查是否尝试自连接 if (currentState == DrawingState.SelectingEnd && IsSelfConnection(hitObject)) { // 自连接目标仍然可以吸附,但会显示警告 ShowSelfConnectionWarning(hitObject); } // 检查堆叠数量限制 if (enableStacking && maxStackCount > 0 && IsStackLimitReached(hitObject)) { // 达到堆叠限制的接口仍然可以吸附,但会显示警告 ShowStackLimitWarning(hitObject); } // 计算鼠标位置与物体中心的距离 Vector3 objectCenter = GetObjectCenter(hitObject); float distance = Vector3.Distance(GetMouseWorldPosition(), objectCenter); if (distance <= snapDistance) { // 设置吸附目标 snapTarget = hitObject; // 高亮显示可吸附的物体 HighlightSnapTarget(snapTarget); } } } Vector3 GetSnapPoint(RaycastHit hit) { if (!enableSnapping) return hit.point; GameObject hitObject = hit.collider.gameObject; Vector3 objectCenter = GetObjectCenter(hitObject); // 检查是否在吸附距离内 float distance = Vector3.Distance(hit.point, objectCenter); if (distance <= snapDistance) { return objectCenter; } return hit.point; } Vector3 GetObjectCenter(GameObject obj) { if (obj == null) return Vector3.zero; if (snapToColliderCenter) { Collider collider = obj.GetComponent(); if (collider != null) { return collider.bounds.center; } } if (snapToTransformCenter) { return obj.transform.position; } // 默认使用碰撞体中心 Collider defaultCollider = obj.GetComponent(); if (defaultCollider != null) { return defaultCollider.bounds.center; } // 如果都没有,使用变换位置 return obj.transform.position; } void HighlightSnapTarget(GameObject target) { if (target == null) return; // 如果已经高亮了其他接口,先恢复它的颜色 if (currentlyHighlightedInterface != null && currentlyHighlightedInterface != target) { RestoreInterfaceColor(currentlyHighlightedInterface); } snapTargetRenderer = target.GetComponent(); if (snapTargetRenderer != null) { // 确保使用材质实例,避免影响其他对象 EnsureMaterialInstance(target); // 记录原始颜色到字典中 if (!highlightedInterfaces.ContainsKey(target)) { highlightedInterfaces[target] = snapTargetRenderer.material.color; Debug.Log($"记录接口 {target.name} 的原始颜色: {snapTargetRenderer.material.color}"); } // 设置高亮颜色 // snapTargetRenderer.material.color = snapHighlightColor; Debug.Log($"设置接口 {target.name} 为吸附高亮颜色: {snapHighlightColor}"); // 更新当前高亮接口 currentlyHighlightedInterface = target; isSnapTargetHighlighted = true; } } /// /// 确保使用材质实例,避免影响其他使用相同材质的对象 /// void EnsureMaterialInstance(GameObject target) { if (target == null) return; Renderer renderer = target.GetComponent(); if (renderer != null) { // 如果还没有记录原始材质,记录下来 if (!originalMaterials.ContainsKey(target)) { originalMaterials[target] = renderer.sharedMaterial; } // 确保使用材质实例 if (renderer.sharedMaterial == renderer.material) { // 如果共享材质和实例材质相同,说明还没有创建实例 renderer.material = new Material(renderer.sharedMaterial); Debug.Log($"为接口 {target.name} 创建材质实例"); } } } /// /// 恢复接口的原始材质 /// void RestoreOriginalMaterial(GameObject target) { if (target == null) return; if (originalMaterials.ContainsKey(target)) { Renderer renderer = target.GetComponent(); if (renderer != null) { // 恢复原始材质 renderer.sharedMaterial = originalMaterials[target]; Debug.Log($"恢复接口 {target.name} 的原始材质"); } // 从字典中移除 originalMaterials.Remove(target); } } void ClearSnapHighlight() { if (snapTargetRenderer != null && isSnapTargetHighlighted) { // snapTargetRenderer.material.color = snapTargetOriginalColor; snapTargetRenderer = null; isSnapTargetHighlighted = false; } snapTarget = null; } // 修改 FinalizeWire 方法,添加记录功能 void FinalizeWire() { if (currentWire != null) { currentWireObject.name = $"Wire_{System.DateTime.Now:yyyyMMddHHmmss}"; // 将连接点附加到连线对象 AttachConnectionPointsToWire(currentWireObject); // 添加到连线列表 allWires.Add(currentWireObject); // 可选:添加物理碰撞器 if (addColliderToWires) { AddWireCollider(currentWire); } // 保存连线数据 WireData data = currentWireObject.AddComponent(); data.startPoint = startPoint; data.endPoint = endPoint; data.creationTime = System.DateTime.Now; // 存储接口引用 if (startInterfaceObject != null) { data.snapStartObject = startInterfaceObject; } if (endInterfaceObject != null) { data.snapEndObject = endInterfaceObject; } // 存储连线连接数据 WireConnectionData connectionData = new WireConnectionData { startInterface = startInterfaceObject, endInterface = endInterfaceObject }; wireConnectionData[currentWireObject] = connectionData; Debug.Log($"连线完成: {currentWireObject.name}, 起点: {startInterfaceObject?.name ?? "无"}, 终点: {endInterfaceObject?.name ?? "无"}"); } currentWire = null; currentWireObject = null; } // 修改 CancelDrawing 方法,确保正确清理 void CancelDrawing() { if (currentWireObject != null) { Destroy(currentWireObject); } // 删除未完成的连接点 RemoveUnfinishedConnectionPoints(); // 清除吸附点预览 ClearSnapPreview(); // 清除所有高亮和材质实例 ClearAllHighlights(); // 恢复所有接口的原始材质 List interfacesToRestore = new List(originalMaterials.Keys); foreach (GameObject interfaceObj in interfacesToRestore) { if (interfaceObj != null) { RestoreOriginalMaterial(interfaceObj); } } originalMaterials.Clear(); currentState = DrawingState.Idle; currentWire = null; currentWireObject = null; // 重置当前接口 currentStartInterface = null; currentEndInterface = null; startInterfaceObject = null; endInterfaceObject = null; Debug.Log("取消连线操作"); } /// /// 更新吸附点预览模型 /// void UpdateSnapPreview() { if (snapPreviewPrefab == null) return; // 如果有吸附目标,显示预览 if (snapTarget != null) { if (currentSnapPreview == null) { currentSnapPreview = Instantiate(snapPreviewPrefab); currentSnapPreview.name = "SnapPreview"; currentSnapPreview.transform.localScale = Vector3.one * connectionPointScale * 0.8f; // 设置为半透明 Renderer renderer = currentSnapPreview.GetComponent(); if (renderer != null) { Color color = renderer.material.color; color.a = 0.5f; renderer.material.color = color; } } Vector3 snapPosition = GetObjectCenter(snapTarget); currentSnapPreview.transform.position = snapPosition; // 根据状态改变预览颜色 Renderer previewRenderer = currentSnapPreview.GetComponent(); if (previewRenderer != null) { // 如果尝试自连接,使用自连接警告颜色 if (currentState == DrawingState.SelectingEnd && IsSelfConnection(snapTarget)) { previewRenderer.material.color = selfConnectionWarningColor; } // 如果达到堆叠限制,改变预览颜色 else if (enableStacking && maxStackCount > 0 && IsStackLimitReached(snapTarget)) { previewRenderer.material.color = maxStackWarningColor; } else { // 恢复半透明颜色 Color color = previewRenderer.material.color; color.a = 0.5f; previewRenderer.material.color = color; } } } else if (currentSnapPreview != null) { // 没有吸附目标时隐藏预览 ClearSnapPreview(); } } /// /// 清除吸附点预览 /// void ClearSnapPreview() { if (currentSnapPreview != null) { Destroy(currentSnapPreview); currentSnapPreview = null; } } void ConfigureLineRenderer(LineRenderer lr) { 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 AddWireCollider(LineRenderer lr) { if (lr.positionCount < 2) { Debug.LogWarning("连线点数不足,无法添加碰撞器"); return; } // 创建胶囊碰撞器而不是网格碰撞器,避免Convex Mesh错误 CreateCapsuleCollidersAlongLine(lr); } // 修复:使用胶囊碰撞器替代网格碰撞器 void CreateCapsuleCollidersAlongLine(LineRenderer lr) { // 获取连线上的所有点 Vector3[] positions = new Vector3[lr.positionCount]; lr.GetPositions(positions); // 为连线的每个线段创建胶囊碰撞器 for (int i = 0; i < positions.Length - 1; i++) { Vector3 start = positions[i]; Vector3 end = positions[i + 1]; // 计算线段长度和方向 float distance = Vector3.Distance(start, end); if (distance < 0.01f) continue; // 跳过太短的线段 Vector3 direction = (end - start).normalized; // 创建胶囊碰撞器 CapsuleCollider capsule = currentWireObject.AddComponent(); // 设置胶囊碰撞器的方向和位置 capsule.direction = 2; // Z轴方向 capsule.height = distance; capsule.radius = wireWidth / 2; // 计算胶囊的位置和旋转 capsule.center = start + direction * distance / 2; // 设置胶囊的方向 if (direction != Vector3.zero) { capsule.transform.rotation = Quaternion.LookRotation(direction); } capsule.isTrigger = true; } } // 修复:改进的鼠标位置获取方法 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)) { // 检查击中的物体是否有碰撞体 if (requireColliderForConnection && !HasCollider(hit.collider.gameObject)) { // 如果没有碰撞体,继续寻找其他点 return FindAlternativePosition(ray); } return GetSnapPoint(hit); } // 如果没有击中任何物体,使用默认平面 return FindAlternativePosition(ray); } // 寻找替代位置(当没有碰撞体时) Vector3 FindAlternativePosition(Ray ray) { // 尝试在默认平面上寻找位置 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 SetSnapTarget(GameObject target) { if (enableSnapping && target != null) { // 检查目标是否有碰撞体 if (requireColliderForConnection && !HasCollider(target)) { Debug.LogWarning($"目标物体 {target.name} 没有碰撞体,无法设置为吸附目标"); return; } snapTarget = target; HighlightSnapTarget(target); } } // 公共方法:清除吸附目标 public void ClearSnapTarget() { ClearSnapHighlight(); } // 公共方法:检查物体是否可以连线 public bool CanConnectToObject(GameObject obj) { if (obj == null) return false; // 如果不需要碰撞体,任何物体都可以连线 if (!requireColliderForConnection) return true; // 检查物体是否有碰撞体 return HasCollider(obj); } /// /// 检查接口是否可以接受新连接点(考虑堆叠限制和自连接) /// public bool CanInterfaceAcceptConnection(GameObject targetInterface) { if (targetInterface == null) return true; // 检查自连接 if (IsSelfConnection(targetInterface)) { return false; } // 检查堆叠限制 if (!enableStacking || maxStackCount <= 0) return true; return !IsStackLimitReached(targetInterface); } /// /// 获取接口上所有连接点的位置信息 /// public List GetConnectionPointsOnInterface(GameObject targetInterface) { List positions = new List(); if (interfaceStacks.ContainsKey(targetInterface)) { foreach (GameObject connectionPoint in interfaceStacks[targetInterface]) { if (connectionPoint != null) { positions.Add(connectionPoint.transform.position); } } } return positions; } /// /// 获取接口上的连接点数量 /// public int GetConnectionPointCountOnInterface(GameObject targetInterface) { return GetStackCountForInterface(targetInterface); } /// /// 获取接口的剩余可用连接点数量 /// public int GetRemainingConnectionSlots(GameObject targetInterface) { if (!enableStacking || maxStackCount <= 0) return int.MaxValue; int currentCount = GetStackCountForInterface(targetInterface); return Mathf.Max(0, maxStackCount - currentCount); } /// /// 设置特定接口的最大堆叠数量(覆盖全局设置) /// public void SetInterfaceMaxStackCount(GameObject targetInterface, int maxCount) { // 这里可以扩展为每个接口有不同的最大堆叠数量 // 目前使用全局设置,但保留接口用于未来扩展 Debug.Log($"接口 {targetInterface?.name} 最大堆叠数量请求设置为 {maxCount},当前使用全局设置: {maxStackCount}"); } /// /// 清除特定接口上的所有连接点 /// public void ClearConnectionPointsOnInterface(GameObject targetInterface) { if (interfaceStacks.ContainsKey(targetInterface)) { // 创建副本,避免在遍历时修改集合 List pointsToRemove = new List(interfaceStacks[targetInterface]); foreach (GameObject connectionPoint in pointsToRemove) { if (connectionPoint != null) { DeleteConnectionPoint(connectionPoint); } } interfaceStacks.Remove(targetInterface); } } /// /// 设置高亮功能开启/关闭 /// public void SetHighlightEnabled(bool enabled) { enableHighlight = enabled; if (!enabled) { // 如果禁用高亮,立即停止所有高亮效果 if (currentHighlightCoroutine != null) { StopCoroutine(currentHighlightCoroutine); currentHighlightCoroutine = null; } // 恢复所有高亮物体的颜色 ClearAllHighlights(); } } /// /// 切换高亮功能状态 /// public void ToggleHighlight() { SetHighlightEnabled(!enableHighlight); Debug.Log($"点击高亮效果: {(enableHighlight ? "开启" : "关闭")}"); } /// /// 设置高亮颜色 /// public void SetHighlightColor(Color color) { clickHighlightColor = color; } /// /// 设置高亮持续时间 /// public void SetHighlightDuration(float duration) { highlightDuration = Mathf.Max(0.1f, duration); } }