AlicizaX/Client/Assets/Scripts/CustomeModule/InputGlyph/TestRebindScript.cs

246 lines
7.5 KiB
C#
Raw Normal View History

using System;
2026-03-11 11:40:00 +08:00
using System.Collections.Generic;
using System.Threading.Tasks;
using TMPro;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;
public class TestRebindScript : MonoBehaviour
{
2025-12-10 17:38:31 +08:00
[Header("UI")] public UXButton btn;
public TextMeshProUGUI bindKeyText;
public Image targetImage;
[Tooltip("如果不使用 actionReference则用 name 在全局 manager 查找")]
public string actionName = "movement";
2025-12-10 17:38:31 +08:00
[Header("Optional composite part (WASD style)")] [Tooltip("如果需要绑定 composite 的某一部分(例如 Up/Down/Left/Right填这个留空表示绑定非 composite 或整体 binding")]
public string compositePartName = "";
2025-12-10 17:38:31 +08:00
[Header("Behavior")] [Tooltip("如果 true在 Prepare 后自动调用 ConfirmApply() 并保存;否则等待手动 ConfirmPrepared()/CancelPrepared()")]
public bool autoConfirm = false;
2026-03-11 13:04:31 +08:00
/// <summary>
/// 启动时初始化并订阅事件
/// </summary>
private void Start()
{
if (btn != null) btn.onClick.AddListener(OnBtnClicked);
InputDeviceWatcher.OnDeviceContextChanged += OnDeviceContextChanged;
InputBindingManager.BindingsChanged += OnBindingsChanged;
UpdateBindingText();
if (InputBindingManager.Instance != null)
{
2026-03-11 13:04:31 +08:00
// 订阅事件
2026-03-11 11:28:36 +08:00
InputBindingManager.Instance.OnRebindPrepare += OnRebindPrepareHandler;
InputBindingManager.Instance.OnApply += OnApplyHandler;
InputBindingManager.Instance.OnRebindEnd += OnRebindEndHandler;
InputBindingManager.Instance.OnRebindConflict += OnRebindConflictHandler;
}
}
2026-03-11 13:04:31 +08:00
/// <summary>
/// 禁用时取消订阅事件
/// </summary>
2026-03-11 11:28:36 +08:00
private void OnDisable()
{
if (btn != null) btn.onClick.RemoveListener(OnBtnClicked);
InputDeviceWatcher.OnDeviceContextChanged -= OnDeviceContextChanged;
InputBindingManager.BindingsChanged -= OnBindingsChanged;
2026-03-09 20:38:15 +08:00
2026-03-11 11:28:36 +08:00
if (InputBindingManager.Instance != null)
{
InputBindingManager.Instance.OnRebindPrepare -= OnRebindPrepareHandler;
InputBindingManager.Instance.OnApply -= OnApplyHandler;
InputBindingManager.Instance.OnRebindEnd -= OnRebindEndHandler;
InputBindingManager.Instance.OnRebindConflict -= OnRebindConflictHandler;
}
}
2026-03-11 13:04:31 +08:00
/// <summary>
/// 重新绑定准备完成的处理器
/// </summary>
2026-03-11 11:28:36 +08:00
private void OnRebindPrepareHandler(InputBindingManager.RebindContext ctx)
{
if (IsTargetContext(ctx))
{
var disp = ctx.overridePath == InputBindingManager.NULL_BINDING ? "<Cleared>" : ctx.overridePath;
bindKeyText.text = disp;
if (autoConfirm) _ = ConfirmPreparedAsync();
}
}
2026-03-09 20:38:15 +08:00
2026-03-11 13:04:31 +08:00
/// <summary>
/// 应用重新绑定的处理器
/// </summary>
2026-03-11 11:28:36 +08:00
private void OnApplyHandler(bool success, HashSet<InputBindingManager.RebindContext> appliedContexts)
{
if (appliedContexts != null)
{
2026-03-11 13:04:31 +08:00
// 仅当任何应用/丢弃的上下文与此实例匹配时才更新
2026-03-11 11:28:36 +08:00
foreach (var ctx in appliedContexts)
2026-03-09 20:38:15 +08:00
{
2026-03-11 11:28:36 +08:00
if (IsTargetContext(ctx))
2026-03-09 20:38:15 +08:00
{
UpdateBindingText();
2026-03-11 11:28:36 +08:00
break;
2026-03-09 20:38:15 +08:00
}
2026-03-11 11:28:36 +08:00
}
}
}
2026-03-11 13:04:31 +08:00
/// <summary>
/// 重新绑定结束的处理器
/// </summary>
2026-03-11 11:28:36 +08:00
private void OnRebindEndHandler(bool success, InputBindingManager.RebindContext context)
{
2026-03-11 11:28:36 +08:00
if (IsTargetContext(context))
{
UpdateBindingText();
}
}
2026-03-11 13:04:31 +08:00
/// <summary>
/// 重新绑定冲突的处理器
/// </summary>
2026-03-11 11:28:36 +08:00
private void OnRebindConflictHandler(InputBindingManager.RebindContext prepared, InputBindingManager.RebindContext conflict)
{
2026-03-11 13:04:31 +08:00
// 如果准备的或冲突的上下文匹配此实例,则更新
2026-03-11 11:28:36 +08:00
if (IsTargetContext(prepared) || IsTargetContext(conflict))
{
UpdateBindingText();
}
}
2026-03-11 13:04:31 +08:00
/// <summary>
/// 设备变更的回调
/// </summary>
private void OnDeviceContextChanged(InputDeviceWatcher.DeviceContext _)
{
UpdateBindingText();
}
private void OnBindingsChanged()
{
UpdateBindingText();
}
2026-03-11 13:04:31 +08:00
/// <summary>
/// 获取当前的输入操作
/// </summary>
private InputAction GetAction()
{
return InputBindingManager.Action(actionName);
}
2026-03-11 13:04:31 +08:00
/// <summary>
/// 判断上下文是否为目标上下文
/// </summary>
2026-03-09 20:38:15 +08:00
private bool IsTargetContext(InputBindingManager.RebindContext ctx)
{
if (ctx == null || ctx.action == null) return false;
var action = GetAction();
if (action == null) return false;
2026-03-09 20:38:15 +08:00
2026-03-11 13:04:31 +08:00
// 必须匹配操作
2026-03-09 20:38:15 +08:00
if (ctx.action != action) return false;
2026-03-11 13:04:31 +08:00
// 如果指定了复合部分,需要匹配绑定索引
2026-03-09 20:38:15 +08:00
if (!string.IsNullOrEmpty(compositePartName))
{
2026-03-11 13:04:31 +08:00
// 获取上下文索引处的绑定
2026-03-09 20:38:15 +08:00
if (ctx.bindingIndex < 0 || ctx.bindingIndex >= action.bindings.Count)
return false;
var binding = action.bindings[ctx.bindingIndex];
2026-03-11 13:04:31 +08:00
// 检查绑定的名称是否与我们的复合部分匹配
2026-03-09 20:38:15 +08:00
return string.Equals(binding.name, compositePartName, StringComparison.OrdinalIgnoreCase);
}
2026-03-11 13:04:31 +08:00
// 如果未指定复合部分,仅匹配操作就足够了
2026-03-09 20:38:15 +08:00
return true;
}
2026-03-11 13:04:31 +08:00
/// <summary>
/// 按钮点击的回调
/// </summary>
private void OnBtnClicked()
{
2026-03-11 13:04:31 +08:00
// 使用管理器 API我们传递部分名称以便管理器可以在需要时选择适当的绑定
InputBindingManager.StartRebind(actionName, string.IsNullOrEmpty(compositePartName) ? null : compositePartName);
}
2026-03-11 13:04:31 +08:00
/// <summary>
/// 确认准备好的重新绑定(公共方法)
/// </summary>
public async void ConfirmPrepared()
{
bool ok = await ConfirmPreparedAsync();
if (!ok) Debug.LogError("ConfirmPrepared: apply failed.");
}
2026-03-11 13:04:31 +08:00
/// <summary>
/// 确认准备好的重新绑定(异步)
/// </summary>
private async Task<bool> ConfirmPreparedAsync()
{
try
{
var task = InputBindingManager.ConfirmApply();
return await task;
}
2025-12-10 17:38:31 +08:00
catch (Exception ex)
{
Debug.LogError(ex);
return false;
}
}
2026-03-11 13:04:31 +08:00
/// <summary>
/// 取消准备好的重新绑定
/// </summary>
public void CancelPrepared()
{
InputBindingManager.DiscardPrepared();
2026-03-11 13:04:31 +08:00
// UpdateBindingText 将通过 OnApply 事件自动调用
}
2026-03-11 13:04:31 +08:00
/// <summary>
/// 更新绑定文本和图标显示
/// </summary>
private void UpdateBindingText()
{
var action = GetAction();
var deviceCat = InputDeviceWatcher.CurrentCategory;
if (action == null)
{
bindKeyText.text = "<no action>";
if (targetImage != null) targetImage.sprite = null;
return;
}
bindKeyText.text = GlyphService.GetDisplayNameFromInputAction(action, compositePartName, deviceCat);
try
{
if (GlyphService.TryGetUISpriteForActionPath(action, compositePartName, deviceCat, out Sprite sprite))
{
if (targetImage != null) targetImage.sprite = sprite;
}
else
{
if (targetImage != null) targetImage.sprite = null;
}
}
catch
{
if (targetImage != null) targetImage.sprite = null;
}
}
}