udpate 0.6.2
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace XericLibrary.Runtime.Blueprint
|
||||
{
|
||||
/// <summary>
|
||||
/// 蓝图框选工具 —— 鼠标左键在空白区域拖拽,绘制矩形选框,
|
||||
/// 释放时计算框选范围内所有元素的交集并输出统计日志。
|
||||
/// <para>选框 UGUI Image 默认渲染在所有层之上(SetAsLastSibling)。</para>
|
||||
/// <para>框选结果保存在 <see cref="SelectedElements"/> 中供其他工具读取。</para>
|
||||
/// </summary>
|
||||
[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;
|
||||
|
||||
// ---------- 结果 ----------
|
||||
|
||||
/// <summary>本次框选选中的元素列表。</summary>
|
||||
public List<IBlueprintElement> SelectedElements { get; } = new List<IBlueprintElement>();
|
||||
|
||||
// ===== 鼠标按下 =====
|
||||
|
||||
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<RectTransform>();
|
||||
_selectionBoxRt.SetParent(root, false);
|
||||
_selectionBoxRt.SetAsLastSibling(); // 渲染在最顶层
|
||||
|
||||
_selectionBoxImage = _selectionBoxGo.GetComponent<Image>();
|
||||
_selectionBoxImage.color = new Color(0.2f, 0.5f, 1.0f, 0.15f); // 半透明蓝
|
||||
// 边框通过 Outline 或额外 Image 实现,直接使用透明填充 + 轮廓不好做,
|
||||
// 使用两个 Image:填充(当前) + 边框(另一个,1px 白色)
|
||||
// 为简化,在填充 Image 上挂一个 Outline 组件模拟边框
|
||||
var outline = _selectionBoxGo.AddComponent<Outline>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e9d5cdc3f1b8c9840b274a6db73a9fb1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,47 @@
|
||||
namespace XericLibrary.Runtime.Blueprint
|
||||
{
|
||||
/// <summary>
|
||||
/// 蓝图输入系统的 Action Map / Action 名称常量。
|
||||
/// 供 Editor 的 InputAction 模板和 Runtime 的输入处理工具共同引用,
|
||||
/// 避免字符串硬编码散布各处。
|
||||
/// </summary>
|
||||
public static class BlueprintInputConstants
|
||||
{
|
||||
// ── Action Map ──
|
||||
public const string MapName = "Blueprint";
|
||||
|
||||
// ── 鼠标 / 指针 Actions(名称须与 UnityEngine.InputSystem.UI.InputSystemUIInputModule 官方预设一致)──
|
||||
/// <summary>画布内指针位置(Vector2)。InputSystemUIInputModule 预设名称。</summary>
|
||||
public const string Point = "Point";
|
||||
/// <summary>滚轮滚动(Vector2)。InputSystemUIInputModule 预设名称。</summary>
|
||||
public const string ScrollWheel = "ScrollWheel";
|
||||
/// <summary>左键点击(Button)。InputSystemUIInputModule 预设名称。</summary>
|
||||
public const string LeftClick = "LeftClick";
|
||||
/// <summary>右键点击(Button)。InputSystemUIInputModule 预设名称。</summary>
|
||||
public const string RightClick = "RightClick";
|
||||
/// <summary>中键点击(Button)。InputSystemUIInputModule 预设名称。</summary>
|
||||
public const string MiddleClick = "MiddleClick";
|
||||
|
||||
// ── 功能键 Actions ──
|
||||
/// <summary>左 Shift(Button)</summary>
|
||||
public const string Shift = "Shift";
|
||||
/// <summary>左 Ctrl(Button)</summary>
|
||||
public const string Ctrl = "Ctrl";
|
||||
/// <summary>左 Alt(Button)</summary>
|
||||
public const string Alt = "Alt";
|
||||
|
||||
// ── 快捷键组合 Actions(仅新输入系统)──
|
||||
/// <summary>平移画布(Vector2,4方向)</summary>
|
||||
public const string Pan = "Pan";
|
||||
/// <summary>缩放画布(float,滚轮 + Ctrl 组合)</summary>
|
||||
public const string Zoom = "Zoom";
|
||||
/// <summary>定位到核心节点</summary>
|
||||
public const string FocusHome = "FocusHome";
|
||||
/// <summary>撤销</summary>
|
||||
public const string Undo = "Undo";
|
||||
/// <summary>重做</summary>
|
||||
public const string Redo = "Redo";
|
||||
/// <summary>进入父级</summary>
|
||||
public const string NavigateParent = "NavigateParent";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e718c439f4fa3d449427433ed729467
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,188 @@
|
||||
#if ENABLE_INPUT_SYSTEM
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
namespace XericLibrary.Runtime.Blueprint
|
||||
{
|
||||
/// <summary>
|
||||
/// 蓝图输入管理器(新输入系统)—— 管理 InputActionAsset 的引用计数与启用/停用。
|
||||
/// <para>
|
||||
/// 多个蓝图可能引用同一个 InputActionAsset,管理器确保:
|
||||
/// - 首个蓝图注册时启用 asset 并绑定 Action 回调;
|
||||
/// - 后续蓝图注册仅增加计数,不重复启用;
|
||||
/// - 蓝图注销时减少计数,计数归零时才解绑并禁用 asset。
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// 蓝图组件在 Awake 时调用 <see cref="RegisterAsset"/>,OnDestroy 时调用
|
||||
/// <see cref="UnregisterAsset"/>;运行时动态更换 asset 也通过这两方法。
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public static class BlueprintInputManager
|
||||
{
|
||||
/// <summary>asset → 引用计数</summary>
|
||||
private static Dictionary<InputActionAsset, int> _refCounts
|
||||
= new Dictionary<InputActionAsset, int>();
|
||||
|
||||
/// <summary>asset → 已绑定回调的 ActionMap</summary>
|
||||
private static Dictionary<InputActionAsset, InputActionMap> _activeMaps
|
||||
= new Dictionary<InputActionAsset, InputActionMap>();
|
||||
|
||||
// ===== 快捷键最新值(供 QuickGraphInputTool.OnUpdate 轮询) =====
|
||||
|
||||
/// <summary>Pan 动作最新的 Vector2 方向值(在 performed 中写入,canceled 中归零)。</summary>
|
||||
public static Vector2 LastPanDirection;
|
||||
|
||||
/// <summary>Zoom 动作最新的 float 轴值(在 performed 中写入,canceled 中归零)。</summary>
|
||||
public static float LastZoomAxis;
|
||||
|
||||
// 缓存的回调引用(确保 Bind/Unbind 使用同一委托实例)
|
||||
private static readonly System.Action<InputAction.CallbackContext> s_panHandler
|
||||
= ctx => LastPanDirection = ctx.ReadValue<Vector2>();
|
||||
private static readonly System.Action<InputAction.CallbackContext> s_panCanceled
|
||||
= _ => LastPanDirection = Vector2.zero;
|
||||
private static readonly System.Action<InputAction.CallbackContext> s_zoomHandler
|
||||
= ctx => LastZoomAxis = ctx.ReadValue<float>();
|
||||
private static readonly System.Action<InputAction.CallbackContext> s_zoomCanceled
|
||||
= _ => LastZoomAxis = 0f;
|
||||
private static readonly System.Action<InputAction.CallbackContext> s_homeHandler
|
||||
= _ => OnActionReceived(BlueprintInputConstants.FocusHome);
|
||||
private static readonly System.Action<InputAction.CallbackContext> s_undoHandler
|
||||
= _ => OnActionReceived(BlueprintInputConstants.Undo);
|
||||
private static readonly System.Action<InputAction.CallbackContext> s_redoHandler
|
||||
= _ => OnActionReceived(BlueprintInputConstants.Redo);
|
||||
private static readonly System.Action<InputAction.CallbackContext> s_parentHandler
|
||||
= _ => OnActionReceived(BlueprintInputConstants.NavigateParent);
|
||||
|
||||
// ===== 当前焦点蓝图(快捷键分发目标) =====
|
||||
|
||||
private static BlueprintGraph s_focusedGraph;
|
||||
|
||||
/// <summary>设置当前焦点蓝图(快捷键分发目标)。</summary>
|
||||
public static void SetFocusedGraph(BlueprintGraph graph)
|
||||
{
|
||||
s_focusedGraph = graph;
|
||||
}
|
||||
|
||||
// ===== 注册 / 注销 =====
|
||||
|
||||
/// <summary>
|
||||
/// 注册一个蓝图对 InputActionAsset 的引用。
|
||||
/// 若 asset 尚未启用,则启用并绑定快捷键回调。
|
||||
/// 允许多个蓝图共享同一 asset。
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注销一个蓝图对 InputActionAsset 的引用。
|
||||
/// 引用计数归零时解绑回调并禁用 asset。
|
||||
/// </summary>
|
||||
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<InputAction.CallbackContext> handler)
|
||||
{
|
||||
var action = map.FindAction(name);
|
||||
if (action != null)
|
||||
action.performed += handler;
|
||||
}
|
||||
|
||||
private static void Unbind(InputActionMap map, string name, System.Action<InputAction.CallbackContext> handler)
|
||||
{
|
||||
var action = map.FindAction(name);
|
||||
if (action != null)
|
||||
action.performed -= handler;
|
||||
}
|
||||
|
||||
private static void BindCancelled(InputActionMap map, string name, System.Action<InputAction.CallbackContext> handler)
|
||||
{
|
||||
var action = map.FindAction(name);
|
||||
if (action != null)
|
||||
action.canceled += handler;
|
||||
}
|
||||
|
||||
private static void UnbindCancelled(InputActionMap map, string name, System.Action<InputAction.CallbackContext> 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
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 72f3c53c414fbef4b80270adb10afe1d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,135 @@
|
||||
#if !ENABLE_INPUT_SYSTEM
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace XericLibrary.Runtime.Blueprint
|
||||
{
|
||||
/// <summary>
|
||||
/// 蓝图输入管理器(旧输入系统)—— 通过旧输入系统轮询鼠标和键盘事件。
|
||||
/// <para>
|
||||
/// 提供与 <c>BlueprintInputManager.InputSystem.cs</c> 相同的静态 API 签名,
|
||||
/// 但底层使用 <see cref="Input.GetMouseButton"/>/<see cref="Input.GetKey"/> 等旧 API,
|
||||
/// 不依赖任何新输入系统类型。
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// 鼠标点击、拖拽、右键等事件通过 <see cref="PollMouseEvents"/> 轮询后
|
||||
/// 分发到 <see cref="BlueprintToolProvider"/> 的 DispatchXxx 方法。
|
||||
/// 键盘 Pan/Zoom 值通过 <see cref="PollKeyboard"/> 轮询后由
|
||||
/// <see cref="QuickGraphInputTool"/> 在 OnUpdate 中读取。
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public static class BlueprintInputManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 当前焦点蓝图(快捷键分发目标)。
|
||||
/// 由 <see cref="SetFocusedGraph"/> 设置。
|
||||
/// </summary>
|
||||
private static BlueprintGraph s_focusedGraph;
|
||||
|
||||
/// <summary>设置当前焦点蓝图。</summary>
|
||||
public static void SetFocusedGraph(BlueprintGraph graph)
|
||||
{
|
||||
s_focusedGraph = graph;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注册 InputActionAsset(旧系统空操作)。
|
||||
/// 仅保留签名以兼容编译。
|
||||
/// </summary>
|
||||
public static void RegisterAsset(Object asset)
|
||||
{
|
||||
// 旧输入系统:不管理 InputActionAsset
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注销 InputActionAsset(旧系统空操作)。
|
||||
/// 仅保留签名以兼容编译。
|
||||
/// </summary>
|
||||
public static void UnregisterAsset(Object asset)
|
||||
{
|
||||
// 旧输入系统:不管理 InputActionAsset
|
||||
}
|
||||
|
||||
// ===== 键盘轮询值(供 QuickGraphInputTool.OnUpdate 读取) =====
|
||||
|
||||
/// <summary>
|
||||
/// 方向键 Pan 方向值(每帧由 <see cref="PollKeyboard"/> 更新)。
|
||||
/// </summary>
|
||||
public static Vector2 LastPanDirection;
|
||||
|
||||
/// <summary>
|
||||
/// +/- 键 Zoom 轴值(每帧由 <see cref="PollKeyboard"/> 更新)。
|
||||
/// </summary>
|
||||
public static float LastZoomAxis;
|
||||
|
||||
// ===== 事件轮询 =====
|
||||
|
||||
/// <summary>
|
||||
/// 轮询鼠标按键事件。
|
||||
/// 每帧在 Update 中调用,检测鼠标按下/释放并分发到 <see cref="BlueprintToolProvider"/>。
|
||||
/// </summary>
|
||||
/// <param name="mouseButton">0=左键, 1=右键, 2=中键</param>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 轮询鼠标滚轮事件。
|
||||
/// 每帧在 Update 中调用,检测滚轮增量并分发到 <see cref="BlueprintToolProvider"/>。
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 轮询键盘快捷键(方向键 Pan、+/- Zoom)。
|
||||
/// 每帧在 Update 中调用,更新 <see cref="LastPanDirection"/> 和 <see cref="LastZoomAxis"/>。
|
||||
/// </summary>
|
||||
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
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1f94468ab3403984494aa0d980297093
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,110 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace XericLibrary.Runtime.Blueprint
|
||||
{
|
||||
/// <summary>
|
||||
/// 蓝图输入接收工具 —— 继承自 <see cref="BlueprintTool"/>,由 BlueprintToolProvider 统一管理生命周期。
|
||||
/// <para>
|
||||
/// 职责:
|
||||
/// 1. 创建并驱动 <see cref="BlueprintUGUIInputManager"/>,绑定 UGUI EventTrigger 到背景 Image;
|
||||
/// 2. 在 <see cref="OnUpdate"/> 中统一刷新鼠标位置、检测指针进入/离开背景区域;
|
||||
/// 3. 将画布内的指针移动事件分发给 <see cref="BlueprintToolProvider"/>。
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// 鼠标按键按下/释放/拖拽/滚轮等由 <see cref="BlueprintUGUIInputManager"/> 通过 EventTrigger 回调直接分发。
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
|
||||
// ===== 辅助 =====
|
||||
|
||||
/// <summary>检查屏幕坐标是否落在背景 RectTransform 内。</summary>
|
||||
private bool IsScreenPointOverBackground(Vector2 screenPoint)
|
||||
{
|
||||
var rt = _uguiManager?.BgRectTransform;
|
||||
if (rt == null) return false;
|
||||
return RectTransformUtility.RectangleContainsScreenPoint(rt, screenPoint, null);
|
||||
}
|
||||
|
||||
/// <summary>屏幕坐标 → 画布坐标。</summary>
|
||||
private Vector2 ScreenToCanvas(Vector2 screenPos)
|
||||
{
|
||||
return Graph?.Canvas != null ? Graph.Canvas.ScreenToCanvas(screenPos) : screenPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7e1d0771b4cc2aa4692eff637fa5d5af
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,239 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace XericLibrary.Runtime.Blueprint
|
||||
{
|
||||
/// <summary>
|
||||
/// 蓝图 UGUI 输入管理器 —— 负责绑定 UGUI EventTrigger 到背景 Image,
|
||||
/// 将 UGUI 指针事件转发到 <see cref="BlueprintToolProvider"/> 的分发方法。
|
||||
/// <para>
|
||||
/// 该类仅处理与 UGUI EventTrigger 相关的绑定逻辑,不感知新旧输入系统的差异。
|
||||
/// 由 <see cref="BlueprintInputReceiver"/> 创建并在 OnUpdate 中驱动。
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public class BlueprintUGUIInputManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 当前帧鼠标/指针在屏幕空间的位置。
|
||||
/// 每帧由 <see cref="UpdateMousePosition"/> 刷新。
|
||||
/// </summary>
|
||||
public static Vector2 MousePosition { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前帧鼠标/指针是否处于新输入系统模式。
|
||||
/// <c>true</c> = 新输入系统(<c>ENABLE_INPUT_SYSTEM</c> 已定义);
|
||||
/// <c>false</c> = 旧输入系统。
|
||||
/// </summary>
|
||||
public static bool IsNewInputSystem
|
||||
{
|
||||
get
|
||||
{
|
||||
#if ENABLE_INPUT_SYSTEM
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新静态鼠标位置。每帧由 BlueprintInputReceiver 的 OnUpdate 调用。
|
||||
/// 内部根据条件编译选择鼠标位置读取源。
|
||||
/// </summary>
|
||||
public static void UpdateMousePosition()
|
||||
{
|
||||
#if ENABLE_INPUT_SYSTEM
|
||||
MousePosition = UnityEngine.InputSystem.Mouse.current?.position.ReadValue() ?? Vector2.zero;
|
||||
#else
|
||||
MousePosition = (Vector2)Input.mousePosition;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回当前是否按住指定鼠标按钮。
|
||||
/// 内部根据条件编译选择检测方式。
|
||||
/// </summary>
|
||||
/// <param name="button">0=左键, 1=右键, 2=中键</param>
|
||||
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";
|
||||
|
||||
// ===== 生命周期 =====
|
||||
|
||||
/// <summary>初始化 UGUI 输入管理器并绑定到指定蓝图。</summary>
|
||||
public BlueprintUGUIInputManager(BlueprintGraph graph)
|
||||
{
|
||||
_graph = graph;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试绑定 EventTrigger 到背景 Image。
|
||||
/// 返回 <c>true</c> 表示绑定成功,<c>false</c> 表示背景尚未创建。
|
||||
/// </summary>
|
||||
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<EventTrigger>();
|
||||
if (_eventTrigger == null)
|
||||
_eventTrigger = _bgTransform.gameObject.AddComponent<EventTrigger>();
|
||||
else
|
||||
_eventTrigger.triggers.Clear();
|
||||
|
||||
BindEventTrigger();
|
||||
_isBound = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>解绑并清理 EventTrigger。</summary>
|
||||
public void Unbind()
|
||||
{
|
||||
if (_eventTrigger != null)
|
||||
{
|
||||
_eventTrigger.triggers.Clear();
|
||||
_eventTrigger = null;
|
||||
}
|
||||
_bgTransform = null;
|
||||
_isBound = false;
|
||||
}
|
||||
|
||||
/// <summary>是否已绑定。</summary>
|
||||
public bool IsBound => _isBound;
|
||||
|
||||
/// <summary>背景 RectTransform(用于屏幕点包含检测)。</summary>
|
||||
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<BaseEventData> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 00688a047bee7ee4b837f07886b45d44
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,226 @@
|
||||
using UnityEngine;
|
||||
using XericLibrary.Runtime.Blueprint.Render;
|
||||
|
||||
namespace XericLibrary.Runtime.Blueprint
|
||||
{
|
||||
/// <summary>
|
||||
/// QuickGraph 输入交互工具 —— 处理画布平移、缩放和节点选择。
|
||||
/// <para>中键拖拽 = 平移;左键点击 = 选择;滚轮 = 缩放。</para>
|
||||
/// <para>快捷键(新输入系统):方向键 = 平移,Ctrl+± = 缩放,Ctrl+H = 归位。</para>
|
||||
/// </summary>
|
||||
[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<QuickGraphInputConfig>();
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>当前悬停的元素(可能为 null)</summary>
|
||||
public BlueprintElementBase HoveredElement => _hoveredElement;
|
||||
|
||||
/// <summary>当前选中的元素(可能为 null)</summary>
|
||||
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 计算)=====
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1fe1086d7e9750541a9a5de047e05ce6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user