GFramework/GFramework.Godot/Input/GodotInputMapBackend.cs
GeWuYou 158f98a465 fix(input): 修复输入绑定重置与审查遗留问题
- 修复 InputBindingStore 的线程安全使用说明并消除 GodotInputBindingCodec 的重复键码计算\n- 修复 GodotInputMapBackend 在全量重置时未移除运行时新增动作的语义偏差并补回归测试\n- 更新 input-system-godot-integration 的 tracking 与 trace,补录 PR review follow-up 验证结果
2026-05-11 10:33:52 +08:00

100 lines
2.8 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)
{
ArgumentException.ThrowIfNullOrWhiteSpace(actionName);
if (!InputMap.HasAction(actionName))
{
return Array.Empty<InputBindingDescriptor>();
}
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;
}
if (InputMap.HasAction(actionName))
{
// Actions absent from the captured default snapshot should disappear after reset
// so the live InputMap matches the original project defaults exactly.
InputMap.EraseAction(actionName);
}
}
/// <inheritdoc />
public void ResetAll()
{
foreach (var actionName in GetActionNames())
{
ResetAction(actionName);
}
}
}