diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..89d9bb0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +Runtime/DrawDebugLibrary/README_DXXL_API.md +Runtime/DrawDebugLibrary/demo scene scripts/ diff --git a/CHANGELOG.md b/CHANGELOG.md index fa0cd43..f4defa8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,9 @@ ## [Unrealse] -* 添加运行时蓝图框架 +## [0.6.2] 2026-07-13 + +* 添加运行时图论框架(v2) ## [0.6.1] 2026-07-09 diff --git a/Editor/Blueprint.meta b/Editor/Blueprint.meta new file mode 100644 index 0000000..5f7b177 --- /dev/null +++ b/Editor/Blueprint.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9b93e95735924284ba5561cdcb1af3dc +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Blueprint/BlueprintMenuItems.cs b/Editor/Blueprint/BlueprintMenuItems.cs new file mode 100644 index 0000000..2a48297 --- /dev/null +++ b/Editor/Blueprint/BlueprintMenuItems.cs @@ -0,0 +1,73 @@ +using UnityEditor; +using UnityEngine; +using UnityEngine.UI; +using XericLibrary.Runtime.Blueprint; + +namespace XericLibraryEditor.Bluprint.Editor +{ + /// + /// 蓝图右键菜单项 —— 通过 GameObject 菜单快捷创建蓝图示例。 + /// + public static class BlueprintMenuItems + { + private const string MenuRoot = "GameObject/Xeric Library/Blueprint/"; + + [MenuItem(MenuRoot + "UI Graph Bp", false, 10)] + private static void CreateUIGraphBlueprint() + { + var go = CreateBlueprintGameObject("Blueprint Graph (UI)"); + + var canvas = go.AddComponent(); + canvas.renderMode = RenderMode.ScreenSpaceOverlay; + + go.AddComponent(); + go.AddComponent(); + + go.AddComponent(); + + Selection.activeGameObject = go; + Undo.RegisterCreatedObjectUndo(go, "Create UI Graph Blueprint"); + } + + [MenuItem(MenuRoot + "UI Graph Bp (QuickGraph)", false, 11)] + private static void CreateUIQuickGraphBlueprint() + { + var go = CreateBlueprintGameObject("Blueprint Graph - QuickGraph (UI)"); + + var canvas = go.AddComponent(); + canvas.renderMode = RenderMode.ScreenSpaceOverlay; + + go.AddComponent(); + go.AddComponent(); + + var comp = go.AddComponent(); + comp.ThemeName = "QuickGraph"; + + Selection.activeGameObject = go; + Undo.RegisterCreatedObjectUndo(go, "Create UI QuickGraph Blueprint"); + } + + [MenuItem(MenuRoot + "World Space Graph Bp", false, 12)] + private static void CreateWorldSpaceGraphBlueprint() + { + var go = CreateBlueprintGameObject("Blueprint Graph (World)"); + go.AddComponent(); + + Selection.activeGameObject = go; + Undo.RegisterCreatedObjectUndo(go, "Create World Space Graph Blueprint"); + } + + private static GameObject CreateBlueprintGameObject(string name) + { + var go = new GameObject(name); + + if (Selection.activeTransform != null) + { + go.transform.SetParent(Selection.activeTransform, false); + } + + go.transform.SetAsLastSibling(); + return go; + } + } +} diff --git a/Editor/Blueprint/BlueprintMenuItems.cs.meta b/Editor/Blueprint/BlueprintMenuItems.cs.meta new file mode 100644 index 0000000..d6d2cf9 --- /dev/null +++ b/Editor/Blueprint/BlueprintMenuItems.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ef23c61b0362b8b4e99796655c109d78 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/InputAction.meta b/Editor/InputAction.meta new file mode 100644 index 0000000..8739b17 --- /dev/null +++ b/Editor/InputAction.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9a618e02e848483d82f62c25637f3719 +timeCreated: 1783824464 \ No newline at end of file diff --git a/Editor/InputAction/BlueprintInputActionTemplate.cs b/Editor/InputAction/BlueprintInputActionTemplate.cs new file mode 100644 index 0000000..aa68c24 --- /dev/null +++ b/Editor/InputAction/BlueprintInputActionTemplate.cs @@ -0,0 +1,236 @@ +#if UNITY_EDITOR && ENABLE_INPUT_SYSTEM +using System.IO; +using UnityEditor; +using UnityEngine; + +using UnityEngine.InputSystem; +using UnityEngine.InputSystem.Controls; + +namespace XericLibrary.Runtime.Blueprint.Editor +{ + /// + /// 蓝图 InputAction 模板生成器。 + /// 右键 Project 窗口 → Xeric Library / Input Action Template / BlueprintAction + /// 即可创建预设 InputActionAsset,内含蓝图所有的 Actions 和绑定。 + /// + public static class BlueprintInputActionTemplate + { + private const string MenuPath = "Assets/Create/Xeric Library/Input Action Template/BlueprintAction"; + private const string DefaultFileName = "BlueprintActions"; + + [MenuItem(MenuPath, priority = 1100)] + public static void CreateBlueprintInputActionAsset() + { + var asset = ScriptableObject.CreateInstance(); + + var map = asset.AddActionMap(BlueprintInputConstants.MapName); + + var binding = InputBinding.MaskByGroup("Keyboard&Mouse"); + var gamepadBinding = InputBinding.MaskByGroup("Gamepad"); + + // ── 指针位置 ── + map.AddAction( + BlueprintInputConstants.Point, + type: InputActionType.Value, + binding: "/position", + interactions: null, + processors: null, + groups: binding.ToString()); + + // ── 滚轮 ── + map.AddAction( + BlueprintInputConstants.ScrollWheel, + type: InputActionType.Value, + binding: "/scroll", + interactions: null, + processors: null, + groups: binding.ToString()); + + // ── 左键 ── + map.AddAction( + BlueprintInputConstants.LeftClick, + type: InputActionType.Button, + binding: "/leftButton", + interactions: null, + processors: null, + groups: binding.ToString()); + + // ── 右键 ── + map.AddAction( + BlueprintInputConstants.RightClick, + type: InputActionType.Button, + binding: "/rightButton", + interactions: null, + processors: null, + groups: binding.ToString()); + + // ── 中键 ── + map.AddAction( + BlueprintInputConstants.MiddleClick, + type: InputActionType.Button, + binding: "/middleButton", + interactions: null, + processors: null, + groups: binding.ToString()); + + // ── Shift ── + map.AddAction( + BlueprintInputConstants.Shift, + type: InputActionType.Button, + binding: "/leftShift", + interactions: null, + processors: null, + groups: binding.ToString()); + + // ── Ctrl ── + map.AddAction( + BlueprintInputConstants.Ctrl, + type: InputActionType.Button, + binding: "/leftCtrl", + interactions: null, + processors: null, + groups: binding.ToString()); + + // ── Alt ── + map.AddAction( + BlueprintInputConstants.Alt, + type: InputActionType.Button, + binding: "/leftAlt", + interactions: null, + processors: null, + groups: binding.ToString()); + + // ── Pan(平移:方向键 + Shift+滚轮 → 水平, Alt+滚轮 → 垂直)── + var panAction = map.AddAction( + BlueprintInputConstants.Pan, + type: InputActionType.Value, + binding: null); // 多个 composite,需手动绑定 + + // 键盘方向键 Vector2 + var kbCompositeIndex = panAction.AddCompositeBinding("2DVector") + .With("Up", "/upArrow") + .With("Down", "/downArrow") + .With("Left", "/leftArrow") + .With("Right", "/rightArrow"); + ApplyGroupToLastBindings(panAction, binding.ToString()); + + // 手柄左摇杆 + panAction.AddCompositeBinding("2DVector") + .With("Up", "/leftStick/up") + .With("Down", "/leftStick/down") + .With("Left", "/leftStick/left") + .With("Right", "/leftStick/right"); + ApplyGroupToLastBindings(panAction, gamepadBinding.ToString()); + + // ── Zoom(缩放:滚轮 + Ctrl热键)── + var zoomAction = map.AddAction( + BlueprintInputConstants.Zoom, + type: InputActionType.Value); + + zoomAction.AddCompositeBinding("1DAxis") + .With("Positive", "/scroll/up") + .With("Negative", "/scroll/down"); + ApplyGroupToLastBindings(zoomAction, binding.ToString()); + + // Ctrl 组合(多一层) + zoomAction.AddCompositeBinding("1DAxis") + .With("Positive", "/ctrl") + .With("Negative", "/ctrl"); + ApplyGroupToLastBindings(zoomAction, binding.ToString()); + + // ── FocusHome(Ctrl + H)── + map.AddAction( + BlueprintInputConstants.FocusHome, + type: InputActionType.Button, + binding: "/h", + interactions: null, + processors: null, + groups: binding.ToString()); + + // ── Undo(Ctrl + Z)── + map.AddAction( + BlueprintInputConstants.Undo, + type: InputActionType.Button, + binding: "/z", + interactions: null, + processors: null, + groups: binding.ToString()); + + // ── Redo(Ctrl + Y)── + map.AddAction( + BlueprintInputConstants.Redo, + type: InputActionType.Button, + binding: "/y", + interactions: null, + processors: null, + groups: binding.ToString()); + + // ── NavigateParent(Tab)── + map.AddAction( + BlueprintInputConstants.NavigateParent, + type: InputActionType.Button, + binding: "/tab", + interactions: null, + processors: null, + groups: binding.ToString()); + + // 保存资产 —— .inputactions 文件需以 JSON 文本形式写入, + // 再由 AssetDatabase.ImportAsset 触发 InputActionImporter 解析。 + string folderPath = GetSelectedFolderPath(); + string assetPath = AssetDatabase.GenerateUniqueAssetPath( + Path.Combine(folderPath, $"{DefaultFileName}.inputactions")); + + string json = asset.ToJson(); + System.IO.File.WriteAllText(assetPath, json); + + AssetDatabase.ImportAsset(assetPath); + AssetDatabase.SaveAssets(); + + var imported = AssetDatabase.LoadAssetAtPath(assetPath); + EditorGUIUtility.PingObject(imported); + Debug.Log($"[Blueprint] InputAction 模板已创建: {assetPath}"); + } + + /// + /// 将 action 最后一个 binding(及所有 composite part)的 groups 设为指定值。 + /// + private static void ApplyGroupToLastBindings(InputAction action, string groups) + { + int count = action.bindings.Count; + int compositeStart = -1; + for (int i = count - 1; i >= 0; i--) + { + if (action.bindings[i].isComposite) + { + compositeStart = i; + break; + } + } + if (compositeStart < 0) return; + + for (int i = compositeStart; i < count; i++) + action.ChangeBinding(i).WithGroup(groups); + } + + /// + /// 获取当前选中的 Project 窗口文件夹路径。 + /// + private static string GetSelectedFolderPath() + { + string path = "Assets"; + var selected = Selection.activeObject; + if (selected != null) + { + string assetPath = AssetDatabase.GetAssetPath(selected); + if (!string.IsNullOrEmpty(assetPath)) + { + if (AssetDatabase.IsValidFolder(assetPath)) + return assetPath; + return Path.GetDirectoryName(assetPath); + } + } + return path; + } + } +} +#endif diff --git a/Editor/InputAction/BlueprintInputActionTemplate.cs.meta b/Editor/InputAction/BlueprintInputActionTemplate.cs.meta new file mode 100644 index 0000000..4a6df51 --- /dev/null +++ b/Editor/InputAction/BlueprintInputActionTemplate.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: be6718b37ded78141820ec5058fba150 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Lrss3.Deconstruction.Editor.asmdef b/Editor/Lrss3.Deconstruction.Editor.asmdef index fe9718f..999fb8f 100644 --- a/Editor/Lrss3.Deconstruction.Editor.asmdef +++ b/Editor/Lrss3.Deconstruction.Editor.asmdef @@ -2,8 +2,8 @@ "name": "Lrss3.SesothoLine.Editor", "rootNamespace": "SesothoLineEditor", "references": [ - "Lrss3.SesothoLine", - "Lrss3.Deconstruction" + "Lrss3.Deconstruction", + "Unity.InputSystem" ], "includePlatforms": [ "Editor" diff --git a/Editor/XericLibraryEditor.dll b/Editor/XericLibraryEditor.dll index a44a617..d90bb8e 100644 Binary files a/Editor/XericLibraryEditor.dll and b/Editor/XericLibraryEditor.dll differ diff --git a/README.md b/README.md index be22b0e..df9dd5b 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,11 @@ Xeric Library 是一个专注代码的 Unity 扩展库。 --- +## 蓝图 (Blueprint) + +蓝图系统,用于创建运行时轻量蓝图渲染框架。 +通过内置的渲染工具实现分层绘制。 + ## 样式表 (XSSS) 自研 Xeric Super Style Sheet (XSSS) 系统,使用 `.xsss` 自定义文本格式,类 CSS/USS 语法,专为 Unity 组件样式设计。基于字典树进行样式路径查找与匹配,支持命名空间隔离、动态监听热重载。提供完整的编辑器导入、Inspector 编辑及 PropertyDrawer 支持。 diff --git a/Resources/Material/Graph/Lit/SceneGround.mat b/Resources/Material/Graph/Lit/SceneGround.mat index 5490a7a..a609695 100644 --- a/Resources/Material/Graph/Lit/SceneGround.mat +++ b/Resources/Material/Graph/Lit/SceneGround.mat @@ -8,7 +8,9 @@ Material: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_Name: SceneGround - m_Shader: {fileID: -6465566751694194690, guid: c508188bc2ab04746ba729aded8434fa, type: 3} + m_Shader: {fileID: -6465566751694194690, guid: 10fb48704e4629d40a97198ec7047bf2, type: 3} + m_Parent: {fileID: 0} + m_ModifiedSerializedProperties: 0 m_ValidKeywords: [] m_InvalidKeywords: [] m_LightmapFlags: 4 @@ -17,6 +19,7 @@ Material: m_CustomRenderQueue: -1 stringTagMap: {} disabledShaderPasses: [] + m_LockedProperties: m_SavedProperties: serializedVersion: 3 m_TexEnvs: diff --git a/Resources/Material/Graph/UI/Blueprint.meta b/Resources/Material/Graph/UI/Blueprint.meta new file mode 100644 index 0000000..4575b5f --- /dev/null +++ b/Resources/Material/Graph/UI/Blueprint.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ea72d510e84667c46831c1387dd88327 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Resources/Material/Graph/UI/Blueprint/BlueprintBackgound-GridLine.shader b/Resources/Material/Graph/UI/Blueprint/BlueprintBackgound-GridLine.shader new file mode 100644 index 0000000..d17d512 --- /dev/null +++ b/Resources/Material/Graph/UI/Blueprint/BlueprintBackgound-GridLine.shader @@ -0,0 +1,183 @@ + +Shader "XericLibrary/BluePrint/BlueprintBackgound_GridLine" +{ + Properties + { + [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {} + _Color ("Tint", Color) = (1,1,1,1) + + _StencilComp ("Stencil Comparison", Float) = 8 + _Stencil ("Stencil ID", Float) = 0 + _StencilOp ("Stencil Operation", Float) = 0 + _StencilWriteMask ("Stencil Write Mask", Float) = 255 + _StencilReadMask ("Stencil Read Mask", Float) = 255 + + _ColorMask ("Color Mask", Float) = 15 + + [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0 + + _Transform( "Transform", Vector ) = ( 10, 10, 0, 0 ) + _GridOverlayPower( "Grid Overlay Power", Float ) = 5 + _GridColor( "Grid Color", Color ) = ( 1, 1, 1, 1 ) + _GridLineThreshold( "Grid Line Threshold ", Range( 0, 1 ) ) = 0.9888518 + _GridBackgroundColor( "Grid Background Color", Color ) = ( 0, 0, 0, 1 ) + _GridExp( "Grid Exp", Range( 0.001, 1 ) ) = 0.001 + _LodTransthd( "Lod Trans thd", Float ) = 30 + + } + + SubShader + { + LOD 0 + + Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" "CanUseSpriteAtlas"="True" } + + Stencil + { + Ref [_Stencil] + ReadMask [_StencilReadMask] + WriteMask [_StencilWriteMask] + Comp [_StencilComp] + Pass [_StencilOp] + } + + + Cull Off + Lighting Off + ZWrite Off + ZTest [unity_GUIZTestMode] + Blend One OneMinusSrcAlpha + ColorMask [_ColorMask] + + + Pass + { + Name "Default" + CGPROGRAM + #define ASE_VERSION 19908 + + #pragma vertex vert + #pragma fragment frag + #pragma target 3.5 + + #include "UnityCG.cginc" + #include "UnityUI.cginc" + + #pragma multi_compile_local _ UNITY_UI_CLIP_RECT + #pragma multi_compile_local _ UNITY_UI_ALPHACLIP + + #include "UnityShaderVariables.cginc" + #define ASE_NEEDS_TEXTURE_COORDINATES0 + #define ASE_NEEDS_FRAG_TEXTURE_COORDINATES0 + + + struct appdata_t + { + float4 vertex : POSITION; + float4 color : COLOR; + float2 texcoord : TEXCOORD0; + UNITY_VERTEX_INPUT_INSTANCE_ID + + }; + + struct v2f + { + float4 vertex : SV_POSITION; + fixed4 color : COLOR; + float2 texcoord : TEXCOORD0; + float4 worldPosition : TEXCOORD1; + float4 mask : TEXCOORD2; + UNITY_VERTEX_OUTPUT_STEREO + + }; + + sampler2D _MainTex; + fixed4 _Color; + fixed4 _TextureSampleAdd; + float4 _ClipRect; + float4 _MainTex_ST; + float _UIMaskSoftnessX; + float _UIMaskSoftnessY; + + uniform float4 _GridBackgroundColor; + uniform float4 _GridColor; + uniform float _GridLineThreshold; + uniform float4 _Transform; + uniform float _LodTransthd; + uniform float _GridOverlayPower; + uniform float _GridExp; + + + v2f vert(appdata_t v ) + { + v2f OUT; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT); + + + + v.vertex.xyz += float3( 0, 0, 0 ) ; + + float4 vPosition = UnityObjectToClipPos(v.vertex); + OUT.worldPosition = v.vertex; + OUT.vertex = vPosition; + + float2 pixelSize = vPosition.w; + pixelSize /= float2(1, 1) * abs(mul((float2x2)UNITY_MATRIX_P, _ScreenParams.xy)); + + float4 clampedRect = clamp(_ClipRect, -2e10, 2e10); + float2 maskUV = (v.vertex.xy - clampedRect.xy) / (clampedRect.zw - clampedRect.xy); + OUT.texcoord = v.texcoord; + OUT.mask = float4(v.vertex.xy * 2 - clampedRect.xy - clampedRect.zw, 0.25 / (0.25 * half2(_UIMaskSoftnessX, _UIMaskSoftnessY) + abs(pixelSize.xy))); + + OUT.color = v.color * _Color; + return OUT; + } + + fixed4 frag(v2f IN ) : SV_Target + { + //Round up the alpha color coming from the interpolator (to 1.0/256.0 steps) + //The incoming alpha could have numerical instability, which makes it very sensible to + //HDR color transparency blend, when it blends with the world's texture. + const half alphaPrecision = half(0xff); + const half invAlphaPrecision = half(1.0/alphaPrecision); + IN.color.a = round(IN.color.a * alphaPrecision)*invAlphaPrecision; + + float4 temp_cast_0 = (_GridLineThreshold).xxxx; + float2 texCoord2 = IN.texcoord.xy * (_Transform).xy + (_Transform).zw; + float2 temp_output_62_0 = fwidth( texCoord2 ); + float4 appendResult73 = (float4(temp_output_62_0 , temp_output_62_0)); + float4 temp_cast_1 = (1.0).xxxx; + float2 temp_cast_2 = (0.5).xx; + float smoothstepResult95 = smoothstep( -0.8 , 1.0 , ( ( max( _Transform.x, _Transform.y ) - _LodTransthd ) / _LodTransthd )); + float2 lerpResult94 = lerp( frac( texCoord2 ) , temp_cast_2 , smoothstepResult95); + float4 appendResult5 = (float4(lerpResult94 , frac( ( texCoord2 / _GridOverlayPower ) ))); + float temp_output_85_0 = ( 0.5 * 1.1 ); + float4 appendResult83 = (float4(0.5 , 0.5 , temp_output_85_0 , temp_output_85_0)); + float4 smoothstepResult33 = smoothstep( ( _ScreenParams.z * ( temp_cast_0 - appendResult73 ) ) , temp_cast_1 , ( abs( ( appendResult5 + -0.5 ) ) / appendResult83 )); + float4 break35 = smoothstepResult33; + float4 lerpResult46 = lerp( _GridBackgroundColor , _GridColor , saturate( pow( ( break35.x + break35.y + break35.z + break35.w ) , _GridExp ) )); + + + half4 color = lerpResult46; + + #ifdef UNITY_UI_CLIP_RECT + half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(IN.mask.xy)) * IN.mask.zw); + color.a *= m.x * m.y; + #endif + + #ifdef UNITY_UI_ALPHACLIP + clip (color.a - 0.001); + #endif + + color.rgb *= color.a; + + return color; + } + ENDCG + } + } + CustomEditor "AmplifyShaderEditor.MaterialInspector" + + Fallback Off +} \ No newline at end of file diff --git a/Resources/Material/Graph/UI/Blueprint/BlueprintBackgound-GridLine.shader.meta b/Resources/Material/Graph/UI/Blueprint/BlueprintBackgound-GridLine.shader.meta new file mode 100644 index 0000000..00786a4 --- /dev/null +++ b/Resources/Material/Graph/UI/Blueprint/BlueprintBackgound-GridLine.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 2571bd49141e8ec429a8c3ec86b5da5b +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Resources/Material/Graph/UI/Blueprint/BlueprintBackgound_GridLine.mat b/Resources/Material/Graph/UI/Blueprint/BlueprintBackgound_GridLine.mat new file mode 100644 index 0000000..78c109e --- /dev/null +++ b/Resources/Material/Graph/UI/Blueprint/BlueprintBackgound_GridLine.mat @@ -0,0 +1,51 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: BlueprintBackgound_GridLine + m_Shader: {fileID: 4800000, guid: 2571bd49141e8ec429a8c3ec86b5da5b, type: 3} + m_Parent: {fileID: 0} + m_ModifiedSerializedProperties: 0 + m_ValidKeywords: [] + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_LockedProperties: + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _ColorMask: 15 + - _Float2: 0.95 + - _Float3: 1 + - _GridExp: 0.001 + - _GridLineThreshold: 0.98 + - _GridOverlayPower: 5 + - _OverlayerMult: 1 + - _Stencil: 0 + - _StencilComp: 8 + - _StencilOp: 0 + - _StencilReadMask: 255 + - _StencilWriteMask: 255 + - _UseUIAlphaClip: 0 + m_Colors: + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _GridBackgroundColor: {r: 0, g: 0, b: 0, a: 1} + - _GridColor: {r: 1, g: 1, b: 1, a: 1} + - _Transform: {r: 19.2, g: 10.8, b: -0, a: -0} + - _Vector0: {r: 0.95, g: 1, b: 0, a: 0} + m_BuildTextureStacks: [] diff --git a/Resources/Material/Graph/UI/Blueprint/BlueprintBackgound_GridLine.mat.meta b/Resources/Material/Graph/UI/Blueprint/BlueprintBackgound_GridLine.mat.meta new file mode 100644 index 0000000..9b702ee --- /dev/null +++ b/Resources/Material/Graph/UI/Blueprint/BlueprintBackgound_GridLine.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c16d74b277ae17c479319990fca9477b +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Resources/Material/Graph/UI/General/UISelectionBoxDraw.mat b/Resources/Material/Graph/UI/General/UISelectionBoxDraw.mat index d0a085a..dbfdcea 100644 --- a/Resources/Material/Graph/UI/General/UISelectionBoxDraw.mat +++ b/Resources/Material/Graph/UI/General/UISelectionBoxDraw.mat @@ -8,7 +8,9 @@ Material: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_Name: UISelectionBoxDraw - m_Shader: {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0} + m_Shader: {fileID: -6465566751694194690, guid: 7da14d69552d5a64791f8abd5e98dc5b, type: 3} + m_Parent: {fileID: 0} + m_ModifiedSerializedProperties: 0 m_ValidKeywords: [] m_InvalidKeywords: - _BUILTIN_SURFACE_TYPE_TRANSPARENT @@ -18,6 +20,7 @@ Material: m_CustomRenderQueue: -1 stringTagMap: {} disabledShaderPasses: [] + m_LockedProperties: m_SavedProperties: serializedVersion: 3 m_TexEnvs: diff --git a/Resources/Material/Graph/UI/General/XUIDotBasic.shader b/Resources/Material/Graph/UI/General/XUIDotBasic.shader index ad78bdd..22687bc 100644 --- a/Resources/Material/Graph/UI/General/XUIDotBasic.shader +++ b/Resources/Material/Graph/UI/General/XUIDotBasic.shader @@ -1,4 +1,3 @@ - Shader "XUIDotBasic" { Properties @@ -128,9 +127,9 @@ Shader "XUIDotBasic" fixed4 frag(v2f IN ) : SV_Target { - //Round up the alpha color coming from the interpolator (to 1.0/256.0 steps) - //The incoming alpha could have numerical instability, which makes it very sensible to - //HDR color transparency blend, when it blends with the world's texture. + + + const half alphaPrecision = half(0xff); const half invAlphaPrecision = half(1.0/alphaPrecision); IN.color.a = round(IN.color.a * alphaPrecision)*invAlphaPrecision; diff --git a/Resources/Material/Graph/UI/General/XUILinearBasic.shader b/Resources/Material/Graph/UI/General/XUILinearBasic.shader index 87e8357..0af1882 100644 --- a/Resources/Material/Graph/UI/General/XUILinearBasic.shader +++ b/Resources/Material/Graph/UI/General/XUILinearBasic.shader @@ -1,4 +1,5 @@ + Shader "XUIBasic" { Properties @@ -143,9 +144,9 @@ Shader "XUIBasic" fixed4 frag(v2f IN ) : SV_Target { - //Round up the alpha color coming from the interpolator (to 1.0/256.0 steps) - //The incoming alpha could have numerical instability, which makes it very sensible to - //HDR color transparency blend, when it blends with the world's texture. + + + const half alphaPrecision = half(0xff); const half invAlphaPrecision = half(1.0/alphaPrecision); IN.color.a = round(IN.color.a * alphaPrecision)*invAlphaPrecision; diff --git a/Resources/Material/Graph/UI/UI Graph/XericUICurve.shader b/Resources/Material/Graph/UI/UI Graph/XericUICurve.shader index a17b84c..518508d 100644 --- a/Resources/Material/Graph/UI/UI Graph/XericUICurve.shader +++ b/Resources/Material/Graph/UI/UI Graph/XericUICurve.shader @@ -1,4 +1,5 @@ + Shader "XericLibrary/UIGraph/XericUICurve" { Properties @@ -129,9 +130,9 @@ Shader "XericLibrary/UIGraph/XericUICurve" fixed4 frag(v2f IN ) : SV_Target { - //Round up the alpha color coming from the interpolator (to 1.0/256.0 steps) - //The incoming alpha could have numerical instability, which makes it very sensible to - //HDR color transparency blend, when it blends with the world's texture. + + + const half alphaPrecision = half(0xff); const half invAlphaPrecision = half(1.0/alphaPrecision); IN.color.a = round(IN.color.a * alphaPrecision)*invAlphaPrecision; diff --git a/Resources/Material/Graph/UI/UI Graph/XericUILineRender.shader b/Resources/Material/Graph/UI/UI Graph/XericUILineRender.shader index 59a12af..ad2afe9 100644 --- a/Resources/Material/Graph/UI/UI Graph/XericUILineRender.shader +++ b/Resources/Material/Graph/UI/UI Graph/XericUILineRender.shader @@ -1,4 +1,5 @@ + Shader "XericLibrary/UIGraph/UILineRender" { Properties @@ -121,9 +122,9 @@ Shader "XericLibrary/UIGraph/UILineRender" fixed4 frag(v2f IN ) : SV_Target { - //Round up the alpha color coming from the interpolator (to 1.0/256.0 steps) - //The incoming alpha could have numerical instability, which makes it very sensible to - //HDR color transparency blend, when it blends with the world's texture. + + + const half alphaPrecision = half(0xff); const half invAlphaPrecision = half(1.0/alphaPrecision); IN.color.a = round(IN.color.a * alphaPrecision)*invAlphaPrecision; diff --git a/Resources/Material/Graph/UI/UI Graph/XericUIPattern.shader b/Resources/Material/Graph/UI/UI Graph/XericUIPattern.shader index d6246f0..a6a6319 100644 --- a/Resources/Material/Graph/UI/UI Graph/XericUIPattern.shader +++ b/Resources/Material/Graph/UI/UI Graph/XericUIPattern.shader @@ -1,4 +1,5 @@ + Shader "XericLibrary/UIGraph/UIPattern" { Properties @@ -121,7 +122,7 @@ Shader "XericLibrary/UIGraph/UIPattern" OUT.ase_texcoord3.xy = v.ase_texcoord2.xy; OUT.ase_tangent = v.ase_tangent; - //setting value to unused interpolator channels and avoid initialization warnings + OUT.ase_texcoord3.zw = 0; v.vertex.xyz += float3( 0, 0, 0 ) ; @@ -144,9 +145,9 @@ Shader "XericLibrary/UIGraph/UIPattern" fixed4 frag(v2f IN ) : SV_Target { - //Round up the alpha color coming from the interpolator (to 1.0/256.0 steps) - //The incoming alpha could have numerical instability, which makes it very sensible to - //HDR color transparency blend, when it blends with the world's texture. + + + const half alphaPrecision = half(0xff); const half invAlphaPrecision = half(1.0/alphaPrecision); IN.color.a = round(IN.color.a * alphaPrecision)*invAlphaPrecision; diff --git a/Resources/ScriptableObject.meta b/Resources/ScriptableObject.meta new file mode 100644 index 0000000..8033d81 --- /dev/null +++ b/Resources/ScriptableObject.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c60a425d5c8e87345b4f0909350d6861 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Resources/ScriptableObject/BlueprintActions.inputactions b/Resources/ScriptableObject/BlueprintActions.inputactions new file mode 100644 index 0000000..851a9a2 --- /dev/null +++ b/Resources/ScriptableObject/BlueprintActions.inputactions @@ -0,0 +1,449 @@ +{ + "version": 1, + "name": "BlueprintActions", + "maps": [ + { + "name": "Blueprint", + "id": "57e48183-533c-4d6d-b131-8ad65e183c27", + "actions": [ + { + "name": "Point", + "type": "Value", + "id": "3fecd77e-e6b0-4c35-923f-4b75c319c835", + "expectedControlType": "", + "processors": "", + "interactions": "", + "initialStateCheck": true + }, + { + "name": "ScrollWheel", + "type": "Value", + "id": "026f04b5-3585-4a14-b362-905a03b9cd6c", + "expectedControlType": "", + "processors": "", + "interactions": "", + "initialStateCheck": true + }, + { + "name": "LeftClick", + "type": "Button", + "id": "a64e638b-6923-45f3-ae92-a4c2c93b9404", + "expectedControlType": "", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "RightClick", + "type": "Button", + "id": "6d27d898-d133-4b87-84f4-f4d3ffb30036", + "expectedControlType": "", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "MiddleClick", + "type": "Button", + "id": "a62871ba-e098-4311-a013-031040604a8c", + "expectedControlType": "", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Shift", + "type": "Button", + "id": "82515a87-cc55-4c1c-bf55-8b22eb994e51", + "expectedControlType": "", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Ctrl", + "type": "Button", + "id": "29b51781-8763-4d20-8e79-6c49c81ed45e", + "expectedControlType": "", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Alt", + "type": "Button", + "id": "5d3332a3-b320-4e6b-b877-5b7cc2f2e15f", + "expectedControlType": "", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Pan", + "type": "Value", + "id": "437af76f-a22f-4e97-9454-c8ad4a04ea7b", + "expectedControlType": "", + "processors": "", + "interactions": "", + "initialStateCheck": true + }, + { + "name": "Zoom", + "type": "Value", + "id": "fb9c164e-b854-49e2-8e65-3f89ac15ba0e", + "expectedControlType": "", + "processors": "", + "interactions": "", + "initialStateCheck": true + }, + { + "name": "FocusHome", + "type": "Button", + "id": "1fb4d951-0fe4-4a0c-8231-aecb2940501f", + "expectedControlType": "", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Undo", + "type": "Button", + "id": "15c7f65a-293d-47d5-a911-3165ac581985", + "expectedControlType": "", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Redo", + "type": "Button", + "id": "2924dab5-49f4-42fc-97a7-78fdcb63e0f7", + "expectedControlType": "", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "NavigateParent", + "type": "Button", + "id": "f285f303-6045-42a4-87b5-ce32f3174551", + "expectedControlType": "", + "processors": "", + "interactions": "", + "initialStateCheck": false + } + ], + "bindings": [ + { + "name": "", + "id": "0c5d3758-e653-4a4b-b5bf-f5cb387c26af", + "path": "/position", + "interactions": "", + "processors": "", + "groups": "[Keyboard&Mouse]", + "action": "Point", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "6b2eed90-22cf-4698-b6c3-fffe60b0be27", + "path": "/scroll", + "interactions": "", + "processors": "", + "groups": "[Keyboard&Mouse]", + "action": "ScrollWheel", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "5de88c78-b8d1-4116-a489-2fcddc16ac00", + "path": "/leftButton", + "interactions": "", + "processors": "", + "groups": "[Keyboard&Mouse]", + "action": "LeftClick", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "90ca46b6-838c-4b33-b823-2b8be1e1cd7c", + "path": "/rightButton", + "interactions": "", + "processors": "", + "groups": "[Keyboard&Mouse]", + "action": "RightClick", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "87e46bb3-fc2c-4d1d-bdfd-92104cbf8585", + "path": "/middleButton", + "interactions": "", + "processors": "", + "groups": "[Keyboard&Mouse]", + "action": "MiddleClick", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "b5c6968e-6302-4312-8e92-b975f1369e91", + "path": "/leftShift", + "interactions": "", + "processors": "", + "groups": "[Keyboard&Mouse]", + "action": "Shift", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "42a36e22-d17a-46f8-8a2c-c5f5fe9976b1", + "path": "/leftCtrl", + "interactions": "", + "processors": "", + "groups": "[Keyboard&Mouse]", + "action": "Ctrl", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "752ad893-e912-4206-93eb-c476910b49c0", + "path": "/leftAlt", + "interactions": "", + "processors": "", + "groups": "[Keyboard&Mouse]", + "action": "Alt", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "2DVector", + "id": "809dd943-9bc5-441b-96c5-88408f588ba5", + "path": "2DVector", + "interactions": "", + "processors": "", + "groups": "[Keyboard&Mouse]", + "action": "Pan", + "isComposite": true, + "isPartOfComposite": false + }, + { + "name": "Up", + "id": "51ceee12-44fc-43aa-b953-3bca4a2c648a", + "path": "/upArrow", + "interactions": "", + "processors": "", + "groups": "[Keyboard&Mouse]", + "action": "Pan", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "Down", + "id": "1f81dec0-886b-4adc-86ce-8b17fd471dee", + "path": "/downArrow", + "interactions": "", + "processors": "", + "groups": "[Keyboard&Mouse]", + "action": "Pan", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "Left", + "id": "9b2865f0-450c-41f3-87dc-f75de30757d9", + "path": "/leftArrow", + "interactions": "", + "processors": "", + "groups": "[Keyboard&Mouse]", + "action": "Pan", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "Right", + "id": "3cbbb040-2bcf-4856-b030-eadffce1d864", + "path": "/rightArrow", + "interactions": "", + "processors": "", + "groups": "[Keyboard&Mouse]", + "action": "Pan", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "2DVector", + "id": "8c6b2980-4b14-4e3d-beba-43a04dd40294", + "path": "2DVector", + "interactions": "", + "processors": "", + "groups": "[Gamepad]", + "action": "Pan", + "isComposite": true, + "isPartOfComposite": false + }, + { + "name": "Up", + "id": "03370fa0-29c6-4569-9619-628eaacfb3c6", + "path": "/leftStick/up", + "interactions": "", + "processors": "", + "groups": "[Gamepad]", + "action": "Pan", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "Down", + "id": "b85caf61-0f41-4255-a80a-c402df5b2a20", + "path": "/leftStick/down", + "interactions": "", + "processors": "", + "groups": "[Gamepad]", + "action": "Pan", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "Left", + "id": "85d70aff-19dd-444f-a477-26f86a047341", + "path": "/leftStick/left", + "interactions": "", + "processors": "", + "groups": "[Gamepad]", + "action": "Pan", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "Right", + "id": "eda1e128-379a-4c87-ad82-951f8a69e294", + "path": "/leftStick/right", + "interactions": "", + "processors": "", + "groups": "[Gamepad]", + "action": "Pan", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "1DAxis", + "id": "dca71d91-5b59-41ed-a4d3-868f19753361", + "path": "1DAxis", + "interactions": "", + "processors": "", + "groups": "[Keyboard&Mouse]", + "action": "Zoom", + "isComposite": true, + "isPartOfComposite": false + }, + { + "name": "Positive", + "id": "56e24cf8-5c62-4da9-a54e-71282d26ea66", + "path": "/scroll/up", + "interactions": "", + "processors": "", + "groups": "[Keyboard&Mouse]", + "action": "Zoom", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "Negative", + "id": "e5d6e5fb-6450-4124-a961-ff22bf6cfcbd", + "path": "/scroll/down", + "interactions": "", + "processors": "", + "groups": "[Keyboard&Mouse]", + "action": "Zoom", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "1DAxis", + "id": "391d3207-a469-4240-9b49-8cb67f385af2", + "path": "1DAxis", + "interactions": "", + "processors": "", + "groups": "[Keyboard&Mouse]", + "action": "Zoom", + "isComposite": true, + "isPartOfComposite": false + }, + { + "name": "Positive", + "id": "add5f511-0cdc-427f-a2dc-743fe7bce6f4", + "path": "/ctrl", + "interactions": "", + "processors": "", + "groups": "[Keyboard&Mouse]", + "action": "Zoom", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "Negative", + "id": "1378e62c-5fc0-4e8b-8680-4a61c5088529", + "path": "/ctrl", + "interactions": "", + "processors": "", + "groups": "[Keyboard&Mouse]", + "action": "Zoom", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "", + "id": "9a024cd3-ef65-4421-91a3-1963c7e3c422", + "path": "/h", + "interactions": "", + "processors": "", + "groups": "[Keyboard&Mouse]", + "action": "FocusHome", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "3b26b918-d6c9-4995-b180-c6c47ef8cf38", + "path": "/z", + "interactions": "", + "processors": "", + "groups": "[Keyboard&Mouse]", + "action": "Undo", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "9da8a358-d6f6-48ee-bbb8-e064ee1806cd", + "path": "/y", + "interactions": "", + "processors": "", + "groups": "[Keyboard&Mouse]", + "action": "Redo", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "1c3cf3be-12c2-440d-8294-5dc04ec61abb", + "path": "/tab", + "interactions": "", + "processors": "", + "groups": "[Keyboard&Mouse]", + "action": "NavigateParent", + "isComposite": false, + "isPartOfComposite": false + } + ] + } + ], + "controlSchemes": [] +} \ No newline at end of file diff --git a/Resources/ScriptableObject/BlueprintActions.inputactions.meta b/Resources/ScriptableObject/BlueprintActions.inputactions.meta new file mode 100644 index 0000000..f2ba6af --- /dev/null +++ b/Resources/ScriptableObject/BlueprintActions.inputactions.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 58e605f9253ce864ab140170b0d7c467 +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 11500000, guid: 8404be70184654265930450def6a9037, type: 3} + generateWrapperCode: 0 + wrapperCodePath: + wrapperClassName: + wrapperCodeNamespace: diff --git a/Resources/ScriptableObject/QuickGraphGridBackgroundConfig.asset b/Resources/ScriptableObject/QuickGraphGridBackgroundConfig.asset new file mode 100644 index 0000000..619b2cb --- /dev/null +++ b/Resources/ScriptableObject/QuickGraphGridBackgroundConfig.asset @@ -0,0 +1,22 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 32753aff001677a4b81e10dfae39b4cb, type: 3} + m_Name: QuickGraphGridBackgroundConfig + m_EditorClassIdentifier: + ShaderName: XericLibrary/BluePrint/BlueprintBackgound_GridLine + OverrideMaterial: {fileID: 0} + GridSize: 100 + GridOverlayPower: 5 + GridLineThreshold: 0.95 + GridExp: 1 + GridColor: {r: 0.6156863, g: 0.6156863, b: 0.6156863, a: 1} + GridBackgroundColor: {r: 0.1764706, g: 0.1764706, b: 0.1764706, a: 1} diff --git a/Resources/ScriptableObject/QuickGraphGridBackgroundConfig.asset.meta b/Resources/ScriptableObject/QuickGraphGridBackgroundConfig.asset.meta new file mode 100644 index 0000000..df31b8b --- /dev/null +++ b/Resources/ScriptableObject/QuickGraphGridBackgroundConfig.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 092ad6102a8bc354bb081e3ee26e5ed6 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Resources/ScriptableObject/QuickGraphInputConfig.asset b/Resources/ScriptableObject/QuickGraphInputConfig.asset new file mode 100644 index 0000000..db21638 --- /dev/null +++ b/Resources/ScriptableObject/QuickGraphInputConfig.asset @@ -0,0 +1,22 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: cfce52570fac4f5488becd85ce4f023b, type: 3} + m_Name: QuickGraphInputConfig + m_EditorClassIdentifier: + PanLerpSpeed: 8 + ZoomLerpSpeed: 8 + ZoomDivisor: 1100 + MinZoom: 0.1 + MaxZoom: 3 + KeyboardPanSpeed: 600 + KeyboardZoomSpeed: 0.05 + DragThreshold: 5 diff --git a/Resources/ScriptableObject/QuickGraphInputConfig.asset.meta b/Resources/ScriptableObject/QuickGraphInputConfig.asset.meta new file mode 100644 index 0000000..3b5acc1 --- /dev/null +++ b/Resources/ScriptableObject/QuickGraphInputConfig.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3f441c61e0e252146a114c471a77c507 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Resources/ScriptableObject/QuickGraphNodeRenderConfig.asset b/Resources/ScriptableObject/QuickGraphNodeRenderConfig.asset new file mode 100644 index 0000000..3f48ea4 --- /dev/null +++ b/Resources/ScriptableObject/QuickGraphNodeRenderConfig.asset @@ -0,0 +1,25 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8ab3d3829c73815468ed8c5ae306a6ee, type: 3} + m_Name: QuickGraphNodeRenderConfig + m_EditorClassIdentifier: + NodeWidth: 120 + NodeHeight: 80 + BorderThickness: 5 + ChamferSize: 0.5 + ChamferSegments: 6 + DefaultBackgroundColor: {r: 0.16037738, g: 0.16037738, b: 0.16037738, a: 1} + DefaultTextColor: {r: 0.6156863, g: 0.6156863, b: 0.6156863, a: 1} + TitleFontSize: 20 + TitleFont: {fileID: 0} + Lod0Threshold: 0.1 + Lod1Threshold: 0.15 diff --git a/Resources/ScriptableObject/QuickGraphNodeRenderConfig.asset.meta b/Resources/ScriptableObject/QuickGraphNodeRenderConfig.asset.meta new file mode 100644 index 0000000..fb300b2 --- /dev/null +++ b/Resources/ScriptableObject/QuickGraphNodeRenderConfig.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cdebd6b7f25eb334093a428b6414b0ce +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Resources/ScriptableObject/QuickGraphWireRenderConfig.asset b/Resources/ScriptableObject/QuickGraphWireRenderConfig.asset new file mode 100644 index 0000000..a2ae408 --- /dev/null +++ b/Resources/ScriptableObject/QuickGraphWireRenderConfig.asset @@ -0,0 +1,24 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 21208cffd6108e04d815bb385f0af3fc, type: 3} + m_Name: QuickGraphWireRenderConfig + m_EditorClassIdentifier: + WireWidth: 5 + SourceHandleLength: 120 + TargetHandleLength: 120 + ArrowShape: 0 + ArrowReversed: 0 + ArrowProgress: 1 + ArrowWidth: 20 + ArrowHeight: 30 + ArrowDepthCompensation: -1 + TessellationSegments: 24 diff --git a/Resources/ScriptableObject/QuickGraphWireRenderConfig.asset.meta b/Resources/ScriptableObject/QuickGraphWireRenderConfig.asset.meta new file mode 100644 index 0000000..3267251 --- /dev/null +++ b/Resources/ScriptableObject/QuickGraphWireRenderConfig.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: aad533644ae5c8b4bb61786c413d4844 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Blueprint.meta b/Runtime/Blueprint.meta new file mode 100644 index 0000000..723ca65 --- /dev/null +++ b/Runtime/Blueprint.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1566851acf6847a88b28779b8778be7d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Blueprint/Canvas.meta b/Runtime/Blueprint/Canvas.meta new file mode 100644 index 0000000..32ff5db --- /dev/null +++ b/Runtime/Blueprint/Canvas.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a62ba5ace01c4084b9c4e8cb553cf3b0 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Blueprint/Canvas/ScreenSpaceBlueprintCanvas.cs b/Runtime/Blueprint/Canvas/ScreenSpaceBlueprintCanvas.cs new file mode 100644 index 0000000..854c504 --- /dev/null +++ b/Runtime/Blueprint/Canvas/ScreenSpaceBlueprintCanvas.cs @@ -0,0 +1,114 @@ +using UnityEngine; + +namespace XericLibrary.Runtime.Blueprint.Canvas +{ + /// + /// 屏幕空间蓝图画布实现,基于 UGUI Canvas。 + /// + /// 使用 RectTransformUtility 处理屏幕坐标和画布坐标之间的转换。 + /// 缩放范围:0.1x ~ 3x。 + /// + /// + public class ScreenSpaceBlueprintCanvas : BlueprintCanvasBase + { + private readonly UnityEngine.Canvas _unityCanvas; + private readonly RectTransform _rectTransform; + + /// + /// 构造屏幕空间蓝图画布 + /// + /// Unity UGUI Canvas 组件 + public ScreenSpaceBlueprintCanvas(UnityEngine.Canvas unityCanvas) + { + _unityCanvas = unityCanvas; + _rectTransform = unityCanvas.GetComponent(); + } + + /// + /// 将屏幕坐标转换为画布坐标 + /// + /// 屏幕空间的坐标点 + /// 画布空间的坐标点 + public override Vector2 ScreenToCanvas(Vector2 screenPoint) + { + RectTransformUtility.ScreenPointToLocalPointInRectangle( + _rectTransform, screenPoint, _unityCanvas.worldCamera, out Vector2 localPoint); + return (localPoint - _panOffset) / _zoomLevel; + } + + /// + /// 将画布坐标转换为屏幕坐标 + /// + /// 画布空间的坐标点 + /// 屏幕空间的坐标点 + public override Vector2 CanvasToScreen(Vector2 canvasPoint) + { + Vector2 local = canvasPoint * _zoomLevel + _panOffset; + Vector3 world = _rectTransform.TransformPoint(local); + if (_unityCanvas.worldCamera != null) + { + return _unityCanvas.worldCamera.WorldToScreenPoint(world); + } + + return world; + } + + /// + /// 将画布坐标转换为世界坐标 + /// + /// 屏幕空间画布的世界空间转换依赖于 Canvas 的渲染模式: + /// Screen Space - Overlay 时直接使用 localPoint 作为屏幕坐标; + /// Screen Space - Camera 时通过 camera 进行坐标转换。 + /// + /// + /// 画布空间的坐标点 + /// 世界空间的坐标点 + public override Vector3 CanvasToWorld(Vector2 canvasPoint) + { + Vector2 local = canvasPoint * _zoomLevel + _panOffset; + Vector3 world = _rectTransform.TransformPoint(local); + if (_unityCanvas.worldCamera != null) + { + Vector3 screenPoint = _unityCanvas.worldCamera.WorldToScreenPoint(world); + return _unityCanvas.worldCamera.ScreenToWorldPoint(screenPoint); + } + + return world; + } + + /// + /// 将世界坐标转换为画布坐标 + /// + /// 世界空间的坐标点 + /// 画布空间的坐标点 + public override Vector2 WorldToCanvas(Vector3 worldPoint) + { + if (_unityCanvas.worldCamera != null) + { + Vector3 screenPoint = _unityCanvas.worldCamera.WorldToScreenPoint(worldPoint); + RectTransformUtility.ScreenPointToLocalPointInRectangle( + _rectTransform, screenPoint, _unityCanvas.worldCamera, out Vector2 localPoint); + return (localPoint - _panOffset) / _zoomLevel; + } + + Vector3 local = _rectTransform.InverseTransformPoint(worldPoint); + return (new Vector2(local.x, local.y) - _panOffset) / _zoomLevel; + } + + /// + /// 获取渲染根 Transform(UGUI Canvas 的 RectTransform) + /// + public override Transform GetRootTransform() + { + return _rectTransform; + } + + /// + /// 底层 Unity Canvas 引用 + /// + public UnityEngine.Canvas UnityCanvas + { + get { return _unityCanvas; } + } + } +} diff --git a/Runtime/Blueprint/Canvas/ScreenSpaceBlueprintCanvas.cs.meta b/Runtime/Blueprint/Canvas/ScreenSpaceBlueprintCanvas.cs.meta new file mode 100644 index 0000000..f411904 --- /dev/null +++ b/Runtime/Blueprint/Canvas/ScreenSpaceBlueprintCanvas.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2df8866bfc0672749a3ea7ebb87aa708 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Blueprint/Canvas/WorldSpaceBlueprintCanvas.cs b/Runtime/Blueprint/Canvas/WorldSpaceBlueprintCanvas.cs new file mode 100644 index 0000000..b571bea --- /dev/null +++ b/Runtime/Blueprint/Canvas/WorldSpaceBlueprintCanvas.cs @@ -0,0 +1,104 @@ +using UnityEngine; + +namespace XericLibrary.Runtime.Blueprint.Canvas +{ + /// + /// 世界空间蓝图画布实现,用于 Sprite 渲染等世界空间场景。 + /// + /// 通过相机射线与画布平面的交点实现屏幕坐标到画布坐标的转换。 + /// 缩放范围:0.05x ~ 5x。 + /// + /// + public class WorldSpaceBlueprintCanvas : BlueprintCanvasBase + { + private readonly Transform _canvasTransform; + + /// + /// 构造世界空间蓝图画布 + /// + /// 画布的 Transform 组件 + /// 使用的摄像机,如果为 null 则使用 Camera.main + public WorldSpaceBlueprintCanvas(Transform canvasTransform, Camera camera = null) + { + _canvasTransform = canvasTransform; + SetCamera(camera != null ? camera : Camera.main); + MinZoom = 0.05f; + MaxZoom = 5f; + } + + /// + /// 将屏幕坐标转换为画布坐标 + /// + /// 通过相机的屏幕射线与画布平面的交点计算画布坐标。 + /// 画布平面由 Transform 的前向方向和位置定义。 + /// + /// + /// 屏幕空间的坐标点 + /// 画布空间的坐标点 + public override Vector2 ScreenToCanvas(Vector2 screenPoint) + { + if (_camera == null) + { + return Vector2.zero; + } + + Ray ray = _camera.ScreenPointToRay(screenPoint); + Plane plane = new Plane(-_canvasTransform.forward, _canvasTransform.position); + if (plane.Raycast(ray, out float dist)) + { + Vector3 worldPoint = ray.GetPoint(dist); + Vector3 local = _canvasTransform.InverseTransformPoint(worldPoint); + return (new Vector2(local.x, local.y) - _panOffset) / _zoomLevel; + } + + return Vector2.zero; + } + + /// + /// 将画布坐标转换为屏幕坐标 + /// + /// 画布空间的坐标点 + /// 屏幕空间的坐标点 + public override Vector2 CanvasToScreen(Vector2 canvasPoint) + { + if (_camera == null) + { + return Vector2.zero; + } + + Vector2 local = canvasPoint * _zoomLevel + _panOffset; + Vector3 world = _canvasTransform.TransformPoint(new Vector3(local.x, local.y, 0f)); + return _camera.WorldToScreenPoint(world); + } + + /// + /// 将画布坐标转换为世界坐标 + /// + /// 画布空间的坐标点 + /// 世界空间的坐标点 + public override Vector3 CanvasToWorld(Vector2 canvasPoint) + { + Vector2 local = canvasPoint * _zoomLevel + _panOffset; + return _canvasTransform.TransformPoint(new Vector3(local.x, local.y, 0f)); + } + + /// + /// 将世界坐标转换为画布坐标 + /// + /// 世界空间的坐标点 + /// 画布空间的坐标点 + public override Vector2 WorldToCanvas(Vector3 worldPoint) + { + Vector3 local = _canvasTransform.InverseTransformPoint(worldPoint); + return (new Vector2(local.x, local.y) - _panOffset) / _zoomLevel; + } + + /// + /// 获取渲染根 Transform + /// + public override Transform GetRootTransform() + { + return _canvasTransform; + } + } +} diff --git a/Runtime/Blueprint/Canvas/WorldSpaceBlueprintCanvas.cs.meta b/Runtime/Blueprint/Canvas/WorldSpaceBlueprintCanvas.cs.meta new file mode 100644 index 0000000..1b97d31 --- /dev/null +++ b/Runtime/Blueprint/Canvas/WorldSpaceBlueprintCanvas.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 386905a638369a64c9cc64ec70d51620 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Blueprint/Element.meta b/Runtime/Blueprint/Element.meta new file mode 100644 index 0000000..7c304de --- /dev/null +++ b/Runtime/Blueprint/Element.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 38246c6c75b0cf94f983bff80ccd471e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Blueprint/Element/GraphTheoryNode.cs b/Runtime/Blueprint/Element/GraphTheoryNode.cs new file mode 100644 index 0000000..423c95b --- /dev/null +++ b/Runtime/Blueprint/Element/GraphTheoryNode.cs @@ -0,0 +1,70 @@ +using UnityEngine; +using XericLibrary.Runtime.Blueprint; + +namespace XericLibrary.Runtime.Blueprint.Element +{ + /// + /// 图论测试节点 —— 用于图论蓝图测试的简单节点。 + /// 包含一个输入端口和一个输出端口,节点颜色由外部指定。 + /// + public class GraphTheoryNode : BlueprintNode + { + private readonly string _title; + private readonly string _category; + + public override string NodeTitle + { + get { return _title; } + } + + public override string Category + { + get { return _category; } + } + + /// + /// 创建一个图论测试节点。 + /// + /// 节点标题 + /// 节点边框颜色 + public GraphTheoryNode(string title, Color color) + { + _title = title; + _category = "Test"; + NodeColor = color; + NodeBGColor = new Color(0.15f, 0.15f, 0.15f, 1f); + NodeTextColor = Color.gray; + + AddInputPort("In", "any"); + AddOutputPort("Out", "any"); + } + + /// + /// 创建一个带指定颜色的图论测试节点。 + /// + /// 节点标题 + /// 节点边框颜色 + /// 节点背景颜色 + public GraphTheoryNode(string title, Color color, Color bgColor) + { + _title = title; + _category = "Test"; + NodeColor = color; + NodeBGColor = bgColor; + NodeTextColor = Color.gray; + + AddInputPort("In", "any"); + AddOutputPort("Out", "any"); + } + + public override void Execute(BlueprintExecutionContext context) + { + // 测试节点,无执行逻辑 + } + + public override System.Collections.Generic.IReadOnlyList GetRequiredToolTypes() + { + return System.Array.Empty(); + } + } +} diff --git a/Runtime/Blueprint/Element/GraphTheoryNode.cs.meta b/Runtime/Blueprint/Element/GraphTheoryNode.cs.meta new file mode 100644 index 0000000..d362fef --- /dev/null +++ b/Runtime/Blueprint/Element/GraphTheoryNode.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1866118873770184ba4c48f8c8d2ead6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Blueprint/GraphTheoryBlueprintComponent.cs b/Runtime/Blueprint/GraphTheoryBlueprintComponent.cs new file mode 100644 index 0000000..118ba30 --- /dev/null +++ b/Runtime/Blueprint/GraphTheoryBlueprintComponent.cs @@ -0,0 +1,334 @@ +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 +{ + /// + /// 图论蓝图组件 —— 在一个 UGUI Canvas 上挂载蓝图系统。 + /// 使用 ExecuteAlways 在编辑器中也会运行,方便预览。 + /// 驱动方式: + /// - 编辑器非运行态:EditorApplication.update → EditorTick + /// - 运行时:MonoBehaviour.Update + /// + [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 (_inputActions is InputActionAsset oldAsset) + BlueprintInputManager.UnregisterAsset(oldAsset); +#endif + _inputActions = value; +#if ENABLE_INPUT_SYSTEM + if (value is InputActionAsset newAsset) + BlueprintInputManager.RegisterAsset(newAsset); +#endif + } + } + + [Header("Tool Config Overrides")] + [Tooltip("拖拽配置资产到此,渲染时自动按工具类型+主题匹配。")] + [SerializeField] private List _toolConfigAssets = new List(); + + [Header("Test")] + [Tooltip("是否自动生成测试节点")] + [SerializeField] private bool _autoGenerateTestNodes = true; + + [Header("Debug")] + [Tooltip("显示所有隐藏的运行时对象(HideFlags),用于调试。默认关闭。")] + [SerializeField] private bool _showDebugObjects = false; + + /// 当前蓝图实例 + public BlueprintGraph Graph { get; private set; } + + /// 当前画布 + 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 + { + var unityCanvas = GetComponent(); + if (unityCanvas == null) + { + unityCanvas = gameObject.AddComponent(); + unityCanvas.renderMode = RenderMode.ScreenSpaceOverlay; + } + if (GetComponent() == null) + gameObject.AddComponent(); + + Canvas = new ScreenSpaceBlueprintCanvas(unityCanvas); + } + } + + 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 + EnsureEventSystem(_inputActions as InputActionAsset); + + if (_inputActions is InputActionAsset asset) + { + BlueprintInputManager.RegisterAsset(asset); + } +#else + EnsureEventSystem(null); +#endif + BlueprintInputManager.SetFocusedGraph(Graph); + } + +#if ENABLE_INPUT_SYSTEM + private void EnsureEventSystem(InputActionAsset actionsAsset) +#else + private void EnsureEventSystem(Object _) +#endif + { + var es = FindObjectOfType(); + EventSystem eventSys; + if (es != null) + { + eventSys = es; + } + else + { + var esGo = new GameObject("EventSystem"); + eventSys = esGo.AddComponent(); + } + +#if ENABLE_INPUT_SYSTEM + if (actionsAsset != null) + { + var uiModule = eventSys.GetComponent(); + if (uiModule == null) + { + var standalone = eventSys.GetComponent(); + if (standalone != null) + DestroyImmediate(standalone); + + uiModule = eventSys.gameObject.AddComponent(); + } + uiModule.actionsAsset = actionsAsset; + } + else +#endif + { + if (eventSys.currentInputModule == null) + { + eventSys.gameObject.AddComponent(); + } + } + } + + // ===== 调试可见性 ===== + + 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); + node1.NodeSize = new Vector2(90, 130); + + var node2 = new GraphTheoryNode("Node 2", Color.green); + node2.Position = new Vector2(startX + spacing, centerY); + node2.NodeSize = new Vector2(90, 130); + + var node3 = new GraphTheoryNode("Node 3", Color.blue); + node3.Position = new Vector2(startX + spacing * 2, centerY); + node3.NodeSize = new Vector2(90, 130); + + 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, centerY + branchOffset); + node4.NodeSize = new Vector2(90, 130); + + var node5 = new GraphTheoryNode("Node 5", Color.cyan); + node5.Position = new Vector2(startX + spacing, centerY - branchOffset); + node5.NodeSize = new Vector2(90, 130); + + 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 + BlueprintInputManager.SetFocusedGraph(null); + +#if ENABLE_INPUT_SYSTEM + if (_inputActions is InputActionAsset asset) + BlueprintInputManager.UnregisterAsset(asset); +#endif + + if (Graph != null) + { + var system = BlueprintGraphSystem.LazyInstance; + system?.DestroyGraph(Graph); + Graph = null; + } + } + } +} diff --git a/Runtime/Blueprint/GraphTheoryBlueprintComponent.cs.meta b/Runtime/Blueprint/GraphTheoryBlueprintComponent.cs.meta new file mode 100644 index 0000000..43094ab --- /dev/null +++ b/Runtime/Blueprint/GraphTheoryBlueprintComponent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b0187d970265b514c98d142a8b6c34e9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Blueprint/Input.meta b/Runtime/Blueprint/Input.meta new file mode 100644 index 0000000..ba9134a --- /dev/null +++ b/Runtime/Blueprint/Input.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8d9272dc004d78b409ea3890a6be3ba0 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Blueprint/Input/BlueprintBoxSelectTool.cs b/Runtime/Blueprint/Input/BlueprintBoxSelectTool.cs new file mode 100644 index 0000000..ae8d5cf --- /dev/null +++ b/Runtime/Blueprint/Input/BlueprintBoxSelectTool.cs @@ -0,0 +1,165 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; + +namespace XericLibrary.Runtime.Blueprint +{ + /// + /// 蓝图框选工具 —— 鼠标左键在空白区域拖拽,绘制矩形选框, + /// 释放时计算框选范围内所有元素的交集并输出统计日志。 + /// 选框 UGUI Image 默认渲染在所有层之上(SetAsLastSibling)。 + /// 框选结果保存在 中供其他工具读取。 + /// + [BlueprintTool(phase: ToolPhase.PreUpdate, order: 60)] + [BlueprintTheme("QuickGraph")] + public class BlueprintBoxSelectTool : BlueprintTool + { + // ---------- 状态 ---------- + private bool _isBoxSelecting; + private Vector2 _dragStartCanvas; // 按下时的画布坐标 + private Vector2 _dragEndCanvas; // 当前的画布坐标 + + // 选框 UI + private GameObject _selectionBoxGo; + private Image _selectionBoxImage; + private RectTransform _selectionBoxRt; + + // ---------- 结果 ---------- + + /// 本次框选选中的元素列表。 + public List SelectedElements { get; } = new List(); + + // ===== 鼠标按下 ===== + + public override void OnPointerDown(Vector2 canvasPoint, int mouseButton) + { + if (Graph == null) return; + if (mouseButton != 0) return; // 仅左键 + + // 仅在空白区域开始框选(HitTest 无结果) + var hit = Graph.HitTest(canvasPoint); + if (hit != null) return; + + _isBoxSelecting = true; + _dragStartCanvas = canvasPoint; + _dragEndCanvas = canvasPoint; + + // 创建选框 UI + CreateSelectionBox(); + } + + // ===== 拖拽 ===== + + public override void OnPointerDrag(Vector2 canvasPoint, Vector2 delta, int mouseButton) + { + if (!_isBoxSelecting || mouseButton != 0) return; + + _dragEndCanvas = canvasPoint; + UpdateSelectionBox(); + } + + // ===== 释放 ===== + + public override void OnPointerUp(Vector2 canvasPoint, int mouseButton) + { + if (!_isBoxSelecting || mouseButton != 0) return; + + _dragEndCanvas = canvasPoint; + FinishSelection(); + } + + // ===== 销毁 ===== + + public override void OnDestroy() + { + DestroySelectionBox(); + } + + // ===== 选框 UI ===== + + private void CreateSelectionBox() + { + if (Graph?.Canvas == null) return; + var root = Graph.Canvas.GetRootTransform(); + if (root == null) return; + + _selectionBoxGo = new GameObject("__Bp_BoxSelect", typeof(RectTransform), typeof(Image)); + _selectionBoxGo.hideFlags = HideFlags.HideAndDontSave; + + _selectionBoxRt = _selectionBoxGo.GetComponent(); + _selectionBoxRt.SetParent(root, false); + _selectionBoxRt.SetAsLastSibling(); // 渲染在最顶层 + + _selectionBoxImage = _selectionBoxGo.GetComponent(); + _selectionBoxImage.color = new Color(0.2f, 0.5f, 1.0f, 0.15f); // 半透明蓝 + // 边框通过 Outline 或额外 Image 实现,直接使用透明填充 + 轮廓不好做, + // 使用两个 Image:填充(当前) + 边框(另一个,1px 白色) + // 为简化,在填充 Image 上挂一个 Outline 组件模拟边框 + var outline = _selectionBoxGo.AddComponent(); + outline.effectColor = Color.white; + outline.effectDistance = new Vector2(1, -1); + } + + private void UpdateSelectionBox() + { + if (_selectionBoxRt == null || Graph?.Canvas == null) return; + + // 将画布坐标两角转为屏幕坐标 + var startScreen = Graph.Canvas.CanvasToScreen(_dragStartCanvas); + var endScreen = Graph.Canvas.CanvasToScreen(_dragEndCanvas); + + // 计算 RectTransform 位置和尺寸(屏幕空间) + var minX = Mathf.Min(startScreen.x, endScreen.x); + var minY = Mathf.Min(startScreen.y, endScreen.y); + var maxX = Mathf.Max(startScreen.x, endScreen.x); + var maxY = Mathf.Max(startScreen.y, endScreen.y); + + _selectionBoxRt.anchoredPosition = new Vector2(minX, minY); + _selectionBoxRt.sizeDelta = new Vector2(maxX - minX, maxY - minY); + } + + private void FinishSelection() + { + _isBoxSelecting = false; + UpdateSelectionBox(); + + // 计算选框内的元素(画布坐标空间) + SelectedElements.Clear(); + var min = new Vector2( + Mathf.Min(_dragStartCanvas.x, _dragEndCanvas.x), + Mathf.Min(_dragStartCanvas.y, _dragEndCanvas.y)); + var max = new Vector2( + Mathf.Max(_dragStartCanvas.x, _dragEndCanvas.x), + Mathf.Max(_dragStartCanvas.y, _dragEndCanvas.y)); + var selectRect = new Rect(min, max - min); + + foreach (var element in Graph.Elements) + { + if (element == null) continue; + if (element.BoundingBox.Overlaps(selectRect)) + { + SelectedElements.Add(element); + } + } + + // 销毁选框 + DestroySelectionBox(); + } + + private void DestroySelectionBox() + { + if (_selectionBoxGo != null) + { +#if UNITY_EDITOR + if (!Application.isPlaying) + Object.DestroyImmediate(_selectionBoxGo); + else +#endif + Object.Destroy(_selectionBoxGo); + _selectionBoxGo = null; + _selectionBoxRt = null; + _selectionBoxImage = null; + } + } + } +} diff --git a/Runtime/Blueprint/Input/BlueprintBoxSelectTool.cs.meta b/Runtime/Blueprint/Input/BlueprintBoxSelectTool.cs.meta new file mode 100644 index 0000000..8eee88f --- /dev/null +++ b/Runtime/Blueprint/Input/BlueprintBoxSelectTool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e9d5cdc3f1b8c9840b274a6db73a9fb1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Blueprint/Input/BlueprintInputConstants.cs b/Runtime/Blueprint/Input/BlueprintInputConstants.cs new file mode 100644 index 0000000..ec71d07 --- /dev/null +++ b/Runtime/Blueprint/Input/BlueprintInputConstants.cs @@ -0,0 +1,47 @@ +namespace XericLibrary.Runtime.Blueprint +{ + /// + /// 蓝图输入系统的 Action Map / Action 名称常量。 + /// 供 Editor 的 InputAction 模板和 Runtime 的输入处理工具共同引用, + /// 避免字符串硬编码散布各处。 + /// + public static class BlueprintInputConstants + { + // ── Action Map ── + public const string MapName = "Blueprint"; + + // ── 鼠标 / 指针 Actions(名称须与 UnityEngine.InputSystem.UI.InputSystemUIInputModule 官方预设一致)── + /// 画布内指针位置(Vector2)。InputSystemUIInputModule 预设名称。 + public const string Point = "Point"; + /// 滚轮滚动(Vector2)。InputSystemUIInputModule 预设名称。 + public const string ScrollWheel = "ScrollWheel"; + /// 左键点击(Button)。InputSystemUIInputModule 预设名称。 + public const string LeftClick = "LeftClick"; + /// 右键点击(Button)。InputSystemUIInputModule 预设名称。 + public const string RightClick = "RightClick"; + /// 中键点击(Button)。InputSystemUIInputModule 预设名称。 + public const string MiddleClick = "MiddleClick"; + + // ── 功能键 Actions ── + /// 左 Shift(Button) + public const string Shift = "Shift"; + /// 左 Ctrl(Button) + public const string Ctrl = "Ctrl"; + /// 左 Alt(Button) + public const string Alt = "Alt"; + + // ── 快捷键组合 Actions(仅新输入系统)── + /// 平移画布(Vector2,4方向) + public const string Pan = "Pan"; + /// 缩放画布(float,滚轮 + Ctrl 组合) + public const string Zoom = "Zoom"; + /// 定位到核心节点 + public const string FocusHome = "FocusHome"; + /// 撤销 + public const string Undo = "Undo"; + /// 重做 + public const string Redo = "Redo"; + /// 进入父级 + public const string NavigateParent = "NavigateParent"; + } +} diff --git a/Runtime/Blueprint/Input/BlueprintInputConstants.cs.meta b/Runtime/Blueprint/Input/BlueprintInputConstants.cs.meta new file mode 100644 index 0000000..39a08ba --- /dev/null +++ b/Runtime/Blueprint/Input/BlueprintInputConstants.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3e718c439f4fa3d449427433ed729467 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Blueprint/Input/BlueprintInputManager.InputSystem.cs b/Runtime/Blueprint/Input/BlueprintInputManager.InputSystem.cs new file mode 100644 index 0000000..9c410c4 --- /dev/null +++ b/Runtime/Blueprint/Input/BlueprintInputManager.InputSystem.cs @@ -0,0 +1,188 @@ +#if ENABLE_INPUT_SYSTEM + +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.InputSystem; + +namespace XericLibrary.Runtime.Blueprint +{ + /// + /// 蓝图输入管理器(新输入系统)—— 管理 InputActionAsset 的引用计数与启用/停用。 + /// + /// 多个蓝图可能引用同一个 InputActionAsset,管理器确保: + /// - 首个蓝图注册时启用 asset 并绑定 Action 回调; + /// - 后续蓝图注册仅增加计数,不重复启用; + /// - 蓝图注销时减少计数,计数归零时才解绑并禁用 asset。 + /// + /// + /// 蓝图组件在 Awake 时调用 ,OnDestroy 时调用 + /// ;运行时动态更换 asset 也通过这两方法。 + /// + /// + public static class BlueprintInputManager + { + /// asset → 引用计数 + private static Dictionary _refCounts + = new Dictionary(); + + /// asset → 已绑定回调的 ActionMap + private static Dictionary _activeMaps + = new Dictionary(); + + // ===== 快捷键最新值(供 QuickGraphInputTool.OnUpdate 轮询) ===== + + /// Pan 动作最新的 Vector2 方向值(在 performed 中写入,canceled 中归零)。 + public static Vector2 LastPanDirection; + + /// Zoom 动作最新的 float 轴值(在 performed 中写入,canceled 中归零)。 + public static float LastZoomAxis; + + // 缓存的回调引用(确保 Bind/Unbind 使用同一委托实例) + private static readonly System.Action s_panHandler + = ctx => LastPanDirection = ctx.ReadValue(); + private static readonly System.Action s_panCanceled + = _ => LastPanDirection = Vector2.zero; + private static readonly System.Action s_zoomHandler + = ctx => LastZoomAxis = ctx.ReadValue(); + private static readonly System.Action s_zoomCanceled + = _ => LastZoomAxis = 0f; + private static readonly System.Action s_homeHandler + = _ => OnActionReceived(BlueprintInputConstants.FocusHome); + private static readonly System.Action s_undoHandler + = _ => OnActionReceived(BlueprintInputConstants.Undo); + private static readonly System.Action s_redoHandler + = _ => OnActionReceived(BlueprintInputConstants.Redo); + private static readonly System.Action s_parentHandler + = _ => OnActionReceived(BlueprintInputConstants.NavigateParent); + + // ===== 当前焦点蓝图(快捷键分发目标) ===== + + private static BlueprintGraph s_focusedGraph; + + /// 设置当前焦点蓝图(快捷键分发目标)。 + public static void SetFocusedGraph(BlueprintGraph graph) + { + s_focusedGraph = graph; + } + + // ===== 注册 / 注销 ===== + + /// + /// 注册一个蓝图对 InputActionAsset 的引用。 + /// 若 asset 尚未启用,则启用并绑定快捷键回调。 + /// 允许多个蓝图共享同一 asset。 + /// + public static void RegisterAsset(InputActionAsset asset) + { + if (asset == null) return; + + if (_refCounts.TryGetValue(asset, out int count)) + { + _refCounts[asset] = count + 1; + return; + } + + _refCounts[asset] = 1; + + var map = asset.FindActionMap(BlueprintInputConstants.MapName); + if (map != null) + { + map.Enable(); + BindActions(map); + _activeMaps[asset] = map; + } + } + + /// + /// 注销一个蓝图对 InputActionAsset 的引用。 + /// 引用计数归零时解绑回调并禁用 asset。 + /// + public static void UnregisterAsset(InputActionAsset asset) + { + if (asset == null) return; + + if (!_refCounts.TryGetValue(asset, out int count)) return; + + count--; + if (count > 0) + { + _refCounts[asset] = count; + return; + } + + _refCounts.Remove(asset); + + if (_activeMaps.TryGetValue(asset, out var map)) + { + UnbindActions(map); + map.Disable(); + _activeMaps.Remove(asset); + } + } + + // ===== Action 回调 ===== + + private static void BindActions(InputActionMap map) + { + Bind(map, BlueprintInputConstants.Pan, s_panHandler); + BindCancelled(map, BlueprintInputConstants.Pan, s_panCanceled); + Bind(map, BlueprintInputConstants.Zoom, s_zoomHandler); + BindCancelled(map, BlueprintInputConstants.Zoom, s_zoomCanceled); + Bind(map, BlueprintInputConstants.FocusHome,s_homeHandler); + Bind(map, BlueprintInputConstants.Undo, s_undoHandler); + Bind(map, BlueprintInputConstants.Redo, s_redoHandler); + Bind(map, BlueprintInputConstants.NavigateParent, s_parentHandler); + } + + private static void UnbindActions(InputActionMap map) + { + Unbind(map, BlueprintInputConstants.Pan, s_panHandler); + UnbindCancelled(map, BlueprintInputConstants.Pan, s_panCanceled); + Unbind(map, BlueprintInputConstants.Zoom, s_zoomHandler); + UnbindCancelled(map, BlueprintInputConstants.Zoom, s_zoomCanceled); + Unbind(map, BlueprintInputConstants.FocusHome,s_homeHandler); + Unbind(map, BlueprintInputConstants.Undo, s_undoHandler); + Unbind(map, BlueprintInputConstants.Redo, s_redoHandler); + Unbind(map, BlueprintInputConstants.NavigateParent, s_parentHandler); + } + + private static void Bind(InputActionMap map, string name, System.Action handler) + { + var action = map.FindAction(name); + if (action != null) + action.performed += handler; + } + + private static void Unbind(InputActionMap map, string name, System.Action handler) + { + var action = map.FindAction(name); + if (action != null) + action.performed -= handler; + } + + private static void BindCancelled(InputActionMap map, string name, System.Action handler) + { + var action = map.FindAction(name); + if (action != null) + action.canceled += handler; + } + + private static void UnbindCancelled(InputActionMap map, string name, System.Action handler) + { + var action = map.FindAction(name); + if (action != null) + action.canceled -= handler; + } + + private static void OnActionReceived(string actionName) + { + if (s_focusedGraph != null) + { + s_focusedGraph.MarkDirty(); + BlueprintToolProvider.DispatchAction(s_focusedGraph, actionName); + } + } + } +} + +#endif diff --git a/Runtime/Blueprint/Input/BlueprintInputManager.InputSystem.cs.meta b/Runtime/Blueprint/Input/BlueprintInputManager.InputSystem.cs.meta new file mode 100644 index 0000000..2de743b --- /dev/null +++ b/Runtime/Blueprint/Input/BlueprintInputManager.InputSystem.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 72f3c53c414fbef4b80270adb10afe1d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Blueprint/Input/BlueprintInputManager.oldInputSys.cs b/Runtime/Blueprint/Input/BlueprintInputManager.oldInputSys.cs new file mode 100644 index 0000000..dd202ae --- /dev/null +++ b/Runtime/Blueprint/Input/BlueprintInputManager.oldInputSys.cs @@ -0,0 +1,135 @@ +#if !ENABLE_INPUT_SYSTEM + +using UnityEngine; + +namespace XericLibrary.Runtime.Blueprint +{ + /// + /// 蓝图输入管理器(旧输入系统)—— 通过旧输入系统轮询鼠标和键盘事件。 + /// + /// 提供与 BlueprintInputManager.InputSystem.cs 相同的静态 API 签名, + /// 但底层使用 / 等旧 API, + /// 不依赖任何新输入系统类型。 + /// + /// + /// 鼠标点击、拖拽、右键等事件通过 轮询后 + /// 分发到 的 DispatchXxx 方法。 + /// 键盘 Pan/Zoom 值通过 轮询后由 + /// 在 OnUpdate 中读取。 + /// + /// + public static class BlueprintInputManager + { + /// + /// 当前焦点蓝图(快捷键分发目标)。 + /// 由 设置。 + /// + private static BlueprintGraph s_focusedGraph; + + /// 设置当前焦点蓝图。 + public static void SetFocusedGraph(BlueprintGraph graph) + { + s_focusedGraph = graph; + } + + /// + /// 注册 InputActionAsset(旧系统空操作)。 + /// 仅保留签名以兼容编译。 + /// + public static void RegisterAsset(Object asset) + { + // 旧输入系统:不管理 InputActionAsset + } + + /// + /// 注销 InputActionAsset(旧系统空操作)。 + /// 仅保留签名以兼容编译。 + /// + public static void UnregisterAsset(Object asset) + { + // 旧输入系统:不管理 InputActionAsset + } + + // ===== 键盘轮询值(供 QuickGraphInputTool.OnUpdate 读取) ===== + + /// + /// 方向键 Pan 方向值(每帧由 更新)。 + /// + public static Vector2 LastPanDirection; + + /// + /// +/- 键 Zoom 轴值(每帧由 更新)。 + /// + public static float LastZoomAxis; + + // ===== 事件轮询 ===== + + /// + /// 轮询鼠标按键事件。 + /// 每帧在 Update 中调用,检测鼠标按下/释放并分发到 。 + /// + /// 0=左键, 1=右键, 2=中键 + public static void PollMouseEvents(int mouseButton) + { + if (s_focusedGraph?.Canvas == null) return; + + s_focusedGraph.BeginFrame(); + + if (Input.GetMouseButtonDown(mouseButton)) + { + var canvasPoint = s_focusedGraph.Canvas.ScreenToCanvas(Input.mousePosition); + BlueprintToolProvider.DispatchPointerDown(s_focusedGraph, canvasPoint, mouseButton); + } + + if (Input.GetMouseButtonUp(mouseButton)) + { + var canvasPoint = s_focusedGraph.Canvas.ScreenToCanvas(Input.mousePosition); + BlueprintToolProvider.DispatchPointerUp(s_focusedGraph, canvasPoint, mouseButton); + } + } + + /// + /// 轮询鼠标滚轮事件。 + /// 每帧在 Update 中调用,检测滚轮增量并分发到 。 + /// + public static void PollScrollWheel() + { + if (s_focusedGraph?.Canvas == null) return; + + s_focusedGraph.BeginFrame(); + + float scrollY = Input.mouseScrollDelta.y; + if (!Mathf.Approximately(scrollY, 0f)) + { + var canvasPoint = s_focusedGraph.Canvas.ScreenToCanvas(Input.mousePosition); + var scrollDelta = new Vector2(0f, scrollY); + BlueprintToolProvider.DispatchScroll(s_focusedGraph, canvasPoint, scrollDelta); + } + } + + /// + /// 轮询键盘快捷键(方向键 Pan、+/- Zoom)。 + /// 每帧在 Update 中调用,更新 。 + /// + public static void PollKeyboard() + { + LastPanDirection = Vector2.zero; + if (Input.GetKey(KeyCode.UpArrow) || Input.GetKey(KeyCode.W)) + LastPanDirection.y += 1f; + if (Input.GetKey(KeyCode.DownArrow) || Input.GetKey(KeyCode.S)) + LastPanDirection.y -= 1f; + if (Input.GetKey(KeyCode.LeftArrow) || Input.GetKey(KeyCode.A)) + LastPanDirection.x -= 1f; + if (Input.GetKey(KeyCode.RightArrow) || Input.GetKey(KeyCode.D)) + LastPanDirection.x += 1f; + + LastZoomAxis = 0f; + if (Input.GetKey(KeyCode.Equals) || Input.GetKey(KeyCode.KeypadPlus)) + LastZoomAxis = 1f; + if (Input.GetKey(KeyCode.Minus) || Input.GetKey(KeyCode.KeypadMinus)) + LastZoomAxis = -1f; + } + } +} + +#endif diff --git a/Runtime/Blueprint/Input/BlueprintInputManager.oldInputSys.cs.meta b/Runtime/Blueprint/Input/BlueprintInputManager.oldInputSys.cs.meta new file mode 100644 index 0000000..98a91fa --- /dev/null +++ b/Runtime/Blueprint/Input/BlueprintInputManager.oldInputSys.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1f94468ab3403984494aa0d980297093 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Blueprint/Input/BlueprintInputReceiver.cs b/Runtime/Blueprint/Input/BlueprintInputReceiver.cs new file mode 100644 index 0000000..c75ea69 --- /dev/null +++ b/Runtime/Blueprint/Input/BlueprintInputReceiver.cs @@ -0,0 +1,110 @@ +using UnityEngine; + +namespace XericLibrary.Runtime.Blueprint +{ + /// + /// 蓝图输入接收工具 —— 继承自 ,由 BlueprintToolProvider 统一管理生命周期。 + /// + /// 职责: + /// 1. 创建并驱动 ,绑定 UGUI EventTrigger 到背景 Image; + /// 2. 在 中统一刷新鼠标位置、检测指针进入/离开背景区域; + /// 3. 将画布内的指针移动事件分发给 。 + /// + /// + /// 鼠标按键按下/释放/拖拽/滚轮等由 通过 EventTrigger 回调直接分发。 + /// + /// + [BlueprintTool(phase: ToolPhase.PreUpdate, order: 0)] + [BlueprintTheme("QuickGraph")] + public class BlueprintInputReceiver : BlueprintTool + { + private BlueprintUGUIInputManager _uguiManager; + private bool _initialized; + + private Vector2 _lastMousePos; + private bool _isInside; + + // ===== 生命周期 ===== + + public override void OnUpdate(float deltaTime) + { + if (Graph == null) return; + + // ── 每帧开始:刷新帧缓存 ── + Graph.BeginFrame(); + + // ── 初始化 UGUI 管理器 ── + if (!_initialized) + { + _uguiManager = new BlueprintUGUIInputManager(Graph); + _initialized = true; + } + + // ── 更新鼠标位置(统一入口) ── + BlueprintUGUIInputManager.UpdateMousePosition(); + BlueprintToolProvider.MousePosition = BlueprintUGUIInputManager.MousePosition; + + // ── 延迟绑定 EventTrigger ── + if (!_uguiManager.IsBound) + { + _uguiManager.TryBind(); + return; + } + + var mp = BlueprintUGUIInputManager.MousePosition; + + // ── 检测指针进入/离开背景 ── + bool overBg = IsScreenPointOverBackground(mp); + if (overBg && !_isInside) + { + _isInside = true; + BlueprintToolProvider.DispatchPointerEnter(Graph); + } + else if (!overBg && _isInside) + { + _isInside = false; + BlueprintToolProvider.DispatchPointerExit(Graph); + } + + if (!_isInside) return; + + // ── 指针移动(轮询,EventTrigger 无通用移动事件) ── + if (Vector2.Distance(_lastMousePos, mp) < 0.001f) return; + + var cur = ScreenToCanvas(mp); + var prev = (_lastMousePos == Vector2.zero) ? cur : ScreenToCanvas(_lastMousePos); + var delta = cur - prev; + _lastMousePos = mp; + + BlueprintToolProvider.DispatchPointerMove(Graph, cur, delta); + } + + public override void OnDestroy() + { + if (_uguiManager != null) + { + _uguiManager.Unbind(); + _uguiManager = null; + } + _initialized = false; + _isInside = false; + _lastMousePos = Vector2.zero; + } + + // ===== 辅助 ===== + + /// 检查屏幕坐标是否落在背景 RectTransform 内。 + private bool IsScreenPointOverBackground(Vector2 screenPoint) + { + var rt = _uguiManager?.BgRectTransform; + if (rt == null) return false; + return RectTransformUtility.RectangleContainsScreenPoint(rt, screenPoint, null); + } + + /// 屏幕坐标 → 画布坐标。 + private Vector2 ScreenToCanvas(Vector2 screenPos) + { + return Graph?.Canvas != null ? Graph.Canvas.ScreenToCanvas(screenPos) : screenPos; + } + } +} diff --git a/Runtime/Blueprint/Input/BlueprintInputReceiver.cs.meta b/Runtime/Blueprint/Input/BlueprintInputReceiver.cs.meta new file mode 100644 index 0000000..46563a5 --- /dev/null +++ b/Runtime/Blueprint/Input/BlueprintInputReceiver.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7e1d0771b4cc2aa4692eff637fa5d5af +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Blueprint/Input/BlueprintUGUIInputManager.cs b/Runtime/Blueprint/Input/BlueprintUGUIInputManager.cs new file mode 100644 index 0000000..16d0f52 --- /dev/null +++ b/Runtime/Blueprint/Input/BlueprintUGUIInputManager.cs @@ -0,0 +1,239 @@ +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; + +namespace XericLibrary.Runtime.Blueprint +{ + /// + /// 蓝图 UGUI 输入管理器 —— 负责绑定 UGUI EventTrigger 到背景 Image, + /// 将 UGUI 指针事件转发到 的分发方法。 + /// + /// 该类仅处理与 UGUI EventTrigger 相关的绑定逻辑,不感知新旧输入系统的差异。 + /// 由 创建并在 OnUpdate 中驱动。 + /// + /// + public class BlueprintUGUIInputManager + { + /// + /// 当前帧鼠标/指针在屏幕空间的位置。 + /// 每帧由 刷新。 + /// + public static Vector2 MousePosition { get; private set; } + + /// + /// 当前帧鼠标/指针是否处于新输入系统模式。 + /// true = 新输入系统(ENABLE_INPUT_SYSTEM 已定义); + /// false = 旧输入系统。 + /// + public static bool IsNewInputSystem + { + get + { +#if ENABLE_INPUT_SYSTEM + return true; +#else + return false; +#endif + } + } + + /// + /// 更新静态鼠标位置。每帧由 BlueprintInputReceiver 的 OnUpdate 调用。 + /// 内部根据条件编译选择鼠标位置读取源。 + /// + public static void UpdateMousePosition() + { +#if ENABLE_INPUT_SYSTEM + MousePosition = UnityEngine.InputSystem.Mouse.current?.position.ReadValue() ?? Vector2.zero; +#else + MousePosition = (Vector2)Input.mousePosition; +#endif + } + + /// + /// 返回当前是否按住指定鼠标按钮。 + /// 内部根据条件编译选择检测方式。 + /// + /// 0=左键, 1=右键, 2=中键 + public static bool IsButtonPressed(int button) + { +#if ENABLE_INPUT_SYSTEM + var mouse = UnityEngine.InputSystem.Mouse.current; + if (mouse == null) return false; + return button switch + { + 0 => mouse.leftButton.isPressed, + 1 => mouse.rightButton.isPressed, + 2 => mouse.middleButton.isPressed, + _ => false, + }; +#else + return Input.GetMouseButton(button); +#endif + } + + // ===== 实例部分 ===== + + private BlueprintGraph _graph; + private Transform _bgTransform; + private EventTrigger _eventTrigger; + private bool _isBound; + + // 背景 GameObject 名称(与 QuickUGUIGraphGridBackgroundTool 一致) + private const string BackgroundGoName = "__Bp_GridBackground"; + + // ===== 生命周期 ===== + + /// 初始化 UGUI 输入管理器并绑定到指定蓝图。 + public BlueprintUGUIInputManager(BlueprintGraph graph) + { + _graph = graph; + } + + /// + /// 尝试绑定 EventTrigger 到背景 Image。 + /// 返回 true 表示绑定成功,false 表示背景尚未创建。 + /// + public bool TryBind() + { + if (_isBound) return true; + if (_graph?.Canvas == null) return false; + + var root = _graph.Canvas.GetRootTransform(); + if (root == null) return false; + + _bgTransform = root.Find(BackgroundGoName); + if (_bgTransform == null) return false; + + _eventTrigger = _bgTransform.gameObject.GetComponent(); + if (_eventTrigger == null) + _eventTrigger = _bgTransform.gameObject.AddComponent(); + else + _eventTrigger.triggers.Clear(); + + BindEventTrigger(); + _isBound = true; + return true; + } + + /// 解绑并清理 EventTrigger。 + public void Unbind() + { + if (_eventTrigger != null) + { + _eventTrigger.triggers.Clear(); + _eventTrigger = null; + } + _bgTransform = null; + _isBound = false; + } + + /// 是否已绑定。 + public bool IsBound => _isBound; + + /// 背景 RectTransform(用于屏幕点包含检测)。 + public RectTransform BgRectTransform => _bgTransform as RectTransform; + + // ===== EventTrigger 绑定 ===== + + private void BindEventTrigger() + { + if (_eventTrigger == null) return; + + AddEntry(EventTriggerType.PointerEnter, OnEventPointerEnter); + AddEntry(EventTriggerType.PointerExit, OnEventPointerExit); + AddEntry(EventTriggerType.PointerDown, OnEventPointerDown); + AddEntry(EventTriggerType.PointerUp, OnEventPointerUp); + AddEntry(EventTriggerType.BeginDrag, OnEventBeginDrag); + AddEntry(EventTriggerType.Drag, OnEventDrag); + AddEntry(EventTriggerType.EndDrag, OnEventEndDrag); + AddEntry(EventTriggerType.Scroll, OnEventScroll); + } + + private void AddEntry(EventTriggerType eventType, UnityEngine.Events.UnityAction callback) + { + var entry = new EventTrigger.Entry { eventID = eventType }; + entry.callback.AddListener(callback); + _eventTrigger.triggers.Add(entry); + } + + // ===== 坐标转换 ===== + + private Vector2 ScreenToCanvas(Vector2 screenPos) + { + return _graph?.Canvas != null ? _graph.Canvas.ScreenToCanvas(screenPos) : screenPos; + } + + private Vector2 ScreenToCanvas(PointerEventData data) + { + return ScreenToCanvas(data.position); + } + + // ===== EventTrigger 回调 ===== + + private void OnEventPointerEnter(BaseEventData _) + { + if (_graph != null) + BlueprintToolProvider.DispatchPointerEnter(_graph); + } + + private void OnEventPointerExit(BaseEventData _) + { + if (_graph != null) + BlueprintToolProvider.DispatchPointerExit(_graph); + } + + private void OnEventPointerDown(BaseEventData data) + { + if (_graph == null || !(data is PointerEventData pData)) return; + _graph.BeginFrame(); + var pt = ScreenToCanvas(pData); + _graph.MarkDirty(); + BlueprintToolProvider.DispatchPointerDown(_graph, pt, (int)pData.button); + } + + private void OnEventPointerUp(BaseEventData data) + { + if (_graph == null || !(data is PointerEventData pData)) return; + var pt = ScreenToCanvas(pData); + BlueprintToolProvider.DispatchPointerUp(_graph, pt, (int)pData.button); + } + + // 拖拽状态 + private int _dragButton = -1; + private Vector2 _dragStartPos; + + private void OnEventBeginDrag(BaseEventData data) + { + if (_graph == null || !(data is PointerEventData pData)) return; + _dragButton = (int)pData.button; + _dragStartPos = ScreenToCanvas(pData); + } + + private void OnEventDrag(BaseEventData data) + { + if (_graph == null || !(data is PointerEventData pData)) return; + var current = ScreenToCanvas(pData); + var delta = current - _dragStartPos; + _dragStartPos = current; + BlueprintToolProvider.DispatchPointerDrag(_graph, current, delta, _dragButton); + } + + private void OnEventEndDrag(BaseEventData data) + { + if (_graph == null || !(data is PointerEventData pData)) return; + BlueprintToolProvider.DispatchPointerUp(_graph, ScreenToCanvas(pData), (int)pData.button); + _dragButton = -1; + } + + private void OnEventScroll(BaseEventData data) + { + if (_graph == null || !(data is PointerEventData pData)) return; + _graph.BeginFrame(); + var pt = ScreenToCanvas(pData); + var scroll = new Vector2(-pData.scrollDelta.x, -pData.scrollDelta.y); + _graph.MarkDirty(); + BlueprintToolProvider.DispatchScroll(_graph, pt, scroll); + } + } +} diff --git a/Runtime/Blueprint/Input/BlueprintUGUIInputManager.cs.meta b/Runtime/Blueprint/Input/BlueprintUGUIInputManager.cs.meta new file mode 100644 index 0000000..1472afa --- /dev/null +++ b/Runtime/Blueprint/Input/BlueprintUGUIInputManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 00688a047bee7ee4b837f07886b45d44 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Blueprint/Input/QuickGraphInputTool.cs b/Runtime/Blueprint/Input/QuickGraphInputTool.cs new file mode 100644 index 0000000..32f883f --- /dev/null +++ b/Runtime/Blueprint/Input/QuickGraphInputTool.cs @@ -0,0 +1,226 @@ +using UnityEngine; +using XericLibrary.Runtime.Blueprint.Render; + +namespace XericLibrary.Runtime.Blueprint +{ + /// + /// QuickGraph 输入交互工具 —— 处理画布平移、缩放和节点选择。 + /// 中键拖拽 = 平移;左键点击 = 选择;滚轮 = 缩放。 + /// 快捷键(新输入系统):方向键 = 平移,Ctrl+± = 缩放,Ctrl+H = 归位。 + /// + [BlueprintTool(phase: ToolPhase.PreUpdate, order: 50)] + [BlueprintTheme("QuickGraph")] + public class QuickGraphInputTool : BlueprintTool + { + // ---------- 配置(从 ToolConfigAssets 读取) ---------- + private QuickGraphInputConfig Config + { + get + { + if (ToolConfig is QuickGraphInputConfig external) + return external; + if (_defaultConfig == null) + _defaultConfig = ScriptableObject.CreateInstance(); + return _defaultConfig; + } + } + private static QuickGraphInputConfig _defaultConfig; + + // ---------- 视图状态 ---------- + private Vector2 _targetPanOffset; + private float _targetZoomLevel = 1f; + private bool _viewInitialized; + + // 用于检测帧间变化 + private Vector2 _prevPanOffset; + private float _prevZoomLevel; + + // ---------- 拖拽状态 ---------- + private bool _isDragging; + private int _activeDragButton = -1; + private Vector2 _dragStartPan; + + // ---------- 选择状态 ---------- + private BlueprintElementBase _hoveredElement; + private BlueprintElementBase _selectedElement; + + // ===== 更新(平滑插值 + 键盘轮询) ===== + + public override void OnUpdate(float deltaTime) + { + if (Graph?.Canvas == null) return; + + var cfg = Config; + var canvas = Graph.Canvas; + + // ── 同步缩放范围 ── + canvas.MinZoom = cfg.MinZoom; + canvas.MaxZoom = cfg.MaxZoom; + + if (!_viewInitialized) + { + _targetPanOffset = canvas.PanOffset; + _targetZoomLevel = canvas.ZoomLevel; + _prevPanOffset = _targetPanOffset; + _prevZoomLevel = _targetZoomLevel; + _viewInitialized = true; + } + + // ── 键盘轮询(新输入系统 Pan 动作值) ── + _targetPanOffset += BlueprintInputManager.LastPanDirection + * cfg.KeyboardPanSpeed * deltaTime; + + // ── 键盘轮询(新输入系统 Zoom 动作值) ── + // zoomInput 对键盘按键是 ±1(按下一下),对轴是 0~1 连续值。 + // 统一按一个 scroll notch = 120 缩放,scale = 120 / ZoomDivisor + float zoomInput = BlueprintInputManager.LastZoomAxis; + if (!Mathf.Approximately(zoomInput, 0f)) + { + float oldZoom = _targetZoomLevel; + _targetZoomLevel = Mathf.Clamp( + _targetZoomLevel + zoomInput / cfg.ZoomDivisor, + canvas.MinZoom, canvas.MaxZoom); + // 以鼠标焦点为中心缩放(键盘无光标,从鼠标读取) + ApplyZoomFocus(ref _targetPanOffset, _targetZoomLevel, oldZoom); + // 归零,避免持续累加 + BlueprintInputManager.LastZoomAxis = 0f; + } + + // ── 平滑插值到目标值 ── + canvas.PanOffset = Vector2.Lerp( + canvas.PanOffset, _targetPanOffset, cfg.PanLerpSpeed * deltaTime); + canvas.ZoomLevel = Mathf.Lerp( + canvas.ZoomLevel, _targetZoomLevel, cfg.ZoomLerpSpeed * deltaTime); + + // ── 标记脏 ── + if (canvas.PanOffset != _prevPanOffset || !Mathf.Approximately(canvas.ZoomLevel, _prevZoomLevel)) + { + Graph.MarkDirty(); + _prevPanOffset = canvas.PanOffset; + _prevZoomLevel = canvas.ZoomLevel; + } + } + + // ===== 鼠标移动 ===== + + public override void OnPointerMove(Vector2 canvasPoint, Vector2 delta) + { + if (Graph == null || _isDragging) return; + + var hit = Graph.HitTest(canvasPoint); + _hoveredElement = hit as BlueprintElementBase; + } + + // ===== 鼠标按下 ===== + + public override void OnPointerDown(Vector2 canvasPoint, int mouseButton) + { + if (Graph == null) return; + + if (mouseButton == 2) // 中键 → 平移 + { + _isDragging = true; + _activeDragButton = 2; + _dragStartPan = _targetPanOffset; + } + // 左键点击选择由元素层的 IBlueprintEventHandler.OnPointerDown 处理 + } + + // ===== 鼠标释放 ===== + + public override void OnPointerUp(Vector2 canvasPoint, int mouseButton) + { + if (_isDragging && _activeDragButton == mouseButton) + { + _isDragging = false; + _activeDragButton = -1; + } + } + + // ===== 拖拽 ===== + + public override void OnPointerDrag(Vector2 canvasPoint, Vector2 delta, int mouseButton) + { + if (Graph?.Canvas == null) return; + + if (mouseButton == 2) // 中键拖拽 = 平移 + { + _targetPanOffset += delta * Graph.Canvas.ZoomLevel; + Graph.MarkDirty(); + } + } + + // ===== 滚轮 ===== + + public override void OnScroll(Vector2 canvasPoint, Vector2 scrollDelta) + { + if (Graph == null) return; + + float oldZoom = _targetZoomLevel; + + _targetZoomLevel = Mathf.Clamp( + _targetZoomLevel + scrollDelta.y / Config.ZoomDivisor, + Graph.Canvas.MinZoom, Graph.Canvas.MaxZoom); + + if (!Mathf.Approximately(oldZoom, _targetZoomLevel)) + { + // 以鼠标光标为中心缩放 + _targetPanOffset += canvasPoint * (oldZoom - _targetZoomLevel); + Graph.MarkDirty(); + } + } + + // ===== 快捷键(仅新输入系统) ===== + + public override void OnAction(string actionName) + { + if (Graph?.Canvas == null) return; + + switch (actionName) + { + case BlueprintInputConstants.FocusHome: + ResetView(); + break; + case BlueprintInputConstants.Undo: + break; // TODO + case BlueprintInputConstants.Redo: + break; // TODO + case BlueprintInputConstants.NavigateParent: + break; // TODO + } + } + + // ===== 辅助 ===== + + public void ResetView() + { + _targetPanOffset = Vector2.zero; + _targetZoomLevel = 1f; + Graph?.MarkDirty(); + } + + private void SelectElement(BlueprintElementBase element) + { + _selectedElement = element; + } + + /// 当前悬停的元素(可能为 null) + public BlueprintElementBase HoveredElement => _hoveredElement; + + /// 当前选中的元素(可能为 null) + public BlueprintElementBase SelectedElement => _selectedElement; + + // ===== 以鼠标焦点为中心缩放(键盘调用 — 自行读取鼠标位置) ===== + + private void ApplyZoomFocus(ref Vector2 panOffset, float newZoom, float oldZoom) + { + if (Graph?.Canvas == null) return; + + // 通过 BlueprintUGUIInputManager 获取统一鼠标位置(已被 BlueprintInputReceiver 每帧刷新) + Vector2 canvasCursor = Graph.Canvas.ScreenToCanvas(BlueprintUGUIInputManager.MousePosition); + panOffset += canvasCursor * (oldZoom - newZoom); + } + + // =====(旧 ZoomFocusAdjust 已删除 — 替代为 ApplyZoomFocus,OnScroll 直接内联 canvasPoint 计算)===== + } +} diff --git a/Runtime/Blueprint/Input/QuickGraphInputTool.cs.meta b/Runtime/Blueprint/Input/QuickGraphInputTool.cs.meta new file mode 100644 index 0000000..5376fd1 --- /dev/null +++ b/Runtime/Blueprint/Input/QuickGraphInputTool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1fe1086d7e9750541a9a5de047e05ce6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Blueprint/PortType.meta b/Runtime/Blueprint/PortType.meta new file mode 100644 index 0000000..531f262 --- /dev/null +++ b/Runtime/Blueprint/PortType.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 87791decaa857844d8b3d8d266733abb +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Blueprint/Render.meta b/Runtime/Blueprint/Render.meta new file mode 100644 index 0000000..3a0b2bc --- /dev/null +++ b/Runtime/Blueprint/Render.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: af33ee557aaf7d44182e195524832a41 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Blueprint/Render/BlueprintCurveRenderer.cs b/Runtime/Blueprint/Render/BlueprintCurveRenderer.cs new file mode 100644 index 0000000..f4c9c34 --- /dev/null +++ b/Runtime/Blueprint/Render/BlueprintCurveRenderer.cs @@ -0,0 +1,14 @@ +using UnityEngine; +using XericLibrary.Runtime.UIGraph; + +namespace XericLibrary.Runtime.Blueprint.Render +{ + /// + /// 最简曲线渲染器 —— 继承 CurveCacheRendererBase,无任何额外设定。 + /// 供蓝图渲染工具使用,避免 UICurveRenderer 等自带的默认配置干扰。 + /// + [DisallowMultipleComponent] + public sealed class BlueprintCurveRenderer : CurveCacheRendererBase + { + } +} diff --git a/Runtime/Blueprint/Render/BlueprintCurveRenderer.cs.meta b/Runtime/Blueprint/Render/BlueprintCurveRenderer.cs.meta new file mode 100644 index 0000000..04c36f1 --- /dev/null +++ b/Runtime/Blueprint/Render/BlueprintCurveRenderer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 53a46255c1675484489c772b9cd65277 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Blueprint/Render/BlueprintPrimitiveRenderer.cs b/Runtime/Blueprint/Render/BlueprintPrimitiveRenderer.cs new file mode 100644 index 0000000..42416ba --- /dev/null +++ b/Runtime/Blueprint/Render/BlueprintPrimitiveRenderer.cs @@ -0,0 +1,14 @@ +using UnityEngine; +using XericLibrary.Runtime.UIGraph; + +namespace XericLibrary.Runtime.Blueprint.Render +{ + /// + /// 最简图元渲染器 —— 继承 PrimitiveCacheRendererBase,无任何额外设定。 + /// 供蓝图渲染工具使用,避免 UICurveRenderer 等自带的默认配置干扰。 + /// + [DisallowMultipleComponent] + public sealed class BlueprintPrimitiveRenderer : PrimitiveCacheRendererBase + { + } +} diff --git a/Runtime/Blueprint/Render/BlueprintPrimitiveRenderer.cs.meta b/Runtime/Blueprint/Render/BlueprintPrimitiveRenderer.cs.meta new file mode 100644 index 0000000..5a8da44 --- /dev/null +++ b/Runtime/Blueprint/Render/BlueprintPrimitiveRenderer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6292bb3966ffc0049b2f7546debd0a73 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Blueprint/Render/Config.meta b/Runtime/Blueprint/Render/Config.meta new file mode 100644 index 0000000..1dc2c07 --- /dev/null +++ b/Runtime/Blueprint/Render/Config.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c7cfe95b8a3355c4e8e6585d2266fb5b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Blueprint/Render/Config/QuickGraphGridBackgroundConfig.cs b/Runtime/Blueprint/Render/Config/QuickGraphGridBackgroundConfig.cs new file mode 100644 index 0000000..73b6f99 --- /dev/null +++ b/Runtime/Blueprint/Render/Config/QuickGraphGridBackgroundConfig.cs @@ -0,0 +1,145 @@ +using System; +using UnityEngine; + +namespace XericLibrary.Runtime.Blueprint.Render +{ + /// + /// QuickGraph 网格背景配置表 —— 控制 Shader 全部可调参数。 + /// 材质实例由此配置统一管理,所有调用者通过 获取同一实例。 + /// 封装所有材质属性写入(含 _Transform 同步画布平移/缩放)。 + /// 右键 Project 窗口 → Create/Blueprint/QuickGraph/Grid Background Config 创建资产。 + /// + [CreateAssetMenu( + fileName = "QuickGraphGridBackgroundConfig", + menuName = "Xeric Library/Blueprint/QuickGraph/Grid Background Config", + order = 3)] + [BlueprintTheme("QuickGraph")] + public class QuickGraphGridBackgroundConfig : BlueprintToolConfigBase + { + /// 目标工具类型 + public override System.Type TargetToolType + { + get { return typeof(QuickUGUIGraphGridBackgroundTool); } + } + + [Header("材质来源")] + [Tooltip("网格 Shader 名称")] + public string ShaderName = "XericLibrary/BluePrint/BlueprintBackgound_GridLine"; + + [Tooltip("可选:直接指定背景材质。设置后将忽略 ShaderName。")] + public Material OverrideMaterial = null; + + [Header("网格参数")] + [Tooltip("网格间距(画布单位)。值越小网格越密。")] + public float GridSize = 100f; + + [Tooltip("叠加网格密度")] + public float GridOverlayPower = 5f; + + [Tooltip("线条阈值。0.5=极粗, 1=极细")] + [Range(0.5f, 1f)] + public float GridLineThreshold = 0.99f; + + [Tooltip("线条扩展/柔和度。越大线条越宽。")] + [Range(0.001f, 1f)] + public float GridExp = 0.001f; + + [Header("颜色")] + [Tooltip("网格线颜色")] + public Color GridColor = Color.white; + + [Tooltip("背景底色")] + public Color GridBackgroundColor = Color.black; + + // ─── 运行时缓存的材质实例 ─── + + [NonSerialized] + private Material _cachedMaterial; + + /// + /// 获取或创建材质实例。 + /// - 若 OverrideMaterial 不为空,直接返回。 + /// - 否则按 ShaderName 查找 Shader,创建材质并缓存。 + /// - 同一配置实例上多次调用返回同一材质对象。 + /// + public Material GetOrCreateMaterial() + { + if (OverrideMaterial != null) + return OverrideMaterial; + + if (_cachedMaterial != null) + return _cachedMaterial; + + if (!string.IsNullOrEmpty(ShaderName)) + { + var shader = Shader.Find(ShaderName); + if (shader != null) + { + _cachedMaterial = new Material(shader); + _cachedMaterial.name = "BpGridBackground_Mat"; + } + } + + return _cachedMaterial; + } + + /// + /// 将当前配置的所有参数写入材质。 + /// 包括: + /// - _Transform(scaleX, scaleY, offsetX, offsetY):随画布 zoom / pan 同步更新; + /// - _GridOverlayPower、_GridLineThreshold、_GridExp; + /// - _GridColor、_GridBackgroundColor。 + /// 每次渲染时调用以保持与蓝图画布的缩放/平移同步。 + /// + /// 目标材质实例 + /// 背景板 RectTransform 的像素尺寸 (width, height) + /// 当前画布缩放级别 + /// 当前画布平移偏移量 + public void ApplyToMaterial(Material mat, Vector2 rectSize, float zoom, Vector2 panOffset) + { + if (mat == null) return; + + // ── _Transform: 基于画布 zoom / pan 同步 ── + // Shader 中: texCoord2 = uv * _Transform.xy + _Transform.zw + // uv (0,0) = Image 左下角, uv (1,1) = Image 右上角 + // CanvasToLocal: localPoint = canvasPoint * zoom + panOffset + // UV ↔ localPoint: uv = localPoint / rectSize + 0.5f + // ^^^^^ pivot(0.5,0.5) 的 UV 偏移 + // 代入: uv = (canvasPoint * zoom + panOffset) / rectSize + 0.5f + // 目标: gridPos = canvasPoint / GridSize = uv * scale + offset + // 解出: scale = rectSize / (zoom * GridSize) + // offset = -(panOffset + 0.5 * rectSize) / (zoom * GridSize) + float sx = rectSize.x / (zoom * GridSize); + float sy = rectSize.y / (zoom * GridSize); + float ox = -(panOffset.x + 0.5f * rectSize.x) / (zoom * GridSize); + float oy = -(panOffset.y + 0.5f * rectSize.y) / (zoom * GridSize); + mat.SetVector("_Transform", new Vector4(sx, sy, ox, oy)); + + // ── 网格线参数 ── + mat.SetFloat("_GridOverlayPower", GridOverlayPower); + mat.SetFloat("_GridLineThreshold", GridLineThreshold); + mat.SetFloat("_GridExp", GridExp); + + // ── 颜色 ── + mat.SetColor("_GridColor", GridColor); + mat.SetColor("_GridBackgroundColor", GridBackgroundColor); + } + + /// + /// 释放此配置创建的材质实例(不释放 OverrideMaterial)。 + /// + public void ReleaseMaterial() + { + if (_cachedMaterial != null) + { +#if UNITY_EDITOR + if (!Application.isPlaying) + DestroyImmediate(_cachedMaterial); + else +#endif + Destroy(_cachedMaterial); + _cachedMaterial = null; + } + } + } +} diff --git a/Runtime/Blueprint/Render/Config/QuickGraphGridBackgroundConfig.cs.meta b/Runtime/Blueprint/Render/Config/QuickGraphGridBackgroundConfig.cs.meta new file mode 100644 index 0000000..39beb13 --- /dev/null +++ b/Runtime/Blueprint/Render/Config/QuickGraphGridBackgroundConfig.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 32753aff001677a4b81e10dfae39b4cb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Blueprint/Render/Config/QuickGraphInputConfig.cs b/Runtime/Blueprint/Render/Config/QuickGraphInputConfig.cs new file mode 100644 index 0000000..0dd2b6a --- /dev/null +++ b/Runtime/Blueprint/Render/Config/QuickGraphInputConfig.cs @@ -0,0 +1,60 @@ +using System; +using UnityEngine; + +namespace XericLibrary.Runtime.Blueprint.Render +{ + /// + /// QuickGraph 输入工具配置表 —— 控制平移/缩放插值速率、灵敏度等可调参数。 + /// 右键 Project 窗口 → Create/Blueprint/QuickGraph/Input Config 创建资产。 + /// 拖入蓝图组件的 ToolConfigAssets 列表即可覆盖默认值。 + /// + [CreateAssetMenu( + fileName = "QuickGraphInputConfig", + menuName = "Xeric Library/Blueprint/QuickGraph/Input Config", + order = 4)] + [BlueprintTheme("QuickGraph")] + public class QuickGraphInputConfig : BlueprintToolConfigBase + { + /// 目标工具类型 + public override System.Type TargetToolType + { + get { return typeof(QuickGraphInputTool); } + } + + [Header("平移")] + [Tooltip("平移动画插值速率(越大跟随越快,3=适中,10=几乎瞬移)")] + [Range(1f, 20f)] + public float PanLerpSpeed = 3f; + + [Header("缩放")] + [Tooltip("缩放动画插值速率(越大跟随更快)")] + [Range(1f, 30f)] + public float ZoomLerpSpeed = 8f; + + [Tooltip("滚轮缩放除数。公式:zoom += scrollDelta / ZoomDivisor。\nUnity Input System 的 scrollDelta.y 标准值为 120/格,\n1200 = 每格 +0.1,600 = 每格 +0.2,300 = 每格 +0.4")] + [Range(100f, 5000f)] + public float ZoomDivisor = 1200f; + + [Header("缩放范围")] + [Tooltip("最小缩放级别")] + [Range(0.01f, 1f)] + public float MinZoom = 0.1f; + [Tooltip("最大缩放级别")] + [Range(1f, 10f)] + public float MaxZoom = 3f; + + [Header("键盘")] + [Tooltip("键盘方向键平移速度(画布单位/秒)")] + [Range(50f, 2000f)] + public float KeyboardPanSpeed = 600f; + + [Tooltip("键盘 +/- 缩放速度(每次帧增量,建议 0.01~0.5)")] + [Range(0.005f, 0.5f)] + public float KeyboardZoomSpeed = 0.05f; + + [Header("框选")] + [Tooltip("框选拖拽触发阈值(像素)")] + [Range(1f, 20f)] + public float DragThreshold = 5f; + } +} diff --git a/Runtime/Blueprint/Render/Config/QuickGraphInputConfig.cs.meta b/Runtime/Blueprint/Render/Config/QuickGraphInputConfig.cs.meta new file mode 100644 index 0000000..b8e79bd --- /dev/null +++ b/Runtime/Blueprint/Render/Config/QuickGraphInputConfig.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cfce52570fac4f5488becd85ce4f023b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Blueprint/Render/Config/QuickGraphNodeRenderConfig.cs b/Runtime/Blueprint/Render/Config/QuickGraphNodeRenderConfig.cs new file mode 100644 index 0000000..f191bfd --- /dev/null +++ b/Runtime/Blueprint/Render/Config/QuickGraphNodeRenderConfig.cs @@ -0,0 +1,64 @@ +using System; +using UnityEngine; + +namespace XericLibrary.Runtime.Blueprint.Render +{ + /// + /// QuickGraph 节点渲染配置表 —— 控制节点矩形、边框、圆角等视觉参数。 + /// 右键 Project 窗口 → Create/Blueprint/QuickGraph/Node Render Config 创建资产。 + /// 拖入蓝图组件的 ToolConfigAssets 列表即可覆盖默认值。 + /// + [CreateAssetMenu( + fileName = "QuickGraphNodeRenderConfig", + menuName = "Xeric Library/Blueprint/QuickGraph/Node Render Config", + order = 1)] + [BlueprintTheme("QuickGraph")] + public class QuickGraphNodeRenderConfig : BlueprintToolConfigBase + { + /// 目标工具类型 + public override System.Type TargetToolType + { + get { return typeof(QuickUGUIGraphNodeRenderTool); } + } + + [Header("尺寸")] + [Tooltip("节点宽度(像素)")] + public float NodeWidth = 90f; + + [Tooltip("节点高度(像素)")] + public float NodeHeight = 130f; + + [Header("边框")] + [Tooltip("边框厚度(像素)")] + public float BorderThickness = 3f; + + [Header("圆角")] + [Tooltip("圆角大小(0~1 归一化)")] + [Range(0f, 0.5f)] + public float ChamferSize = 0.2f; + + [Tooltip("圆角细分段数")] + [Range(1, 16)] + public int ChamferSegments = 4; + + [Header("颜色")] + [Tooltip("节点默认背景色")] + public Color DefaultBackgroundColor = new Color(0.16f, 0.16f, 0.16f); + [Tooltip("节点默认文字色")] + public Color DefaultTextColor = Color.white; + + [Header("文本")] + [Tooltip("节点标题字号")] + public float TitleFontSize = 14f; + [Tooltip("节点标题字体(留空使用默认 TMP 字体)")] + public TMPro.TMP_FontAsset TitleFont; + + [Header("LOD(缩放等级细节)")] + [Tooltip("归一化缩放值低于此阈值时触发 LOD 0(极简:纯色节点,无文本)。0~1。默认 0.3。")] + [Range(0f, 1f)] + public float Lod0Threshold = 0.3f; + [Tooltip("归一化缩放值低于此阈值时触发 LOD 1(简化:节点+边框,无文本);高于此值为 LOD 2(完整渲染)。0~1。默认 0.7。")] + [Range(0f, 1f)] + public float Lod1Threshold = 0.7f; + } +} diff --git a/Runtime/Blueprint/Render/Config/QuickGraphNodeRenderConfig.cs.meta b/Runtime/Blueprint/Render/Config/QuickGraphNodeRenderConfig.cs.meta new file mode 100644 index 0000000..43add16 --- /dev/null +++ b/Runtime/Blueprint/Render/Config/QuickGraphNodeRenderConfig.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8ab3d3829c73815468ed8c5ae306a6ee +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Blueprint/Render/Config/QuickGraphWireRenderConfig.cs b/Runtime/Blueprint/Render/Config/QuickGraphWireRenderConfig.cs new file mode 100644 index 0000000..c295213 --- /dev/null +++ b/Runtime/Blueprint/Render/Config/QuickGraphWireRenderConfig.cs @@ -0,0 +1,66 @@ +using System; +using UnityEngine; +using XericLibrary.Runtime.UIGraph; + +namespace XericLibrary.Runtime.Blueprint.Render +{ + /// + /// QuickGraph 连线渲染配置表 —— 控制贝塞尔曲线宽度、箭头形状等全部视觉参数。 + /// 右键 Project 窗口 → Create/Blueprint/QuickGraph/Wire Render Config 创建资产。 + /// 拖入蓝图组件的 ToolConfigAssets 列表即可覆盖默认值。 + /// + [CreateAssetMenu( + fileName = "QuickGraphWireRenderConfig", + menuName = "Xeric Library/Blueprint/QuickGraph/Wire Render Config", + order = 2)] + [BlueprintTheme("QuickGraph")] + public class QuickGraphWireRenderConfig : BlueprintToolConfigBase + { + /// 目标工具类型 + public override System.Type TargetToolType + { + get { return typeof(QuickUGUIGraphWireRenderTool); } + } + + [Header("连线")] + [Tooltip("连线宽度(像素)")] + [Range(1f, 20f)] + public float WireWidth = 5f; + + [Header("贝塞尔手柄")] + [Tooltip("源端手柄从端口伸出的距离(画布单位)。数值越大曲线越平缓。")] + [Range(10f, 500f)] + public float SourceHandleLength = 80f; + [Tooltip("目标端手柄从端口伸出的距离(画布单位)。")] + [Range(10f, 500f)] + public float TargetHandleLength = 80f; + + [Header("箭头")] + [Tooltip("箭头形状")] + public ArrowShape ArrowShape = ArrowShape.Triangle; + + [Tooltip("箭头是否反向")] + public bool ArrowReversed = false; + + [Tooltip("箭头在曲线上的位置(0=起点, 1=终点)")] + [Range(0f, 1f)] + public float ArrowProgress = 1f; + + [Tooltip("箭头宽度(垂直切线方向,像素)")] + [Range(4f, 40f)] + public float ArrowWidth = 12f; + + [Tooltip("箭头高度(沿切线方向,像素)")] + [Range(4f, 40f)] + public float ArrowHeight = 10f; + + [Tooltip("深度补偿:-1=尾部对齐曲线点,0=中心对齐,1=头部对齐曲线点")] + [Range(-1f, 1f)] + public float ArrowDepthCompensation = -1f; + + [Header("曲线质量")] + [Tooltip("贝塞尔曲线细分段数")] + [Range(8, 64)] + public int TessellationSegments = 32; + } +} diff --git a/Runtime/Blueprint/Render/Config/QuickGraphWireRenderConfig.cs.meta b/Runtime/Blueprint/Render/Config/QuickGraphWireRenderConfig.cs.meta new file mode 100644 index 0000000..d0b4e5e --- /dev/null +++ b/Runtime/Blueprint/Render/Config/QuickGraphWireRenderConfig.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 21208cffd6108e04d815bb385f0af3fc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Blueprint/Render/LayerContainerManager.cs b/Runtime/Blueprint/Render/LayerContainerManager.cs new file mode 100644 index 0000000..c86796f --- /dev/null +++ b/Runtime/Blueprint/Render/LayerContainerManager.cs @@ -0,0 +1,275 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; + +namespace XericLibrary.Runtime.Blueprint.Render +{ + /// + /// 层容器条目 —— 一层中挂载的所有渲染组件 + /// + public sealed class LayerContainerEntry + { + /// 容器根 GameObject,锚点撑满蓝图 + public GameObject Container; + + /// 图元渲染器(节点矩形) + public BlueprintPrimitiveRenderer PrimitiveRenderer; + + /// 曲线渲染器(连线) + public BlueprintCurveRenderer CurveRenderer; + + /// 节点 → TMP Text 映射(挂在此容器下) + public Dictionary NodeTexts + = new Dictionary(); + } + + /// + /// 共享层容器管理器 —— 管理桶排序中每一层的容器 GameObject。 + /// 一层一个容器,内部按 sibling 顺序挂载: + /// 1. Image 背景(由 GridBackgroundTool 处理,挂 root 下) + /// 2. __Curve(连线) + /// 3. __Primitive(节点矩形) + /// 4. TMP Text(节点文字) + /// 各渲染工具通过此管理器共享同一组层容器。 + /// + /// 所有创建的 GameObject 默认 HideFlags.HideAndDontSave, + /// 避免在编辑器→运行/运行→编辑器切换时残留场景。 + /// 调试时可通过 GraphTheoryBlueprintComponent._showDebugObjects 强制显示。 + /// + public class LayerContainerManager + { + /// 所有蓝图实例共享的层管理器(蓝图实例 → 层管理器) + private static Dictionary s_Instances + = new Dictionary(); + + /// 获取或创建指定蓝图实例的层容器管理器 + public static LayerContainerManager GetForGraph(BlueprintGraph graph, Transform renderRoot) + { + if (s_Instances.TryGetValue(graph, out var mgr) && mgr != null) + return mgr; + + mgr = new LayerContainerManager(renderRoot); + s_Instances[graph] = mgr; + return mgr; + } + + /// 释放蓝图实例的层容器 + public static void ReleaseForGraph(BlueprintGraph graph) + { + if (s_Instances.TryGetValue(graph, out var mgr)) + { + mgr.DestroyAll(); + s_Instances.Remove(graph); + } + } + + private readonly Transform _renderRoot; + private readonly Dictionary _layers + = new Dictionary(); + + private LayerContainerManager(Transform renderRoot) + { + _renderRoot = renderRoot; + } + + /// + /// 获取指定层级的容器条目。不存在则创建。 + /// + public LayerContainerEntry GetOrCreateLayer(int layer) + { + if (_layers.TryGetValue(layer, out var entry) && entry != null) + return entry; + + // --- 创建容器 --- + var container = new GameObject($"__BpLayer_{layer:D2}", typeof(RectTransform)); + container.hideFlags = HideFlags.HideAndDontSave; + container.transform.SetParent(_renderRoot, false); + var ctRt = container.GetComponent(); + ctRt.anchorMin = Vector2.zero; + ctRt.anchorMax = Vector2.one; + ctRt.offsetMin = Vector2.zero; + ctRt.offsetMax = Vector2.zero; + + entry = new LayerContainerEntry { Container = container }; + + // --- 创建连线渲染器(底层) --- + var curveGo = new GameObject("__Curve", typeof(RectTransform)); + curveGo.hideFlags = HideFlags.HideAndDontSave; + curveGo.transform.SetParent(container.transform, false); + var crRt = curveGo.GetComponent(); + crRt.anchorMin = Vector2.zero; + crRt.anchorMax = Vector2.one; + crRt.offsetMin = Vector2.zero; + crRt.offsetMax = Vector2.zero; + entry.CurveRenderer = curveGo.AddComponent(); + + // --- 创建图元渲染器(中层) --- + var primGo = new GameObject("__Primitive", typeof(RectTransform)); + primGo.hideFlags = HideFlags.HideAndDontSave; + primGo.transform.SetParent(container.transform, false); + var prRt = primGo.GetComponent(); + prRt.anchorMin = Vector2.zero; + prRt.anchorMax = Vector2.one; + prRt.offsetMin = Vector2.zero; + prRt.offsetMax = Vector2.zero; + entry.PrimitiveRenderer = primGo.AddComponent(); + + _layers[layer] = entry; + ReorderAllContainers(); + return entry; + } + + /// + /// 尝试获取已存在的层容器(不创建) + /// + public LayerContainerEntry TryGetLayer(int layer) + { + _layers.TryGetValue(layer, out var entry); + return entry; + } + + /// + /// 获取或创建节点文本(挂到指定层容器下) + /// + public TMPro.TMP_Text GetOrCreateNodeText(BlueprintNode node, int layer) + { + var entry = GetOrCreateLayer(layer); + if (entry.NodeTexts.TryGetValue(node, out var text) && text != null) + return text; + + var textGo = new GameObject($"__Text_{node.ElementId}", typeof(RectTransform)); + textGo.hideFlags = HideFlags.HideAndDontSave; + textGo.transform.SetParent(entry.Container.transform, false); + + var tmp = textGo.AddComponent(); + tmp.alignment = TMPro.TextAlignmentOptions.Center; + tmp.fontSize = 12; + tmp.enableAutoSizing = false; + + var rt = tmp.rectTransform; + rt.pivot = new Vector2(0.5f, 0.5f); + rt.anchorMin = new Vector2(0.5f, 0.5f); + rt.anchorMax = new Vector2(0.5f, 0.5f); + + // 放到该层最末尾(覆盖矩形之上) + tmp.rectTransform.SetAsLastSibling(); + + entry.NodeTexts[node] = tmp; + return tmp; + } + + /// + /// 尝试获取已存在的节点文本对象,不存在则返回 null。 + /// + public TMPro.TMP_Text TryGetNodeText(BlueprintNode node, int layer) + { + if (!_layers.TryGetValue(layer, out var entry) || entry == null) return null; + entry.NodeTexts.TryGetValue(node, out var text); + return text; + } + + /// + /// 清除指定层容器的所有渲染内容(不清除容器结构) + /// + public void ClearLayer(int layer) + { + if (!_layers.TryGetValue(layer, out var entry) || entry == null) return; + + entry.PrimitiveRenderer?.ClearAll(); + entry.CurveRenderer?.ClearAll(); + + foreach (var kvp in entry.NodeTexts) + { + if (kvp.Value != null) + kvp.Value.gameObject.SetActive(false); + } + } + + /// + /// 清理指定层中已不存在的节点的文本对象 + /// + public void PruneDeadTexts(int layer, HashSet aliveNodes) + { + if (!_layers.TryGetValue(layer, out var entry) || entry == null) return; + + var dead = new List(); + foreach (var kvp in entry.NodeTexts) + { + if (!aliveNodes.Contains(kvp.Key)) + dead.Add(kvp.Key); + } + + foreach (var node in dead) + { + if (entry.NodeTexts.TryGetValue(node, out var text) && text != null) + { +#if UNITY_EDITOR + if (!Application.isPlaying) + Object.DestroyImmediate(text.gameObject); + else +#endif + Object.Destroy(text.gameObject); + } + entry.NodeTexts.Remove(node); + } + } + + /// + /// 移除超出活跃范围的所有层容器 + /// + public void PruneLayersAbove(int highestActiveLayer) + { + var toRemove = new List(); + foreach (var kvp in _layers) + { + if (kvp.Key > highestActiveLayer) + toRemove.Add(kvp.Key); + } + + foreach (int layer in toRemove) + { + if (_layers.TryGetValue(layer, out var entry) && entry != null) + { + if (entry.Container != null) + { +#if UNITY_EDITOR + if (!Application.isPlaying) + Object.DestroyImmediate(entry.Container); + else +#endif + Object.Destroy(entry.Container); + } + } + _layers.Remove(layer); + } + } + + /// + /// 销毁所有层容器 + /// + public void DestroyAll() + { + foreach (var kvp in _layers) + { + if (kvp.Value?.Container != null) + { +#if UNITY_EDITOR + if (!Application.isPlaying) + Object.DestroyImmediate(kvp.Value.Container); + else +#endif + Object.Destroy(kvp.Value.Container); + } + } + _layers.Clear(); + } + + private void ReorderAllContainers() + { + var sorted = new List>(_layers); + sorted.Sort((a, b) => a.Key.CompareTo(b.Key)); + foreach (var kvp in sorted) + kvp.Value.Container.transform.SetAsLastSibling(); + } + } +} diff --git a/Runtime/Blueprint/Render/LayerContainerManager.cs.meta b/Runtime/Blueprint/Render/LayerContainerManager.cs.meta new file mode 100644 index 0000000..bed7e08 --- /dev/null +++ b/Runtime/Blueprint/Render/LayerContainerManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 159fb4400ff6e5e4b859ddfd37f60a6c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Blueprint/Render/QuickUGUIGraphGridBackgroundTool.cs b/Runtime/Blueprint/Render/QuickUGUIGraphGridBackgroundTool.cs new file mode 100644 index 0000000..4c714d3 --- /dev/null +++ b/Runtime/Blueprint/Render/QuickUGUIGraphGridBackgroundTool.cs @@ -0,0 +1,141 @@ +using UnityEngine; +using UnityEngine.UI; + +namespace XericLibrary.Runtime.Blueprint.Render +{ + /// + /// 快速 UGUI 图论网格背景渲染工具。 + /// 在蓝图画布最底层创建一个全屏 Image 组件绘制网格背景, + /// 每帧将画布的 zoom / pan 同步到 Shader 的 _Transform 属性。 + /// 全部可调参数由 QuickGraphGridBackgroundConfig 配置资产提供。 + /// + [BlueprintTool(phase: ToolPhase.Render, order: 0)] + [BlueprintTheme("QuickGraph")] + public class QuickUGUIGraphGridBackgroundTool : BlueprintTool + { + private const string BackgroundGoName = "__Bp_GridBackground"; + + private QuickGraphGridBackgroundConfig _defaultConfig; + + private QuickGraphGridBackgroundConfig Config + { + get + { + if (ToolConfig is QuickGraphGridBackgroundConfig external) + return external; + if (_defaultConfig == null) + _defaultConfig = ScriptableObject.CreateInstance(); + return _defaultConfig; + } + } + + private Image _backgroundImage; + private Transform _renderRoot; + + public override void OnInitialize() + { + EnsureBackground(); + } + + public override void OnRender() + { + if (Graph == null) return; + EnsureBackground(); + SyncMaterialProperties(); + } + + private void EnsureBackground() + { + if (_backgroundImage != null) return; + + EnsureRenderRoot(); + if (_renderRoot == null) return; + + var existing = _renderRoot.Find(BackgroundGoName); + if (existing != null) + { + _backgroundImage = existing.GetComponent(); + if (_backgroundImage == null) + { + Object.DestroyImmediate(existing.gameObject); + } + else + { + ApplyMaterial(); + return; + } + } + + var bgGo = new GameObject(BackgroundGoName, typeof(RectTransform)); + bgGo.hideFlags = HideFlags.HideAndDontSave; + bgGo.transform.SetParent(_renderRoot, false); + bgGo.transform.SetAsFirstSibling(); + + var rt = bgGo.GetComponent(); + rt.anchorMin = Vector2.zero; + rt.anchorMax = Vector2.one; + rt.offsetMin = Vector2.zero; + rt.offsetMax = Vector2.zero; + + _backgroundImage = bgGo.AddComponent(); + _backgroundImage.raycastTarget = true; + + ApplyMaterial(); + } + + /// + /// 从配置获取材质并设置到 Image(仅首次执行)。 + /// + private void ApplyMaterial() + { + if (_backgroundImage == null) return; + var mat = Config.GetOrCreateMaterial(); + if (mat != null) + _backgroundImage.material = mat; + } + + /// + /// 每帧将当前画布 zoom / pan 同步到 Shader, + /// 同时写入配置中所有 Shader 属性。 + /// + private void SyncMaterialProperties() + { + if (_backgroundImage == null) return; + if (Graph == null || Graph.Canvas == null) return; + + var mat = _backgroundImage.material; + if (mat == null) return; + + var rt = _backgroundImage.rectTransform; + float zoom = Graph.Canvas.ZoomLevel; + Vector2 pan = Graph.Canvas.PanOffset; + + Config.ApplyToMaterial(mat, rt.rect.size, zoom, pan); + } + + private void EnsureRenderRoot() + { + if (_renderRoot != null) return; + if (Graph == null || Graph.Canvas == null) return; + _renderRoot = Graph.Canvas.GetRootTransform(); + } + + public override void OnDestroy() + { + if (_backgroundImage != null && _backgroundImage.gameObject != null) + { +#if UNITY_EDITOR + if (!Application.isPlaying) + Object.DestroyImmediate(_backgroundImage.gameObject); + else +#endif + Object.Destroy(_backgroundImage.gameObject); + _backgroundImage = null; + } + + Config.ReleaseMaterial(); + _defaultConfig = null; + _renderRoot = null; + } + } +} diff --git a/Runtime/Blueprint/Render/QuickUGUIGraphGridBackgroundTool.cs.meta b/Runtime/Blueprint/Render/QuickUGUIGraphGridBackgroundTool.cs.meta new file mode 100644 index 0000000..27f844a --- /dev/null +++ b/Runtime/Blueprint/Render/QuickUGUIGraphGridBackgroundTool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: da293e1f23cfca24b9ca4d40f91d2d8c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Blueprint/Render/QuickUGUIGraphNodeRenderTool.cs b/Runtime/Blueprint/Render/QuickUGUIGraphNodeRenderTool.cs new file mode 100644 index 0000000..c9c6aa2 --- /dev/null +++ b/Runtime/Blueprint/Render/QuickUGUIGraphNodeRenderTool.cs @@ -0,0 +1,264 @@ +using System.Collections.Generic; +using UnityEngine; +using TMPro; +using XericLibrary.Runtime.UIGraph; +using PrimitiveType = XericLibrary.Runtime.UIGraph.PrimitiveType; + +namespace XericLibrary.Runtime.Blueprint.Render +{ + /// + /// 快速 UGUI 图论节点样式渲染工具。 + /// 通过共享的 LayerContainerManager 管理每层容器, + /// 计算端口相对位置并更新 TMP 文本显示。 + /// 可调参数由 QuickGraphNodeRenderConfig 配置资产提供, + /// 拖入蓝图组件即可覆盖默认值。 + /// + /// LOD 模式(由 zoom 决定): + /// - LOD 0:节点纯色填充(bg=borderColor),无边框无文本 + /// - LOD 1:正常节点+边框,无文本 + /// - LOD 2:完整渲染(含文本) + /// + /// + [BlueprintTool(phase: ToolPhase.Render, order: 100)] + [BlueprintTheme("QuickGraph")] + public class QuickUGUIGraphNodeRenderTool : BlueprintTool + { + private enum LodLevel { Minimal, Simplified, Full } + + // --- 默认配置(代码内建,ScriptableObject.CreateInstance 创建) --- + private QuickGraphNodeRenderConfig _defaultConfig; + + // --- 当前生效的配置 --- + private QuickGraphNodeRenderConfig Config + { + get + { + if (ToolConfig is QuickGraphNodeRenderConfig external) + return external; + if (_defaultConfig == null) + _defaultConfig = ScriptableObject.CreateInstance(); + return _defaultConfig; + } + } + + private Transform _renderRoot; + private LayerContainerManager _layerMgr; + + public override void OnRender() + { + if (Graph == null) return; + EnsureRenderRoot(); + if (_renderRoot == null) return; + + var buckets = Graph.RenderLayers; + if (buckets == null) return; + + _layerMgr = LayerContainerManager.GetForGraph(Graph, _renderRoot); + + var cfg = Config; + + // ── 将配置默认值写入所有节点(config → node property) ── + foreach (var node in Graph.Nodes) + { + ApplyConfigToNode(node, cfg); + } + + // 视口裁剪:只渲染画布可见范围内的节点 + var viewport = Graph.Canvas.GetViewportRect(); + float margin = cfg.NodeWidth + cfg.NodeHeight; // 扩展一点避免边缘抖动 + viewport.xMin -= margin; + viewport.xMax += margin; + viewport.yMin -= margin; + viewport.yMax += margin; + + int bucketCount = buckets.BucketCount; + var layerNodes = new List[bucketCount]; + for (int i = 0; i < bucketCount; i++) + layerNodes[i] = new List(); + + foreach (var node in Graph.Nodes) + { + int layer = node.RenderLayer; + if (layer < 0 || layer >= bucketCount) continue; + // 视口裁剪过滤 + if (!viewport.Contains(node.Position)) continue; + layerNodes[layer].Add(node); + } + + int highestActiveLayer = -1; + for (int i = 0; i < bucketCount; i++) + { + if (layerNodes[i].Count > 0) + { + highestActiveLayer = i; + var entry = _layerMgr.GetOrCreateLayer(i); + RenderLayerNodes(entry, layerNodes[i], i, cfg); + } + else + { + var entry = _layerMgr.TryGetLayer(i); + entry?.PrimitiveRenderer?.ClearAll(); + // 空层也要回收残留的 TMP 文本(被视口裁剪掉的节点) + _layerMgr.PruneDeadTexts(i, new HashSet()); + } + } + + _layerMgr.PruneLayersAbove(highestActiveLayer); + } + + /// + /// 根据归一化缩放值计算 LOD 等级。 + /// normalizedZoom = (zoom - MinZoom) / (MaxZoom - MinZoom),将 zoom 映射到 [0, 1]。 + /// + private LodLevel GetLodLevel(QuickGraphNodeRenderConfig cfg) + { + float min = Graph.Canvas.MinZoom; + float max = Graph.Canvas.MaxZoom; + float range = max - min; + float normalized = range > 0.001f ? (Graph.Canvas.ZoomLevel - min) / range : 1f; + + if (normalized < cfg.Lod0Threshold) return LodLevel.Minimal; + if (normalized < cfg.Lod1Threshold) return LodLevel.Simplified; + return LodLevel.Full; + } + + private void RenderLayerNodes(LayerContainerEntry entry, List nodes, int layer, + QuickGraphNodeRenderConfig cfg) + { + if (entry.PrimitiveRenderer != null) + entry.PrimitiveRenderer.ClearAll(); + + var aliveNodes = new HashSet(nodes); + + float zoom = Graph.Canvas.ZoomLevel; + LodLevel lod = GetLodLevel(cfg); + + float nw = cfg.NodeWidth * zoom; + float nh = cfg.NodeHeight * zoom; + float border = (lod == LodLevel.Minimal) ? 0f : cfg.BorderThickness * zoom; + + foreach (var node in nodes) + { + node.NodeSize = new Vector2(nw, nh); + // 端口始终用未缩放尺寸计算(连线系统的坐标基准) + CalculatePortPositions(node, cfg.NodeWidth, cfg.NodeHeight); + + var localPos = Graph.Canvas.CanvasToLocal(node.Position); + + Color bgColor, borderColor; + if (lod == LodLevel.Minimal) + { + // LOD 0:纯色块,bg = 边框色, 无边框 + bgColor = node.NodeColor; + borderColor = node.NodeColor; + } + else + { + bgColor = node.NodeBGColor; + borderColor = node.NodeColor; + } + + var primEntry = new PrimitiveCacheEntry + { + type = PrimitiveType.Rectangle, + sizeMode = SizeMode.InscribedEllipse, + center = localPos, + size = new Vector2(nw, nh), + angle = 0f, + @params = new PrimitiveParams + { + axisScaleX = 1f, + axisScaleY = 1f, + sideCount = 4, + chamferSize = cfg.ChamferSize, + chamferSegments = cfg.ChamferSegments, + bgColor = bgColor, + centerColor = Color.white, + borderColor = borderColor, + borderThickness = border, + } + }; + entry.PrimitiveRenderer.AddPrimitive(primEntry); + + // LOD >= Full 时才显示节点文本 + if (lod == LodLevel.Full) + UpdateNodeText(node, layer, nw, nh, border, cfg); + else + HideNodeText(node, layer); + } + + entry.PrimitiveRenderer.SetPrimitiveDirty(0); + _layerMgr.PruneDeadTexts(layer, aliveNodes); + } + + private static void CalculatePortPositions(BlueprintNode node, float nw, float nh) + { + int inCount = node.InputPorts.Count; + int outCount = node.OutputPorts.Count; + + for (int i = 0; i < inCount; i++) + { + float y = nh * (0.5f - (float)(i + 1) / (inCount + 1)); + node.InputPorts[i].RelativePosition = new Vector2(-nw * 0.5f, y); + } + + for (int i = 0; i < outCount; i++) + { + float y = nh * (0.5f - (float)(i + 1) / (outCount + 1)); + node.OutputPorts[i].RelativePosition = new Vector2(nw * 0.5f, y); + } + } + + private void UpdateNodeText(BlueprintNode node, int layer, float nw, float nh, float border, + QuickGraphNodeRenderConfig cfg) + { + var tmp = _layerMgr.GetOrCreateNodeText(node, layer); + float zoom = Graph.Canvas.ZoomLevel; + + tmp.text = node.NodeTitle; + tmp.color = node.NodeTextColor; + tmp.fontSize = cfg.TitleFontSize * Mathf.Max(zoom, 0.5f); + if (cfg.TitleFont != null) + tmp.font = cfg.TitleFont; + + var rt = tmp.rectTransform; + rt.anchoredPosition = Graph.Canvas.CanvasToLocal(node.Position); + rt.sizeDelta = new Vector2(nw - border * 2f, nh - border * 2f); + + rt.SetAsLastSibling(); + tmp.gameObject.SetActive(true); + } + + /// 隐藏节点的 TMP 文本(不销毁,复用)。 + private void HideNodeText(BlueprintNode node, int layer) + { + var tmp = _layerMgr.TryGetNodeText(node, layer); + if (tmp != null) + tmp.gameObject.SetActive(false); + } + + /// + /// 将配置默认值写入节点,使渲染工具直接读取节点值。 + /// config 只控制背景色,边框色和文本色由节点初始化时定义。 + /// + private static void ApplyConfigToNode(BlueprintNode node, QuickGraphNodeRenderConfig cfg) + { + node.NodeBGColor = cfg.DefaultBackgroundColor; + } + + private void EnsureRenderRoot() + { + if (_renderRoot != null) return; + if (Graph.Canvas == null) return; + _renderRoot = Graph.Canvas.GetRootTransform(); + } + + public override void OnDestroy() + { + if (Graph != null) + LayerContainerManager.ReleaseForGraph(Graph); + _layerMgr = null; + _renderRoot = null; + } + } +} diff --git a/Runtime/Blueprint/Render/QuickUGUIGraphNodeRenderTool.cs.meta b/Runtime/Blueprint/Render/QuickUGUIGraphNodeRenderTool.cs.meta new file mode 100644 index 0000000..1b4768a --- /dev/null +++ b/Runtime/Blueprint/Render/QuickUGUIGraphNodeRenderTool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 552d9e16260f6704f8a3da72908af509 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Blueprint/Render/QuickUGUIGraphWireRenderTool.cs b/Runtime/Blueprint/Render/QuickUGUIGraphWireRenderTool.cs new file mode 100644 index 0000000..1ed8917 --- /dev/null +++ b/Runtime/Blueprint/Render/QuickUGUIGraphWireRenderTool.cs @@ -0,0 +1,181 @@ +using System.Collections.Generic; +using UnityEngine; +using XericLibrary.Runtime.UIGraph; + +namespace XericLibrary.Runtime.Blueprint.Render +{ + /// + /// 快速 UGUI 图论连线渲染工具。 + /// 通过共享的 LayerContainerManager 使用与节点相同的层容器, + /// 连线绘制在两端节点所在层的较低层中(不遮挡节点)。 + /// 可调参数由 QuickGraphWireRenderConfig 配置资产提供。 + /// + [BlueprintTool(phase: ToolPhase.Render, order: 110)] + [BlueprintTheme("QuickGraph")] + public class QuickUGUIGraphWireRenderTool : BlueprintTool + { + private QuickGraphWireRenderConfig _defaultConfig; + + private QuickGraphWireRenderConfig Config + { + get + { + if (ToolConfig is QuickGraphWireRenderConfig external) + return external; + if (_defaultConfig == null) + _defaultConfig = ScriptableObject.CreateInstance(); + return _defaultConfig; + } + } + + private Transform _renderRoot; + private LayerContainerManager _layerMgr; + + public override void OnRender() + { + if (Graph == null) return; + EnsureRenderRoot(); + if (_renderRoot == null) return; + + var buckets = Graph.RenderLayers; + if (buckets == null) return; + + _layerMgr = LayerContainerManager.GetForGraph(Graph, _renderRoot); + + var cfg = Config; + int bucketCount = buckets.BucketCount; + + // 视口裁剪 + var viewport = Graph.Canvas.GetViewportRect(); + float margin = cfg.WireWidth * 20f; + viewport.xMin -= margin; + viewport.xMax += margin; + viewport.yMin -= margin; + viewport.yMax += margin; + + var layerWires = new List[bucketCount]; + for (int i = 0; i < bucketCount; i++) + layerWires[i] = new List(); + + foreach (var wire in Graph.Wires) + { + int srcLayer = wire.SourcePort?.OwnerNode?.RenderLayer ?? 0; + int tgtLayer = wire.TargetPort?.OwnerNode?.RenderLayer ?? 0; + + int wireLayer = Mathf.Min(srcLayer, tgtLayer); + if (wireLayer < 0 || wireLayer >= bucketCount) wireLayer = 0; + + // 两端都不在视口内则跳过 + if (wire.SourcePort?.OwnerNode != null && wire.TargetPort?.OwnerNode != null) + { + if (!viewport.Contains(wire.SourcePort.OwnerNode.Position) && + !viewport.Contains(wire.TargetPort.OwnerNode.Position)) + continue; + } + + layerWires[wireLayer].Add(wire); + } + + for (int i = 0; i < bucketCount; i++) + { + if (layerWires[i].Count > 0) + { + var entry = _layerMgr.GetOrCreateLayer(i); + RenderLayerWires(entry, layerWires[i], cfg, Graph.Canvas); + } + else + { + var entry = _layerMgr.TryGetLayer(i); + entry?.CurveRenderer?.ClearAll(); + } + } + } + + private static void RenderLayerWires(LayerContainerEntry entry, List wires, + QuickGraphWireRenderConfig cfg, IBlueprintCanvas canvas) + { + entry.CurveRenderer.ClearAll(); + + float zoom = canvas.ZoomLevel; + + foreach (var wire in wires) + { + if (wire.SourcePort == null || wire.TargetPort == null) continue; + if (wire.SourcePort.OwnerNode == null || wire.TargetPort.OwnerNode == null) continue; + + Vector2 startPos = canvas.CanvasToLocal(wire.SourcePort.GetWorldPosition()); + Vector2 endPos = canvas.CanvasToLocal(wire.TargetPort.GetWorldPosition()); + + // 手柄方向 = 端口在节点上的朝向 + // 端口在节点右侧 (RelativePosition.x > 0) → 手柄朝右 (+X) + // 端口在节点左侧 (RelativePosition.x < 0) → 手柄朝左 (-X) + float srcDirX = Mathf.Sign(wire.SourcePort.RelativePosition.x); + float tgtDirX = Mathf.Sign(wire.TargetPort.RelativePosition.x); + // 同一侧(如两个端口都在右侧)则方向取反 + if (srcDirX == tgtDirX) srcDirX *= -1f; + + // 手柄距离 = 配置值 × zoom(画布单位 → local 坐标,与 CanvasToLocal 一致) + Vector2 handle1 = startPos + new Vector2(srcDirX * cfg.SourceHandleLength * zoom, 0f); + Vector2 handle2 = endPos + new Vector2(tgtDirX * cfg.TargetHandleLength * zoom, 0f); + + float ww = cfg.WireWidth * zoom; + + var ctrlPts = new Vector3[] + { + new Vector3(startPos.x, startPos.y, ww), + new Vector3(handle1.x, handle1.y, ww), + new Vector3(handle2.x, handle2.y, ww), + new Vector3(endPos.x, endPos.y, ww), + }; + + Color32 srcColor = wire.SourcePort.OwnerNode.NodeColor; + Color32 tgtColor = wire.TargetPort.OwnerNode.NodeColor; + + var arrow = new ArrowHeadData + { + shape = cfg.ArrowShape, + reversed = cfg.ArrowReversed, + progress = cfg.ArrowProgress, + width = cfg.ArrowWidth * zoom, + height = cfg.ArrowHeight * zoom, + depthCompensation = cfg.ArrowDepthCompensation, + color = tgtColor, + }; + + int count = ctrlPts.Length; + var ctrlPts2 = new Vector2[count]; + var widths = new float[count]; + for (int i = 0; i < count; i++) + { + ctrlPts2[i] = ctrlPts[i]; + widths[i] = ctrlPts[i].z; + } + + var curveEntry = new CurveCacheEntry + { + startColor = srcColor, + endColor = tgtColor, + tessellationSegments = cfg.TessellationSegments, + }; + + entry.CurveRenderer.AddCurve(curveEntry, ctrlPts2, widths, new[] { arrow }); + } + + if (wires.Count > 0) + entry.CurveRenderer.RebuildAll(); + } + + private void EnsureRenderRoot() + { + if (_renderRoot != null) return; + if (Graph.Canvas == null) return; + _renderRoot = Graph.Canvas.GetRootTransform(); + } + + public override void OnDestroy() + { + _layerMgr = null; + _renderRoot = null; + } + } +} diff --git a/Runtime/Blueprint/Render/QuickUGUIGraphWireRenderTool.cs.meta b/Runtime/Blueprint/Render/QuickUGUIGraphWireRenderTool.cs.meta new file mode 100644 index 0000000..504d661 --- /dev/null +++ b/Runtime/Blueprint/Render/QuickUGUIGraphWireRenderTool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b98af5cb1bdbbcf4d8b60e620f6b2fdb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Blueprint/Utility.meta b/Runtime/Blueprint/Utility.meta new file mode 100644 index 0000000..0e7857d --- /dev/null +++ b/Runtime/Blueprint/Utility.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 708403450e119784988882f777746911 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Lrss3.Deconstruction.asmdef b/Runtime/Lrss3.Deconstruction.asmdef index 225fd46..acf6913 100644 --- a/Runtime/Lrss3.Deconstruction.asmdef +++ b/Runtime/Lrss3.Deconstruction.asmdef @@ -2,7 +2,8 @@ "name": "Lrss3.Deconstruction", "rootNamespace": "Deconstruction", "references": [ - "Unity.TextMeshPro" + "Unity.TextMeshPro", + "Unity.InputSystem" ], "includePlatforms": [], "excludePlatforms": [], diff --git a/Runtime/XericLibrary.dll b/Runtime/XericLibrary.dll index d11e14b..0687eb6 100644 Binary files a/Runtime/XericLibrary.dll and b/Runtime/XericLibrary.dll differ diff --git a/package.json b/package.json index ba717cc..707d2b4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "com.lrss3.deconstruction", "displayName": "Xeric Library", - "version": "0.6.1", + "version": "0.6.2", "unity": "2021.3", "description": "\u6b22\u8fce\u4f7f\u7528Xeric Library, \u8fd9\u662f\u4e00\u4e2a\u4e13\u6ce8\u4ee3\u7801\u7684\u6269\u5c55\u5e93 \r\n\r\n * \u4e13\u6ce8Unity\u6210\u5458\u8bed\u6cd5, \u6269\u5c55\u591a\u79cd\u57fa\u672c\u7c7b\u578b\u7684\u6570\u636e\u7ed3\u6784\u7684Linq\u8bed\u6cd5/\u8bed\u6cd5\u7cd6, \u63d0\u4f9b\uff1a \u51fd\u6570\u5e73\u6ed1\u3001 \u591a\u9879\u5f0f\u8ba1\u7b97\u3001 \u6743\u91cd\u62df\u5408\u3001 \u8fc7\u7a0b\u5206\u7ea7\u3001 \u5de5\u4e1a\u63a7\u5236\u3001 \u66f2\u7ebf\u7ed8\u5236\u3001 \u8def\u5f84\u8bbe\u7f6e\u3001 \u5bf9\u8c61\u63a7\u5236\u3001 \u8fed\u4ee3\u6269\u5c55\u3001 \u7a7a\u95f4\u53d8\u6362\u3001 \u72ec\u7279\u7ed3\u6784\u3001 \u6587\u672c\u683c\u5f0f\u3001 \u7c7b\u578b\u8f6c\u6362\u3001 \u673a\u5668\u7f16\u7801\u3001 \u5f00\u53d1\u8c03\u8bd5\u3001 \u52a8\u6001\u751f\u6210\u3001 \u51e0\u4f55\u521b\u5efa\u3001 \u5feb\u901f\u6c60\u5316\u3001 \u5bfc\u822a\u5bfb\u8def\u3001 \u9694\u79bb\u63a7\u5236\u3001 \u9694\u79bb\u8f93\u5165\u3001 \u8bed\u4e49\u5316\u59d4\u6258\u3001 \u53cd\u5c04\u8d85\u9a70\u3001 \u6570\u5b66\u5e38\u6570\u3001 \u5355\u4f4d\u6362\u7b97\u3001 \u6392\u5e8f\u7b97\u6cd5\u3001 \u7a0b\u5e8f\u8c03\u7528\u3001 \u7f51\u7edc\u8fde\u63a5\u7b49\u5feb\u6377\u7528\u6cd5\u3002 \r\n\r\n * \u6269\u5c55\u7279\u6b8a\u7c7b\u578b, \u5305\u62ec\u4e14\u4e0d\u9650\u4e8e: \u591a\u7ef4\u5e03\u5c14\u3001 \u6837\u6761\u66f2\u7ebf\u3001 \u8d85\u7ea7\u5355\u4f8b\u3001 \u591a\u4f8b\u7cfb\u7edf\u3001 \u90bb\u5c45\u7f51\u7edc\u3001 \u56db\u53c9\u6811\u3001 \u5b57\u5178\u6811\u3001 \u8f6f\u5f15\u7528\u5c01\u88c5\u5668\u7b49\u3002 \r\n\r\n * \u6269\u5c55\u5404\u79cd\u5e38\u7528\u811a\u672c: \u6e38\u620f\u4ea4\u4e92\u3001 \u754c\u9762\u9002\u914d\u3001 \u5f31\u6269\u5c55\u3001 \u7ed8\u5236\u5de5\u5382\u3001 SQL\u6269\u5c55\u3002 \r\n\r\n * \u517c\u5bb9\u4e0d\u89c4\u8303\u7a0b\u5e8f: \u9488\u5bf9\u4e0d\u89c4\u8303\u6216\u6beb\u65e0\u8bbe\u8ba1\u89c4\u5219\u53ef\u8a00\u7684\u4ee3\u7801, \u63d0\u4f9b\u4e86\u66f4\u591a\u57fa\u4e8e\u53cd\u5c04, CIL\u7279\u6027\u7b49\u5e95\u5c42\u8bed\u6cd5\u7684\u7a0b\u5e8f\u6269\u5c55, \u4ee5\u4fbf\u5feb\u901f\u5b9e\u73b0\u76ee\u7684: \r\n1. \u811a\u672c\u6ca1\u6709\u7ee7\u627f\u5355\u4f8b? \"\u8d85\u7ea7\u5355\u4f8b\"\u4e0d\u9700\u8981\u58f0\u540d, \u4e5f\u4e0d\u9700\u8981\u4fee\u6539\u4efb\u4f55\u539f\u6765\u7684\u811a\u672c\u5185\u5bb9, \u5f84\u76f4\u8c03\u7528\u5c31\u662f\u5355\u4f8b\u3002\r\n2. \u6570\u636e\u7ed3\u6784\u5b57\u6bb5\u5197\u4f59\u91cd\u590d? \"\u8f6f\u63a5\u53e3\" (SoftInterface) \u652f\u6301\u5feb\u901f\u6267\u884c\u53cd\u5c04\u83b7\u53d6\u548c\u8bbe\u7f6e\uff0c\u65b9\u6cd5\u59d4\u6258\uff0c\u5b57\u6bb5\u5c5e\u6027\u4e00\u884c\u8c03\u7528\u3002 \r\n3. untiy\u5bf9\u8c61\u8981\u9075\u5faa\u751f\u547d\u5468\u671f\u6c60\u5316\u592a\u9ebb\u70e6? \"\u8054\u5408\u5bf9\u8c61\u6c60\" (MacroPool.UnionSet) \u76f4\u63a5\u5c06\u8bbe\u5b9a\u9879\u76ee\u5f00\u653e\u5230inspector\u4e0a\u914d\u7f6e\uff0c\u4ee3\u7801\u4e2d\u53ea\u9700\u8981get\u548crelease\u5c31\u53ef\u4ee5! \r\n4. \u7a0b\u5e8fUI\u6846\u67b6\u548c\u903b\u8f91\u6846\u67b6\u9ad8\u5ea6\u8026\u5408\u65e0\u4ece\u4e0b\u624b? \u6709\u70b9\u9ebb\u70e6, \u4e0d\u8fc7\u83dc\u5355\u7279\u6027, \u67e5\u627e\u7279\u6027, \u547d\u540d\u6807\u8bb0\u7279\u6027\u53ef\u4ee5\u5e2e\u52a9\u4f60\u65e0\u89c6\u5185\u5bb9\u67e5\u627e\u5b57\u6bb5\u5c5e\u6027\u65b9\u6cd5\u7c7b\u578b\u7b49\u5185\u5bb9, \u4f7f\u7528 `XericUIActionVessel` \u63d2\u4ef6\u66f4\u662f\u5141\u8bb8\u76f4\u63a5\u5c06\u7c7b\u4f20\u5165\u5c31\u80fd\u7a0b\u5e8f\u5316\u751f\u6210\u83dc\u5355\u754c\u9762\u3002 \r\n5. \u9a71\u52a8ui\u7684\u6570\u636e\u91cf\u592a\u5927? \u63d0\u4f9b\u591a\u79cd\u57fa\u4e8eUI\u6846\u67b6\u7684\u6570\u636e\u7ed3\u6784\u865a\u62df\u5316\u6280\u672f, \u4f7f\u7528\u865a\u62df\u5316\u62c6\u5206\u6570\u636e\u53ef\u4ee5\u6bcf\u6b21\u5237\u65b0\u7684\u538b\u529b\u3002 \u4f7f\u7528\u6b64\u529f\u80fd\u9700\u8981\u5b89\u88c5XericUIActionVessel\u63d2\u4ef6, \u4ee3\u7801\u4ee5\u811a\u672c\u5448\u73b0\uff0c\u76f4\u63a5\u6d4f\u89c8\uff0c\u7f16\u5199\u65f6\u9075\u5faa\u7cbe\u7ec6\u7684\u5de5\u5382\u6743\u80fd\u5212\u5206\uff01 \r\n6. \u559c\u6b22\u51fd\u6570\u5f0f\u7f16\u7a0b\u548c\u4fbf\u6377\u7684\u8bed\u6cd5\u7cd6? \u63d2\u4ef6\u63d0\u4f9b\u591a\u79cd\u5de5\u5177\u7c7b: \u679a\u4e3e\u53ef\u4ee5\u4f7f\u7528`MacroEnum`\u5206\u7c7b\u548c`MacroEnum`\u6269\u5c55; \u6570\u5b66, \u5411\u91cf, \u51e0\u4f55, \u6570\u636e\u7ed3\u6784\u6269\u5c55, \u5e38\u89c1\u7b97\u6cd5, \u5e38\u7528\u5b57\u7b26\u8ba1\u7b97\u5df2\u7ecf\u5168\u90e8\u5185\u5d4c`MacroMath`\u5206\u7c7b; \r\n7. \u9700\u8981\u4f7f\u7528\u9ad8\u7ea7\u6570\u636e\u7ed3\u6784? \u5927\u9876\u5806, \u56db\u53c9\u6811, \u516b\u53c9\u6811, \u5b57\u5178\u6811, \u65f6\u95f4\u6233, \u591a\u6bb5\u7ebf, \u8d1d\u585e\u5c14, \u591a\u7ef4\u5e03\u5c14(\u6bd4\u7279\u77e2\u91cf), \u6bd4\u7279\u56fe, \u53cc\u751f\u54c8\u5e0c\u8868, \u76f8\u90bb\u7f51\u683c\u7b49\u5185\u5bb9\u53ef\u4ee5\u76f4\u63a5\u4f7f\u7528\u3002 \r\n8. \u60f3\u8981\u7acb\u523b\u80fd\u591f\u5728\u573a\u666f\u4e2d\u79fb\u52a8\u89d2\u8272? \u7b2c\u4e00\u4eba\u79f0, \u7b2c\u4e09\u4eba\u79f0, \u4e0a\u5e1d\u89c6\u89d2\u5df2\u7ecf\u51c6\u5907\u5c31\u7eea! \r\n9. \u538c\u70e6\u590d\u6742\u7684\u6309\u952e\u68c0\u67e5\u903b\u8f91? \u6309\u952e\u5b8f (MacroKey) \u63d0\u4f9b\u72b6\u6001\u8868\u5904\u7406\u66f4\u591a\u6309\u952e, \u53ef\u4ee5\u8bc6\u522b\u77ed\u6309, \u957f\u6309, \u53cc\u51fb, \u8fde\u51fb, \u62d6\u62fd, \u4ee5\u53ca\u5b83\u4eec\u7684\u6309\u4e0b\u548c\u91ca\u653e\u72b6\u6001! \u4e14\u53ef\u4ee5\u9009\u62e9\u4ee5\u65f6\u95f4, \u5750\u6807\u7b49\u5171\u8ba14\u79cd\u6a21\u5f0f\u89e6\u53d1, \u652f\u6301\u65b0\u8f93\u5165\u7cfb\u7edf, \u6ee1\u8db3\u7edd\u5927\u90e8\u5206\u573a\u666f\u7684\u7ec6\u5206\u9700\u6c42\u3002 \r\n10. \u9700\u8981\u9690\u85cf\u65e5\u5fd7\u8c03\u7528\u6808\u4fe1\u606f? XericLogger \u53ef\u4ee5\u51cf\u5c11\u65e5\u5fd7\u8f93\u51fa\u5185\u5bb9\u3002 \r\n11. \u9700\u8981\u66f4\u591a\u8c03\u8bd5\u4fe1\u606f? \u63d0\u4f9b\u53ef\u4ee5\u548c\u865a\u5e7b\u539f\u751f\u5ab2\u7f8e\u7684Gizmos\u8c03\u8bd5\u663e\u793a\u529f\u80fd\u3002 \r\n11. \u62c5\u5fc3\u517c\u5bb9\u6027\u5417? \u7a0b\u5e8f\u4e3b\u8981\u57fa\u4e8e2022\u7248\u672c\u5f00\u53d1, \u5411\u4e0b\u517c\u5bb92021, \u5411\u4e0a\u652f\u63016000, \u4e0d\u6d89\u53ca\u6e32\u67d3\u7ba1\u7ebfAPI, \u6709\u9488\u5bf9webgl\u7248\u672c\u7684\u5355\u72ec\u6784\u5efa\u7248\u672c, \u4e14\u7ecf\u8fc7\u9879\u76ee\u5b9e\u6218\u9a8c\u8bc1\u3002 \r\n\r\n\u66f4\u591a\u7f16\u8f91\u5668\u529f\u80fd\u53c2\u8003: \r\n * \u5b89\u88c5Xeric Blueprint Graph\u4ee5\u83b7\u53d6Unity\u4e0a\u84dd\u56fe\u8282\u70b9\u529f\u80fd, \u5e76\u652f\u6301Xeric\u51fd\u6570\u5e93\u529f\u80fd\u3002\r\n * \u5b89\u88c5Xeric UI Graph\u4ee5\u83b7\u53d6\u84dd\u56fe\u5316UI\u751f\u6210\u529f\u80fd, \u4f7f\u7528\u84dd\u56fe\u903b\u8f91\u63a7\u5236UI\u4ea4\u4e92, \u751f\u6210\u903b\u8f91, \u5e76\u652f\u6301Xeric\u51fd\u6570\u5e93\u529f\u80fd\u3002\r\n * \u5b89\u88c5Digital Twin Tool\u4ee5\u83b7\u53d6\u5e38\u7528\u8c03\u8bd5\u5de5\u5177\u96c6\u3002\r\n * \u5b89\u88c5Nexus Frame Flow\u4ee5\u83b7\u53d6\u5de5\u4f5c\u67b6\u6784\u6d41\u5904\u7406\u529f\u80fd\u3002\r\n * \u5b89\u88c5Xeric Editor\u8f85\u52a9\u5f15\u64ce\u5927\u7eb2\u7f8e\u5316\u7ec4\u4ef6\u3001 \u8d44\u6e90\u5783\u573e\u5904\u7406\u7ec4\u4ef6\u3001 \u8d44\u6e90\u7f13\u5b58\u7ec4\u4ef6\u3001 \u5feb\u901f\u622a\u56fe\u529f\u80fd, \u4ee5\u53ca\u66f4\u591a\u5feb\u6377\u952e\u529f\u80fd", "keywords": [