/* * @Author: 白哉 * @Description: * @Date: 2025年09月05日 星期五 15:09:36 * @Modify: */ using System.Collections.Generic; using System.IO; using Logic.CrashSight; using MemoryPack; using TH1_Logic.Tools; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; namespace Logic { public class InputConfigManager { public static InputConfigManager Instance = new InputConfigManager(); private InputConfig _config; private List _pressedKeys = new List(); private bool _isListening = false; private float _waitTime = 0.2f; // 等待时间,防止误触 private float _lastKeyPressTime; private InputEnum _curInputEnum; private Dictionary> _inputCallBack; public InputConfig Config => _config; public bool IsListening => _isListening; public void Init() { RefreshInputConfig(); _config.Init(); _inputCallBack = new Dictionary>(); } // 开始监听按键绑定, UI 调用开始记录输入 public void StartRecordInput(InputEnum inputEnum) { if (_isListening) return; _curInputEnum = inputEnum; _isListening = true; _pressedKeys.Clear(); } // 外部获取状态使用, 游戏逻辑用 public bool IsActive(InputEnum inputEnum) { if (_config == null) return false; var keyConfig = _config.GetInputKeyConfig(inputEnum); if (keyConfig == null) return false; return keyConfig.IsActive; } // 注册输入回调 public void RegisterInputCallback(InputEnum inputEnum, System.Action callback) { if (!_inputCallBack.ContainsKey(inputEnum)) { _inputCallBack[inputEnum] = new List(); } _inputCallBack[inputEnum].Add(callback); } public void RefreshInputConfig() { if (_config != null) return; string path = Application.persistentDataPath + "/../Config/input_config.dat"; string backupPath = path + ".bak"; // 尝试读取主文件 _config = FileTools.TryDeserializeFile(path); // 主文件损坏,尝试从备份恢复 if (_config == null && File.Exists(backupPath)) { LogSystem.LogInfo("[InputConfig] 主文件损坏,尝试从备份恢复..."); _config = FileTools.TryDeserializeFile(backupPath); if (_config != null) { LogSystem.LogInfo("[InputConfig] 从备份恢复成功"); try { byte[] bytes = MemoryPackSerializer.Serialize(_config); FileTools.SafeWriteFile(path, bytes); } catch (System.Exception e) { LogSystem.LogError($"[InputConfig] 恢复写入主文件失败: {e.Message}"); } } else { LogSystem.LogError("[InputConfig] 备份文件也已损坏,将重置数据"); } } _config ??= new InputConfig(); } // 保存输入配置 public void SaveInputConfig() { if (_config == null) return; byte[] bytes = MemoryPackSerializer.Serialize(_config); FileTools.SafeWriteFile(Application.persistentDataPath + "/../Config/input_config.dat", bytes); } // 每帧调用,返回绑定结果(null表示还在监听中) public void Update() { UpdateConfigListening(); if (InputTextFocusUtility.IsTextInputFocused()) { ClearInputActiveState(); return; } UpdateConfigActive(); } private void ClearInputActiveState() { if (_config?.InputKeyConfigs == null) return; foreach (var keyConfig in _config.InputKeyConfigs) keyConfig.IsActive = false; } private void UpdateConfigListening() { if (!_isListening) return; // 检测所有按下的键 foreach (KeyCode key in System.Enum.GetValues(typeof(KeyCode))) { if (key == KeyCode.None) continue; if (Input.GetKeyDown(key)) { if (!_pressedKeys.Contains(key)) { _pressedKeys.Add(key); _lastKeyPressTime = Time.time; } } } // 检查是否所有键都已释放 if (_pressedKeys.Count > 0) { bool allReleased = true; foreach (var key in _pressedKeys) { if (Input.GetKey(key)) { allReleased = false; break; } } if (_pressedKeys.Count >= 3) allReleased = true; // 所有键释放后,确认绑定 if (allReleased && Time.time - _lastKeyPressTime > _waitTime) { _isListening = false; _config?.SetInputKeyConfig(_curInputEnum, _pressedKeys); SaveInputConfig(); // TODO 这里可以调用 UI 刷新,因为配置发生了变化 } } } private void UpdateConfigActive() { if (_config == null || _isListening) return; foreach (var keyConfig in _config.InputKeyConfigs) { keyConfig.IsActive = IsKeyCombinationActive(keyConfig.KeyCodes); if (IsKeyCombinationTriggered(keyConfig.KeyCodes)) OnInputCallback(keyConfig.InputType); } } // 检查组合键是否被激活 private bool IsKeyCombinationActive(List keyCodes) { for (int i = 0; i < keyCodes.Count; i++) { if (!Input.GetKey(keyCodes[i])) return false; } return true; } // 检查组合键是否被触发 private bool IsKeyCombinationTriggered(List keyCodes) { if (keyCodes == null || keyCodes.Count == 0) return false; // 单键情况 if (keyCodes.Count == 1) { return Input.GetKeyDown(keyCodes[0]); } // 组合键:前面的键必须按住,最后一个键检测 GetKeyDown for (int i = 0; i < keyCodes.Count - 1; i++) { if (!Input.GetKey(keyCodes[i])) return false; } return Input.GetKeyDown(keyCodes[^1]); } // 输入触发时的回调 private void OnInputCallback(InputEnum inputEnum) { LogSystem.LogInfo($"触发 {inputEnum}"); if (_inputCallBack != null && _inputCallBack.TryGetValue(inputEnum, out var callbackList)) { foreach (var callback in callbackList) { callback?.Invoke(); } } } } public static class InputTextFocusUtility { public static bool IsTextInputFocused() { var selected = EventSystem.current?.currentSelectedGameObject; if (selected == null) return false; return IsFocusedInputField(selected.GetComponent()) || IsFocusedInputField(selected.GetComponentInParent()) || IsFocusedInputField(selected.GetComponent()) || IsFocusedInputField(selected.GetComponentInParent()); } private static bool IsFocusedInputField(TMP_InputField inputField) { return inputField != null && inputField.isActiveAndEnabled && inputField.isFocused; } private static bool IsFocusedInputField(InputField inputField) { return inputField != null && inputField.isActiveAndEnabled && inputField.isFocused; } } }