Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions GFramework.Game.Abstractions/Input/IInputBindingStore.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0

namespace GFramework.Game.Abstractions.Input;

/// <summary>
/// 定义逻辑动作绑定的查询、修改与快照导入导出契约。
/// </summary>
/// <remarks>
/// 该接口承担框架输入系统的持久化与重绑定边界。
/// 宿主层可以把自己的原生输入系统适配到这里,上层业务则只依赖动作名和绑定描述,不直接接触宿主输入事件。
/// </remarks>
public interface IInputBindingStore
{
/// <summary>
/// 获取指定动作的当前绑定。
/// </summary>
/// <param name="actionName">动作名称。</param>
/// <returns>动作绑定快照。</returns>
InputActionBinding GetBindings(string actionName);

/// <summary>
/// 获取所有动作的当前绑定快照。
/// </summary>
/// <returns>全量输入绑定快照。</returns>
InputBindingSnapshot ExportSnapshot();

/// <summary>
/// 使用给定快照替换当前绑定。
/// </summary>
/// <param name="snapshot">要导入的快照。</param>
void ImportSnapshot(InputBindingSnapshot snapshot);

/// <summary>
/// 把指定绑定设置为动作的主绑定。
/// </summary>
/// <param name="actionName">动作名称。</param>
/// <param name="binding">新绑定。</param>
/// <param name="swapIfTaken">是否在冲突时交换已占用绑定。</param>
void SetPrimaryBinding(string actionName, InputBindingDescriptor binding, bool swapIfTaken = true);

/// <summary>
/// 将指定动作恢复为默认绑定。
/// </summary>
/// <param name="actionName">动作名称。</param>
void ResetAction(string actionName);

/// <summary>
/// 将所有动作恢复为默认绑定。
/// </summary>
void ResetAll();
}
15 changes: 15 additions & 0 deletions GFramework.Game.Abstractions/Input/IInputDeviceTracker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0

namespace GFramework.Game.Abstractions.Input;

/// <summary>
/// 定义当前活跃输入设备上下文的查询入口。
/// </summary>
public interface IInputDeviceTracker
{
/// <summary>
/// 获取当前输入设备上下文。
/// </summary>
InputDeviceContext CurrentDevice { get; }
}
20 changes: 20 additions & 0 deletions GFramework.Game.Abstractions/Input/IUiInputActionMap.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0

using GFramework.Game.Abstractions.UI;

namespace GFramework.Game.Abstractions.Input;

/// <summary>
/// 定义逻辑动作名到 UI 语义动作的映射规则。
/// </summary>
public interface IUiInputActionMap
{
/// <summary>
/// 尝试把逻辑动作映射为 UI 语义动作。
/// </summary>
/// <param name="actionName">逻辑动作名称。</param>
/// <param name="action">映射出的 UI 语义动作。</param>
/// <returns>如果映射成功则返回 <see langword="true" />。</returns>
bool TryMap(string actionName, out UiInputAction action);
}
17 changes: 17 additions & 0 deletions GFramework.Game.Abstractions/Input/IUiInputDispatcher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0

namespace GFramework.Game.Abstractions.Input;

/// <summary>
/// 定义面向 UI 语义动作的输入分发入口。
/// </summary>
public interface IUiInputDispatcher
{
/// <summary>
/// 尝试把逻辑动作分发到当前 UI 路由。
/// </summary>
/// <param name="actionName">逻辑动作名称。</param>
/// <returns>如果该动作被映射为 UI 动作并成功分发,则返回 <see langword="true" />。</returns>
bool TryDispatch(string actionName);
}
37 changes: 37 additions & 0 deletions GFramework.Game.Abstractions/Input/InputActionBinding.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0

namespace GFramework.Game.Abstractions.Input;

/// <summary>
/// 描述一个逻辑动作当前持有的绑定集合。
/// </summary>
public sealed class InputActionBinding
{
/// <summary>
/// 初始化一个动作绑定快照。
/// </summary>
/// <param name="actionName">动作名称。</param>
/// <param name="bindings">当前绑定列表。</param>
/// <exception cref="ArgumentException">当 <paramref name="actionName" /> 为空时抛出。</exception>
public InputActionBinding(string actionName, IReadOnlyList<InputBindingDescriptor> bindings)
{
if (string.IsNullOrWhiteSpace(actionName))
{
throw new ArgumentException("Action name cannot be null or whitespace.", nameof(actionName));
}

ActionName = actionName;
Bindings = bindings ?? Array.Empty<InputBindingDescriptor>();
}

/// <summary>
/// 获取动作名称。
/// </summary>
public string ActionName { get; }

/// <summary>
/// 获取当前绑定列表。
/// </summary>
public IReadOnlyList<InputBindingDescriptor> Bindings { get; }
}
67 changes: 67 additions & 0 deletions GFramework.Game.Abstractions/Input/InputBindingDescriptor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0

namespace GFramework.Game.Abstractions.Input;

/// <summary>
/// 描述一个框架无关的动作绑定。
/// </summary>
/// <remarks>
/// 该模型是运行时输入系统与宿主适配层之间的稳定交换格式。
/// 宿主层负责把原生输入事件转成此描述,抽象层和默认运行时只根据这些字段做查询、冲突检测和持久化。
/// </remarks>
public sealed class InputBindingDescriptor
{
/// <summary>
/// 初始化一个动作绑定描述。
/// </summary>
/// <param name="deviceKind">设备族。</param>
/// <param name="bindingKind">绑定类型。</param>
/// <param name="code">宿主无关的物理码值。</param>
/// <param name="displayName">用于设置界面展示的名称。</param>
/// <param name="axisDirection">轴向方向;非轴向绑定时为 <see langword="null" />。</param>
/// <exception cref="ArgumentException">当 <paramref name="code" /> 为空时抛出。</exception>
public InputBindingDescriptor(
InputDeviceKind deviceKind,
InputBindingKind bindingKind,
string code,
string displayName,
float? axisDirection = null)
{
if (string.IsNullOrWhiteSpace(code))
{
throw new ArgumentException("Binding code cannot be null or whitespace.", nameof(code));
}

DeviceKind = deviceKind;
BindingKind = bindingKind;
Code = code;
DisplayName = displayName ?? string.Empty;
AxisDirection = axisDirection;
}

/// <summary>
/// 获取设备族。
/// </summary>
public InputDeviceKind DeviceKind { get; }

/// <summary>
/// 获取绑定类型。
/// </summary>
public InputBindingKind BindingKind { get; }

/// <summary>
/// 获取宿主无关的物理码值。
/// </summary>
public string Code { get; }

/// <summary>
/// 获取用于展示的标签。
/// </summary>
public string DisplayName { get; }

/// <summary>
/// 获取轴向方向。
/// </summary>
public float? AxisDirection { get; }
}
35 changes: 35 additions & 0 deletions GFramework.Game.Abstractions/Input/InputBindingKind.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0

namespace GFramework.Game.Abstractions.Input;

/// <summary>
/// 描述一个逻辑绑定使用的物理输入类型。
/// </summary>
public enum InputBindingKind
{
/// <summary>
/// 未指定。
/// </summary>
Unknown = 0,

/// <summary>
/// 键盘按键。
/// </summary>
Key = 1,

/// <summary>
/// 鼠标按钮。
/// </summary>
MouseButton = 2,

/// <summary>
/// 手柄按钮。
/// </summary>
GamepadButton = 3,

/// <summary>
/// 手柄轴向。
/// </summary>
GamepadAxis = 4
}
24 changes: 24 additions & 0 deletions GFramework.Game.Abstractions/Input/InputBindingSnapshot.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0

namespace GFramework.Game.Abstractions.Input;

/// <summary>
/// 描述一组动作绑定的可持久化快照。
/// </summary>
public sealed class InputBindingSnapshot
{
/// <summary>
/// 初始化一个输入绑定快照。
/// </summary>
/// <param name="actions">动作绑定集合。</param>
public InputBindingSnapshot(IReadOnlyList<InputActionBinding> actions)
{
Actions = actions ?? Array.Empty<InputActionBinding>();
}

/// <summary>
/// 获取动作绑定集合。
/// </summary>
public IReadOnlyList<InputActionBinding> Actions { get; }
}
41 changes: 41 additions & 0 deletions GFramework.Game.Abstractions/Input/InputDeviceContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0

namespace GFramework.Game.Abstractions.Input;

/// <summary>
/// 描述当前活跃输入设备上下文。
/// </summary>
public sealed class InputDeviceContext
{
/// <summary>
/// 初始化一个输入设备上下文。
/// </summary>
/// <param name="deviceKind">当前设备族。</param>
/// <param name="deviceIndex">设备索引;未知时为 <see langword="null" />。</param>
/// <param name="deviceName">宿主归一化后的设备名称。</param>
public InputDeviceContext(
InputDeviceKind deviceKind,
int? deviceIndex = null,
string? deviceName = null)
{
DeviceKind = deviceKind;
DeviceIndex = deviceIndex;
DeviceName = deviceName ?? string.Empty;
}

/// <summary>
/// 获取当前设备族。
/// </summary>
public InputDeviceKind DeviceKind { get; }

/// <summary>
/// 获取当前设备索引。
/// </summary>
public int? DeviceIndex { get; }

/// <summary>
/// 获取宿主归一化后的设备名称。
/// </summary>
public string DeviceName { get; }
}
34 changes: 34 additions & 0 deletions GFramework.Game.Abstractions/Input/InputDeviceKind.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0

namespace GFramework.Game.Abstractions.Input;

/// <summary>
/// 描述框架级输入设备族。
/// </summary>
/// <remarks>
/// 该枚举用于跨宿主共享“当前输入来自哪一类设备”的语义。
/// 它故意避免暴露 Godot、Unity 或平台 SDK 的原生事件类型,确保上层业务只依赖稳定的设备族判断。
/// </remarks>
public enum InputDeviceKind
{
/// <summary>
/// 未识别或尚未产生任何输入。
/// </summary>
Unknown = 0,

/// <summary>
/// 键盘与鼠标输入。
/// </summary>
KeyboardMouse = 1,

/// <summary>
/// 游戏手柄输入。
/// </summary>
Gamepad = 2,

/// <summary>
/// 触摸输入。
/// </summary>
Touch = 3
}
15 changes: 15 additions & 0 deletions GFramework.Game.Abstractions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
- 典型使用场景:
- 定义 `IScene`、`IUiPage`、`ISettingsData`、`IData` 等业务对象
- 让 feature 包只感知 `IConfigRegistry`、`ISaveRepository<T>`、`ISettingsModel`、`IUiRouter`、`ISceneRouter`
- 在输入层共享动作绑定、设备上下文和 UI 语义桥接契约
- 在引擎适配层之外共享设置、场景参数、UI 参数、存档数据类型

## 与相邻包的关系
Expand Down Expand Up @@ -131,6 +132,18 @@ UI 页面与路由契约。

`IUiRouter` 不只覆盖页面栈,还覆盖 Overlay / Modal / Toast / Topmost 等层级 UI 语义。

### `Input/`

- `InputBindingDescriptor`
- `InputActionBinding`
- `InputBindingSnapshot`
- `IInputBindingStore`
- `IInputDeviceTracker`
- `IUiInputActionMap`
- `IUiInputDispatcher`

这一层定义的是统一输入抽象、绑定快照与 UI 语义桥接契约。

### `Routing/`

- `IRoute`
Expand Down Expand Up @@ -164,6 +177,7 @@ Scene 与 UI 路由共享这套基础约定。
| `Setting/` | `ISettingsData`、`ISettingsModel`、`ISettingsSystem`、`LocalizationSettings` | 看设置数据、应用语义、迁移接口和内置设置对象 |
| `Scene/` | `IScene`、`ISceneRouter`、`ISceneFactory`、`SceneTransitionEvent` | 看场景行为、路由、工厂 / root 边界与转场事件模型 |
| `UI/` | `IUiPage`、`IUiRouter`、`IUiFactory`、`UiInteractionProfile`、`UiTransitionHandlerOptions` | 看页面栈、层级 UI、输入动作与 UI 转场契约 |
| `Input/` | `InputBindingDescriptor`、`IInputBindingStore`、`IInputDeviceTracker`、`IUiInputDispatcher` | 看动作绑定、设备上下文和 UI 输入桥接契约 |
Comment thread
coderabbitai[bot] marked this conversation as resolved.
| `Routing/` `Storage/` `Asset/` `Enums/` | `IRoute`、`IRouteContext`、`IFileStorage`、`IAssetRegistry<T>`、`UiLayer`、`SceneTransitionType` | 看公共路由上下文、存储角色、资源注册表与跨层共享枚举 |

## 最小接入路径
Expand Down Expand Up @@ -267,6 +281,7 @@ public sealed class ContinueGameCommandHandler
- 序列化系统:[序列化系统](../docs/zh-CN/game/serialization.md)
- 场景系统:[场景系统](../docs/zh-CN/game/scene.md)
- UI 系统:[UI 系统](../docs/zh-CN/game/ui.md)
- 输入系统:[输入系统](../docs/zh-CN/game/input.md)

## 选择建议

Expand Down
Loading
Loading