From 7e93d7d08903a3b6e929efd1f42508f81f3ac6cd Mon Sep 17 00:00:00 2001 From: GwWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Sat, 13 Dec 2025 22:29:49 +0800 Subject: [PATCH] =?UTF-8?q?feat(godot):=20=E6=B7=BB=E5=8A=A02D=E6=8B=96?= =?UTF-8?q?=E6=8B=BD=E5=8A=9F=E8=83=BD=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增抽象基类 AbstractDragDrop2DComponentBase,提供基础拖拽信号和属性 - 实现 AbstractDragDropArea2DComponent 类,处理具体拖拽逻辑与输入事件 - 支持拖拽开始、取消、放置等完整交互流程 - 集成 Godot 节点生命周期管理与输入系统 - 添加对拖拽组、Z轴层级及偏移量的控制支持 - 引入 Godot.SourceGenerators 包以支持信号生成 --- .../GFramework.Core.Godot.csproj | 1 + .../AbstractDragDrop2DComponentBase.cs | 100 ++++++++++++++ .../AbstractDragDropArea2DComponent.cs | 127 ++++++++++++++++++ 3 files changed, 228 insertions(+) create mode 100644 GFramework.Core.Godot/component/AbstractDragDrop2DComponentBase.cs create mode 100644 GFramework.Core.Godot/component/AbstractDragDropArea2DComponent.cs diff --git a/GFramework.Core.Godot/GFramework.Core.Godot.csproj b/GFramework.Core.Godot/GFramework.Core.Godot.csproj index 952517b..26631d2 100644 --- a/GFramework.Core.Godot/GFramework.Core.Godot.csproj +++ b/GFramework.Core.Godot/GFramework.Core.Godot.csproj @@ -9,6 +9,7 @@ + diff --git a/GFramework.Core.Godot/component/AbstractDragDrop2DComponentBase.cs b/GFramework.Core.Godot/component/AbstractDragDrop2DComponentBase.cs new file mode 100644 index 0000000..f85b6a7 --- /dev/null +++ b/GFramework.Core.Godot/component/AbstractDragDrop2DComponentBase.cs @@ -0,0 +1,100 @@ +using GFramework.Core.architecture; +using GFramework.Core.controller; +using GFramework.Core.events; +using GFramework.Core.extensions; +using Godot; + +namespace GFramework.Core.Godot.component; + +/// +/// 抽象基类,用于实现2D拖拽功能的组件。 +/// 继承自Godot的Node类并实现了IController接口。 +/// 提供了拖拽相关的信号定义以及基础属性配置。 +/// +public abstract partial class AbstractDragDrop2DComponentBase : Node, IController +{ + /// + /// 取消注册列表,用于管理需要在节点销毁时取消注册的对象 + /// + protected readonly IUnRegisterList UnRegisterList = new UnRegisterList(); + + /// + /// 当拖拽被取消时触发的信号。 + /// + /// 拖拽起始位置。 + [Signal] + public delegate void DragCanceledEventHandler(Vector2 startingPosition); + + /// + /// 当拖拽开始时触发的信号。 + /// + [Signal] + public delegate void DragStartedEventHandler(); + + /// + /// 当拖拽结束并放置时触发的信号。 + /// + /// 拖拽起始位置。 + [Signal] + public delegate void DroppedEventHandler(Vector2 startingPosition); + + /// + /// 是否启用拖拽功能。若为 false,则忽略所有输入事件。 + /// + public bool Enable { get; set; } + + /// + /// 拖拽组的名称,用于区分不同的拖拽组。 + /// + public string GroupName { get; set; } = "dragging"; + + /// + /// 获取或设置取消拖拽输入操作的名称 + /// + public string CancelDragInputActionName { get; set; } = "cancel_drag"; + + /// + /// 获取或设置选择输入操作的名称 + /// + public string SelectInputActionName { get; set; } = "select"; + + /// + /// 拖拽时元素的最大Z轴索引值。 + /// + public int ZIndexMax { get; set; } = 99; + + /// + /// 拖拽时元素的最小Z轴索引值。 + /// + public int ZIndexMin { get; set; } = 0; + + /// + /// 获取架构实例。 + /// + /// 返回实现IArchitecture接口的架构实例。 + public abstract IArchitecture GetArchitecture(); + + /// + /// 表示是否正在拖拽操作的标志位。 + /// + protected bool IsDragging; + + /// + /// 表示拖拽操作中的偏移量,用于计算当前位置与起始位置的差值。 + /// + protected Vector2 Offset = Vector2.Zero; + + /// + /// 表示拖拽操作的起始位置坐标。 + /// + protected Vector2 StartingPosition; + + /// + /// 节点退出场景树时的回调方法。 + /// 在节点从场景树移除前调用,用于清理资源。 + /// + public override void _ExitTree() + { + UnRegisterList.UnRegisterAll(); + } +} \ No newline at end of file diff --git a/GFramework.Core.Godot/component/AbstractDragDropArea2DComponent.cs b/GFramework.Core.Godot/component/AbstractDragDropArea2DComponent.cs new file mode 100644 index 0000000..f83d488 --- /dev/null +++ b/GFramework.Core.Godot/component/AbstractDragDropArea2DComponent.cs @@ -0,0 +1,127 @@ +using GFramework.Core.Godot.extensions; +using Godot; + +namespace GFramework.Core.Godot.component; + +/// +/// 抽象拖拽组件类,用于处理节点的拖放逻辑。 +/// 实现了 IController 接口以支持架构通信,并通过信号通知拖拽事件的发生。 +/// +public abstract partial class AbstractDragDropArea2DComponent : AbstractDragDrop2DComponentBase +{ + /// + /// 目标区域,通常是可交互的游戏对象(如单位或物品)所在的碰撞区域。 + /// + public required Area2D Target { get; set; } + + /// + /// 节点准备就绪时的回调方法。 + /// 在节点添加到场景树后调用,绑定目标区域的输入事件处理器。 + /// + public override void _Ready() + { + Target = GetParent() as Area2D ?? throw new InvalidOperationException("Target must be an Area2D node."); + Target.InputEvent += OnTargetInputEvent; + } + + /// + /// 处理输入事件的回调方法。 + /// 根据当前拖拽状态和输入事件类型,执行相应的拖拽操作。 + /// + /// 输入事件对象 + public override void _Input(InputEvent @event) + { + switch (IsDragging) + { + // 处理取消拖拽操作:当正在拖拽且按下取消拖拽按键时,执行取消拖拽逻辑 + case true when Target.IsValidNode() && @event.IsActionPressed(CancelDragInputActionName): + CancelDragging(); + // 设置输入为处理,防止输入穿透 + this.SetInputAsHandled(); + break; + case true when @event.IsActionReleased(SelectInputActionName): + Drop(); + break; + } + } + + /// + /// 目标区域的输入事件处理器。 + /// 当目标区域接收到输入事件时被调用,用于控制拖拽的开始和结束。 + /// + /// 事件发生的视口节点 + /// 输入事件对象 + /// 事件触点ID(未使用) + private void OnTargetInputEvent(Node viewport, InputEvent @event, long _) + { + if (!Enable) return; + + // 获取当前正在拖拽的对象 + var draggingObj = GetTree().GetFirstNodeInGroup(GroupName); + switch (IsDragging) + { + // 处理开始拖拽操作:当未在拖拽状态且按下选择按键时,开始拖拽 + case false when + // 如果当前没有拖拽操作且已有其他对象正在拖拽,则直接返回 + draggingObj is not null: + return; + case false when @event.IsActionPressed(SelectInputActionName): + StartDragging(); + break; + } + } + + /// + /// 每帧更新逻辑,在拖拽过程中持续更新目标的位置。 + /// + /// 与上一帧的时间间隔(秒)。 + public override void _Process(double delta) + { + if (IsDragging && Target.IsValidNode()) Target.GlobalPosition = Target.GetGlobalMousePosition() + Offset; + } + + /// + /// 结束拖拽流程的基础方法。 + /// 清除拖拽标志位并将目标从拖拽组中移除,恢复其层级顺序。 + /// + private void EndDragging() + { + IsDragging = false; + Target.RemoveFromGroup(GroupName); + Target.ZIndex = ZIndexMin; + } + + /// + /// 执行取消拖拽的操作。 + /// 调用 EndDragging 并发出 DragCanceled 信号。 + /// + private void CancelDragging() + { + EndDragging(); + EmitSignalDragCanceled(StartingPosition); + } + + /// + /// 开始拖拽操作。 + /// 设置初始位置和偏移量,将目标加入拖拽组并提升显示层级,最后发出 DragStarted 信号。 + /// + private void StartDragging() + { + IsDragging = true; + StartingPosition = Target.GlobalPosition; + Target.AddToGroup(GroupName); + Target.ZIndex = ZIndexMax; + Offset = Target.GlobalPosition - Target.GetGlobalMousePosition(); + EmitSignalDragStarted(); + } + + /// + /// 完成一次拖拽操作。 + /// 调用 EndDragging 方法并发出 Dropped 信号。 + /// + private void Drop() + { + EndDragging(); + EmitSignalDropped(StartingPosition); + } +} \ No newline at end of file