GFramework/GFramework.Godot/Input/GodotInputMapBackend.cs
gewuyou ebbef321ad feat(input): 新增统一输入抽象与Godot集成
- 新增输入绑定 DTO、设备上下文和 UI 语义桥接契约。

- 实现 Game 默认输入绑定存储、动作映射和 UI 分发桥接。

- 落地 Godot InputMap 适配、测试覆盖与配套文档。

- 更新 ai-plan 恢复点、worktree 映射与采用入口。
2026-05-10 22:29:26 +08:00

88 lines
2.4 KiB
C#

// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
using GFramework.Game.Abstractions.Input;
namespace GFramework.Godot.Input;
/// <summary>
/// 基于 Godot `InputMap` 的默认后端实现。
/// </summary>
internal sealed class GodotInputMapBackend : IGodotInputMapBackend
{
private readonly Dictionary<string, List<InputBindingDescriptor>> _defaults;
/// <summary>
/// 初始化后端,并捕获当前 `InputMap` 作为默认快照。
/// </summary>
public GodotInputMapBackend()
{
_defaults = GetActionNames().ToDictionary(
static actionName => actionName,
actionName => GetBindings(actionName).ToList(),
StringComparer.Ordinal);
}
/// <inheritdoc />
public IReadOnlyList<string> GetActionNames()
{
return [..InputMap.GetActions().Select(static action => action.ToString())];
}
/// <inheritdoc />
public IReadOnlyList<InputBindingDescriptor> GetBindings(string actionName)
{
var bindings = new List<InputBindingDescriptor>();
foreach (var inputEvent in InputMap.ActionGetEvents(actionName))
{
if (GodotInputBindingCodec.TryCreateBinding(inputEvent, out var binding))
{
bindings.Add(binding);
}
}
return bindings;
}
/// <inheritdoc />
public void SetBindings(string actionName, IReadOnlyList<InputBindingDescriptor> bindings)
{
ArgumentException.ThrowIfNullOrWhiteSpace(actionName);
ArgumentNullException.ThrowIfNull(bindings);
if (!InputMap.HasAction(actionName))
{
InputMap.AddAction(actionName);
}
InputMap.ActionEraseEvents(actionName);
foreach (var binding in bindings)
{
InputMap.ActionAddEvent(actionName, GodotInputBindingCodec.CreateInputEvent(binding));
}
}
/// <inheritdoc />
public void ResetAction(string actionName)
{
ArgumentException.ThrowIfNullOrWhiteSpace(actionName);
if (_defaults.TryGetValue(actionName, out var bindings))
{
SetBindings(actionName, bindings);
return;
}
InputMap.ActionEraseEvents(actionName);
}
/// <inheritdoc />
public void ResetAll()
{
foreach (var actionName in GetActionNames())
{
ResetAction(actionName);
}
}
}