263 lines
8.5 KiB
C#
263 lines
8.5 KiB
C#
/*
|
||
* @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<KeyCode> _pressedKeys = new List<KeyCode>();
|
||
private bool _isListening = false;
|
||
private float _waitTime = 0.2f; // 等待时间,防止误触
|
||
private float _lastKeyPressTime;
|
||
private InputEnum _curInputEnum;
|
||
private Dictionary<InputEnum, List<System.Action>> _inputCallBack;
|
||
public InputConfig Config => _config;
|
||
public bool IsListening => _isListening;
|
||
|
||
|
||
public void Init()
|
||
{
|
||
RefreshInputConfig();
|
||
_config.Init();
|
||
_inputCallBack = new Dictionary<InputEnum, List<System.Action>>();
|
||
}
|
||
|
||
// 开始监听按键绑定, 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<System.Action>();
|
||
}
|
||
_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<InputConfig>(path);
|
||
|
||
// 主文件损坏,尝试从备份恢复
|
||
if (_config == null && File.Exists(backupPath))
|
||
{
|
||
LogSystem.LogInfo("[InputConfig] 主文件损坏,尝试从备份恢复...");
|
||
_config = FileTools.TryDeserializeFile<InputConfig>(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<KeyCode> keyCodes)
|
||
{
|
||
for (int i = 0; i < keyCodes.Count; i++)
|
||
{
|
||
if (!Input.GetKey(keyCodes[i]))
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
// 检查组合键是否被触发
|
||
private bool IsKeyCombinationTriggered(List<KeyCode> 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<TMP_InputField>())
|
||
|| IsFocusedInputField(selected.GetComponentInParent<TMP_InputField>())
|
||
|| IsFocusedInputField(selected.GetComponent<InputField>())
|
||
|| IsFocusedInputField(selected.GetComponentInParent<InputField>());
|
||
}
|
||
|
||
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;
|
||
}
|
||
}
|
||
}
|