using System; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; namespace Ichni.NodeScript { public class NodeObject : MonoBehaviour, IDragHandler, IPointerClickHandler { public TMP_Text TitleText; public NodeBase nodeBase; public Image statusImage; public Transform inputsRoot; public Transform outputsRoot; public Transform middleRoot; public GameObject connectorSlotPrefabI; public GameObject connectorSlotPrefabO; public GameObject dropdownPrefab; public GameObject inputPrefab; bool _selected; public bool Selected { get => _selected; set { _selected = value; if (_bg == null) _bg = GetComponent(); if (_bg != null) _bg.color = value ? new Color(0.3f, 0.5f, 0.9f, 0.4f) : new Color(0.15f, 0.15f, 0.15f, 0.3f); } } Image _bg; public void Init() { if (nodeBase == null) return; TitleText.text = nodeBase.NodeName; nodeBase.nodeObject = this; _bg = GetComponent(); if (_bg == null) { _bg = gameObject.AddComponent(); _bg.color = new Color(0.15f, 0.15f, 0.15f, 0.3f); } nodeBase.InitConnectors(); BuildConnectors(); nodeBase.BuildUI(new NodeUIBuilder(middleRoot, dropdownPrefab, inputPrefab)); NodeManager.Instance?.ComputeLValues(); UpdateLDisplay(); } public void UpdateLDisplay() { if (TitleText != null && nodeBase != null) TitleText.text = $"{nodeBase.NodeName} (L:{nodeBase.L})"; } public void UpdateStatusDisplay() { if (statusImage == null || nodeBase == null) return; statusImage.color = nodeBase.Status switch { NodeStatus.Ready => new Color(0.3f, 0.3f, 0.3f, 0.8f), NodeStatus.Hang => new Color(0.8f, 0.6f, 0.1f, 0.9f), NodeStatus.Complete => new Color(0.2f, 0.8f, 0.3f, 0.9f), _ => Color.white, }; } // ========== 选中 / 拖拽 ========== public void OnPointerClick(PointerEventData e) { if (e.button != PointerEventData.InputButton.Left) return; NodeManager.Instance.SelectNode(this, e); } public void OnDrag(PointerEventData eventData) { if (eventData.button != PointerEventData.InputButton.Left) return; var rt = GetComponent(); rt.anchoredPosition += eventData.delta / GetComponentInParent().scaleFactor; NodeManager.Instance.RefreshAllLines(); } // ========== 插槽构建 ========== void BuildConnectors() { foreach (var (name, connector, type) in nodeBase.GetInputs()) CreateSlot(name, connector, true); foreach (var (name, connector, type) in nodeBase.GetOutputs()) CreateSlot(name, connector, false); } void CreateSlot(string name, object connector, bool isInput) { IInput inp = connector as IInput; IOutput outp = connector as IOutput; if (inp == null && outp == null) return; var conName = inp?.Name ?? outp.Name; if (inp != null) inp.Name = name; if (outp != null) outp.Name = name; var prefab = isInput ? connectorSlotPrefabI : connectorSlotPrefabO; var parent = isInput ? inputsRoot : outputsRoot; if (prefab == null || parent == null) return; var go = Instantiate(prefab, parent); var img = go.GetComponentInChildren(); if (img == null) { var imgGo = new GameObject("Dot", typeof(Image)); imgGo.transform.SetParent(go.transform, false); img = imgGo.GetComponent(); } img.color = (inp as IInput)?.ConnectorColor ?? (outp as IOutput)?.ConnectorColor ?? Color.white; img.raycastTarget = true; var text = go.GetComponentInChildren(); if (text != null) text.text = name; var imgRt = img.GetComponent(); if (imgRt.sizeDelta.sqrMagnitude < 1f) imgRt.sizeDelta = new Vector2(20, 20); var slot = img.GetComponent(); if (slot == null) slot = img.gameObject.AddComponent(); slot.isInput = isInput; slot.connectorIn = inp; slot.connectorOut = outp; slot.ownerNode = this; } } }