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

279 lines
8.4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace XericLibrary.Runtime.Blueprint
{
/// <summary>
/// 蓝图输入工具基类(UGUI)—— 注册 EventTrigger 到蓝图背景上监听鼠标事件,
/// 并将事件发布到 <see cref="BlueprintToolProvider"/>,同时记录光标上下文。
/// <para>
/// 注意:此基类本身不被注册为独立工具(无 <c>[BlueprintTool]</c> 属性)。
/// 由子类 <c>BlueprintInputTool_InputSystem</c> 和 <c>BlueprintInputTool_InputManager</c>
/// 通过继承获得 UGUI 绑定能力。
/// </para>
/// </summary>
public class BlueprintInputTool_UGUI : GraphInputTool
{
// ===== EventTrigger 绑定 =====
private Transform _bgTransform;
private EventTrigger _eventTrigger;
private RectTransform _bgRectTransform;
private const string BackgroundGoName = "__Bp_GridBackground";
// 上一帧的鼠标画布坐标(用于计算 PointerMove delta)
private Vector2 _lastCanvasPos;
/// <summary>
/// 是否通过 EventTrigger 处理鼠标按键/拖拽/滚轮事件。
/// 新输入系统路径下为 true(默认),旧输入系统路径下为 false(由 PollUpdate 轮询)。
/// 子类可在 <see cref="OnInitialize"/> 中修改此值。
/// </summary>
protected bool UseEventTriggerForMouse { get; set; } = true;
// ===== 生命周期 =====
public override void OnInitialize()
{
TryBindEventTrigger();
}
public override void OnUpdate(float deltaTime)
{
if (Graph == null) return;
// 检查底层的 Unity Canvas 组件是否仍存活
// Graph?.Canvas 是 C# 接口引用(非 Unity Object),不适用 Unity 的假空检测
if (Graph.Canvas == null || !Graph.Canvas.IsAlive)
return;
Graph.BeginFrame();
BlueprintUGUIInputManager.UpdateMousePosition();
BlueprintToolProvider.MousePosition = BlueprintUGUIInputManager.MousePosition;
// 每帧检查 EventTrigger 是否有效(应对 Editor 重收集工具场景)
if (_eventTrigger == null || !IsEventTriggerValid())
{
_eventTrigger = null; // 强制重新绑定
TryBindEventTrigger();
return;
}
var canvasPt = Graph.Canvas != null
? Graph.Canvas.ScreenToCanvas(BlueprintUGUIInputManager.MousePosition)
: Vector2.zero;
UpdateCursorContext(canvasPt);
// ── 每帧分发 PointerMove ──
var delta = canvasPt - _lastCanvasPos;
if (delta.sqrMagnitude > 0.0001f || _lastCanvasPos == Vector2.zero)
{
BlueprintToolProvider.DispatchPointerMove(Graph, this, canvasPt, delta);
}
_lastCanvasPos = canvasPt;
}
/// <summary>
/// 检查已绑定的 EventTrigger 实例是否仍然有效(回调仍指向本实例)。
/// </summary>
private bool IsEventTriggerValid()
{
if (_eventTrigger == null) return false;
var triggersList = _eventTrigger.triggers;
if (triggersList == null || triggersList.Count == 0) return false;
var callbacks = triggersList[0].callback;
if (callbacks == null) return false;
int persistentCount = callbacks.GetPersistentEventCount();
if (persistentCount > 0)
{
var target = callbacks.GetPersistentTarget(0);
return target != null && target.Equals(this);
}
return true;
}
public override void OnDestroy()
{
UnbindEventTrigger();
}
/// <summary>
/// 确保场景中存在 EventSystem 和合适的 InputModule。
/// 旧输入系统 → StandaloneInputModule。
/// 新输入系统 → InputSystemUIInputModule(由 GraphTheoryBlueprintComponent 创建)。
/// 不会重复创建模块(通过 GetComponent 检查)。
/// </summary>
private static void EnsureEventSystemExists()
{
var es = Object.FindObjectOfType<EventSystem>();
if (es == null)
{
var go = new GameObject("EventSystem");
es = go.AddComponent<EventSystem>();
}
#if !ENABLE_INPUT_SYSTEM
if (es.GetComponent<StandaloneInputModule>() == null)
{
es.gameObject.AddComponent<StandaloneInputModule>();
}
#endif
}
// ===== EventTrigger 绑定/解绑 =====
private void TryBindEventTrigger()
{
if (_eventTrigger != null) return;
if (Graph?.Canvas == null || !Graph.Canvas.IsAlive) return;
var root = Graph.Canvas.GetRootTransform();
if (root == null) return;
_bgTransform = root.Find(BackgroundGoName);
if (_bgTransform == null) return;
_bgRectTransform = _bgTransform as RectTransform;
// ── 自动创建 EventSystem(含合适 InputModule)──
EnsureEventSystemExists();
_eventTrigger = _bgTransform.gameObject.GetComponent<EventTrigger>();
if (_eventTrigger == null)
{
_eventTrigger = _bgTransform.gameObject.AddComponent<EventTrigger>();
}
else
_eventTrigger.triggers.Clear();
BindAllEntries();
}
private void UnbindEventTrigger()
{
if (_eventTrigger != null)
{
_eventTrigger.triggers.Clear();
_eventTrigger = null;
}
_bgTransform = null;
_bgRectTransform = null;
}
private void BindAllEntries()
{
if (_eventTrigger == null) return;
// ── 始终绑定的基础回调(指针进入/离开) ──
AddEntry(EventTriggerType.PointerEnter, OnPointerEnterEvent);
AddEntry(EventTriggerType.PointerExit, OnPointerExitEvent);
// ── 鼠标按键/拖拽/滚轮(仅在 UseEventTriggerForMouse 时绑定) ──
if (UseEventTriggerForMouse)
{
AddEntry(EventTriggerType.PointerDown, OnPointerDownEvent);
AddEntry(EventTriggerType.PointerUp, OnPointerUpEvent);
AddEntry(EventTriggerType.BeginDrag, OnBeginDragEvent);
AddEntry(EventTriggerType.Drag, OnDragEvent);
AddEntry(EventTriggerType.EndDrag, OnEndDragEvent);
AddEntry(EventTriggerType.Scroll, OnScrollEvent);
}
}
private void AddEntry(EventTriggerType type, UnityEngine.Events.UnityAction<BaseEventData> callback)
{
var entry = new EventTrigger.Entry { eventID = type };
entry.callback.AddListener(callback);
_eventTrigger.triggers.Add(entry);
}
// ===== 坐标转换 =====
private Vector2 ScreenToCanvas(Vector2 screenPos)
{
return Graph?.Canvas != null ? Graph.Canvas.ScreenToCanvas(screenPos) : screenPos;
}
private Vector2 PointerCanvasPos(PointerEventData data)
{
return ScreenToCanvas(data.position);
}
// ===== EventTrigger 回调(以 self 作为 sender 派发) =====
private void OnPointerEnterEvent(BaseEventData _)
{
BlueprintToolProvider.DispatchPointerEnter(Graph, this);
}
private void OnPointerExitEvent(BaseEventData _)
{
BlueprintToolProvider.DispatchPointerExit(Graph, this);
}
private void OnPointerDownEvent(BaseEventData data)
{
if (!(data is PointerEventData pData)) return;
var pt = PointerCanvasPos(pData);
Graph?.MarkDirty();
UpdateCursorContext(pt);
BlueprintToolProvider.DispatchPointerDown(Graph, this, pt, (int)pData.button);
}
private void OnPointerUpEvent(BaseEventData data)
{
if (!(data is PointerEventData pData)) return;
var pt = PointerCanvasPos(pData);
UpdateCursorContext(pt);
// 有拖拽的释放由 OnEndDragEvent 处理,这里只响应无拖拽的点击释放
if (!_hadDragThisClick)
{
bool isEmptyClick = Graph?.HitTest(pt) == null;
BlueprintToolProvider.DispatchPointerUp(Graph, this, pt, (int)pData.button, isEmptyClick);
}
}
private Vector2 _dragStartPos;
private bool _hadDragThisClick;
private void OnBeginDragEvent(BaseEventData data)
{
if (!(data is PointerEventData pData)) return;
_dragStartPos = PointerCanvasPos(pData);
_hadDragThisClick = true;
}
private void OnDragEvent(BaseEventData data)
{
if (!(data is PointerEventData pData)) return;
var current = PointerCanvasPos(pData);
var delta = current - _dragStartPos;
_dragStartPos = current;
UpdateCursorContext(current);
BlueprintToolProvider.DispatchPointerDrag(Graph, this, current, delta, (int)pData.button);
}
private void OnEndDragEvent(BaseEventData data)
{
if (!(data is PointerEventData pData)) return;
var pt = PointerCanvasPos(pData);
UpdateCursorContext(pt);
BlueprintToolProvider.DispatchPointerUp(Graph, this, pt, (int)pData.button);
}
private void OnScrollEvent(BaseEventData data)
{
if (!(data is PointerEventData pData)) return;
var pt = PointerCanvasPos(pData);
var scroll = new Vector2(-pData.scrollDelta.x, -pData.scrollDelta.y);
UpdateCursorContext(pt);
Graph?.MarkDirty();
BlueprintToolProvider.DispatchScroll(Graph, this, pt, scroll);
}
}
}