Files
XericLibrary-WebGL-Publish/Runtime/Blueprint/GraphTheoryBlueprintComponent.cs
T
lrss3 7f3a12cae0 # update 0.6.2.1
更新了蓝图组件相关的用法。
2026-07-15 10:36:16 +08:00

350 lines
9.3 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
#if ENABLE_INPUT_SYSTEM
using UnityEngine.InputSystem;
#endif
#if UNITY_EDITOR
using UnityEditor;
#endif
using XericLibrary.Runtime.Blueprint.Canvas;
using XericLibrary.Runtime.Blueprint.Element;
namespace XericLibrary.Runtime.Blueprint
{
/// <summary>
/// 图论蓝图组件 —— 在一个 UGUI Canvas 上挂载蓝图系统。
/// <para>驱动方式:
/// - 编辑器非运行态:EditorApplication.update → EditorTick
/// - 运行时:MonoBehaviour.Update</para>
/// </summary>
[DisallowMultipleComponent]
[ExecuteAlways]
public class GraphTheoryBlueprintComponent : MonoBehaviour
{
[Header("Canvas Settings")]
[Tooltip("勾选则为世界空间画布,否则为屏幕空间画布")]
[SerializeField] private bool _useWorldSpaceCanvas = false;
[Tooltip("世界空间画布的相机引用")]
[SerializeField] private Camera _worldCamera;
[Header("Theme")]
[Tooltip("蓝图主题名称。留空使用默认(所有 QuickGraph 工具可见)。")]
[SerializeField] private string _themeName = string.Empty;
[Header("Input")]
[Tooltip("新输入系统 InputActionAsset。留空则仅支持鼠标基础操作(中键拖动平移、滚轮缩放)。")]
[SerializeField] private Object _inputActions;
public Object InputActions
{
get { return _inputActions; }
set
{
if (_inputActions == value) return;
#if ENABLE_INPUT_SYSTEM
if (Graph != null && _inputActions is InputActionAsset oldAsset)
{
var oldTool = BlueprintToolProvider.GetToolByType<BlueprintInputTool_InputSystem>(Graph);
if (oldTool != null && oldTool.InputActionAsset == oldAsset)
oldTool.InputActionAsset = null;
}
#endif
_inputActions = value;
#if ENABLE_INPUT_SYSTEM
if (Graph != null && value is InputActionAsset newAsset)
{
var newTool = BlueprintToolProvider.GetToolByType<BlueprintInputTool_InputSystem>(Graph);
if (newTool != null)
newTool.InputActionAsset = newAsset;
}
#endif
}
}
[Header("Tool Config Overrides")]
[Tooltip("拖拽配置资产到此,渲染时自动按工具类型+主题匹配。")]
[SerializeField] private List<BlueprintToolConfigBase> _toolConfigAssets = new List<BlueprintToolConfigBase>();
[Header("Test")]
[Tooltip("是否自动生成测试节点")]
[SerializeField] public bool _autoGenerateTestNodes = true;
[Header("Debug")]
[Tooltip("显示所有隐藏的运行时对象(HideFlags),用于调试。默认关闭。")]
[SerializeField] private bool _showDebugObjects = false;
/// <summary>当前蓝图实例</summary>
public BlueprintGraph Graph { get; private set; }
/// <summary>当前画布</summary>
public IBlueprintCanvas Canvas { get; private set; }
public string ThemeName
{
get { return _themeName; }
set
{
_themeName = value ?? string.Empty;
if (Graph != null)
{
Graph.ThemeName = _themeName;
Graph.RefreshTheme();
}
}
}
// ===== 初始化 =====
private void Awake()
{
#if UNITY_EDITOR
// 编辑器下提前注册 tick
if (!Application.isPlaying)
EditorApplication.update += EditorTick;
#endif
CreateCanvas();
CreateGraph();
SetupInput();
}
private void CreateCanvas()
{
if (_useWorldSpaceCanvas)
{
Canvas = new WorldSpaceBlueprintCanvas(transform, _worldCamera);
}
else
{
// 1) 找父级 Canvas(不在自身挂 Canvas)
var parentCanvas = GetComponentInParent<UnityEngine.Canvas>();
if (parentCanvas == null)
{
// 场景中无 Canvas → 创建全屏 Canvas,将自身移入
var canvasGo = new GameObject("Blueprint Canvas",
typeof(UnityEngine.Canvas),
typeof(UnityEngine.UI.CanvasScaler),
typeof(UnityEngine.UI.GraphicRaycaster));
var cc = canvasGo.GetComponent<UnityEngine.Canvas>();
cc.renderMode = RenderMode.ScreenSpaceOverlay;
canvasGo.transform.SetParent(transform.parent, false);
canvasGo.transform.SetAsLastSibling();
transform.SetParent(canvasGo.transform, false);
parentCanvas = cc;
}
// 2) 创建渲染根子项(所有蓝图层挂载于此,由外层的 Mask 裁剪)
var renderRootGo = new GameObject("__Bp_RenderRoot",
typeof(RectTransform));
renderRootGo.hideFlags = HideFlags.DontSave;
var renderRootRt = renderRootGo.GetComponent<RectTransform>();
renderRootRt.SetParent(transform, false);
renderRootRt.anchorMin = Vector2.zero;
renderRootRt.anchorMax = Vector2.one;
renderRootRt.offsetMin = Vector2.zero;
renderRootRt.offsetMax = Vector2.zero;
// 3) 以父级 Canvas + 渲染根构建画布适配器
Canvas = new ScreenSpaceBlueprintCanvas(parentCanvas, renderRootRt);
}
}
private void CreateGraph()
{
var system = BlueprintGraphSystem.GlobalInstance;
Graph = system.CreateGraph(Canvas);
Graph.ConfigAssets = _toolConfigAssets;
if (!string.IsNullOrEmpty(_themeName))
{
Graph.ThemeName = _themeName;
Graph.RefreshTheme();
}
// 立即执行一次渲染,确保背景等基础设施在首帧 OnUpdate 前就绪
Graph.LateUpdate();
}
private void SetupInput()
{
#if ENABLE_INPUT_SYSTEM
if (_inputActions is InputActionAsset asset)
{
EnsureEventSystem(asset);
// 将 InputActionAsset 传递给输入工具(由工具自身管理绑定)
var tool = BlueprintToolProvider.GetToolByType<BlueprintInputTool_InputSystem>(Graph);
if (tool != null)
tool.InputActionAsset = asset;
}
#endif
}
#if ENABLE_INPUT_SYSTEM
private void EnsureEventSystem(InputActionAsset actionsAsset)
#else
private void EnsureEventSystem(Object _)
#endif
{
var es = FindObjectOfType<EventSystem>();
EventSystem eventSys;
if (es != null)
{
eventSys = es;
}
else
{
var esGo = new GameObject("EventSystem");
eventSys = esGo.AddComponent<EventSystem>();
}
#if ENABLE_INPUT_SYSTEM
if (actionsAsset != null)
{
var uiModule = eventSys.GetComponent<UnityEngine.InputSystem.UI.InputSystemUIInputModule>();
if (uiModule == null)
{
var standalone = eventSys.GetComponent<StandaloneInputModule>();
if (standalone != null)
DestroyImmediate(standalone);
uiModule = eventSys.gameObject.AddComponent<UnityEngine.InputSystem.UI.InputSystemUIInputModule>();
}
uiModule.actionsAsset = actionsAsset;
}
else
#endif
{
if (eventSys.GetComponent<StandaloneInputModule>() == null)
{
eventSys.gameObject.AddComponent<StandaloneInputModule>();
}
}
}
// ===== 调试可见性 =====
private void SyncDebugVisibility()
{
if (Graph == null) return;
var targetFlags = _showDebugObjects
? HideFlags.None | HideFlags.DontSave
: HideFlags.HideAndDontSave;
var root = Graph.Canvas?.GetRootTransform();
if (root == null) return;
for (int i = 0; i < root.childCount; i++)
{
var child = root.GetChild(i);
if (child.name.StartsWith("__BpLayer_") || child.name == "__Bp_GridBackground")
child.gameObject.hideFlags = targetFlags;
}
}
// ===== 测试节点 =====
private void Start()
{
if (_autoGenerateTestNodes)
GenerateTestNodes();
}
// ===== 驱动:Update(运行时)=====
private void Update()
{
#if UNITY_EDITOR
if (!Application.isPlaying) return;
#endif
if (Graph == null) return;
Graph.Update();
Graph.LateUpdate();
SyncDebugVisibility();
}
#if UNITY_EDITOR
// ===== 驱动:EditorTick(编辑器非运行态)=====
private void EditorTick()
{
if (this == null || Graph == null) return;
Graph.ConfigAssets = _toolConfigAssets;
Graph.InvalidateToolConfigCache();
Graph.Update();
Graph.LateUpdate();
SyncDebugVisibility();
}
private void OnValidate()
{
if (Graph == null || Application.isPlaying) return;
Graph.ConfigAssets = _toolConfigAssets;
Graph.InvalidateToolConfigCache();
}
#endif
// ===== 测试节点生成 =====
public void GenerateTestNodes()
{
if (Graph == null) return;
if (Graph.Nodes.Count > 0)
return;
float startX = 200f;
float centerY = 300f;
float spacing = 200f;
float branchOffset = 200f;
var node1 = new GraphTheoryNode("Node 1", Color.red);
node1.Position = new Vector2(startX, centerY);
var node2 = new GraphTheoryNode("Node 2", Color.green);
node2.Position = new Vector2(startX + spacing, centerY);
var node3 = new GraphTheoryNode("Node 3", Color.blue);
node3.Position = new Vector2(startX + spacing * 2, centerY);
Graph.AddNode(node1);
Graph.AddNode(node2);
Graph.AddNode(node3);
Graph.TryConnectPorts(node1.OutputPorts[0], node2.InputPorts[0]);
Graph.TryConnectPorts(node2.OutputPorts[0], node3.InputPorts[0]);
var node4 = new GraphTheoryNode("Node 4", Color.yellow);
node4.Position = new Vector2(startX + spacing * 2, centerY + branchOffset);
var node5 = new GraphTheoryNode("Node 5", Color.cyan);
node5.Position = new Vector2(startX + spacing * 2, centerY - branchOffset);
Graph.AddNode(node4);
Graph.AddNode(node5);
Graph.TryConnectPorts(node2.OutputPorts[0], node4.InputPorts[0]);
Graph.TryConnectPorts(node2.OutputPorts[0], node5.InputPorts[0]);
}
// ===== 清理 =====
private void OnDestroy()
{
#if UNITY_EDITOR
EditorApplication.update -= EditorTick;
#endif
if (Graph != null)
{
var system = BlueprintGraphSystem.LazyInstance;
system?.DestroyGraph(Graph);
Graph = null;
}
}
}
}