前面已经完成了 UI 和业务流程的基本使用。这一篇以实际项目中的 GameHotKeyManager 为例,介绍如何用 FrameSystem 统一管理快捷键。
项目地址:
一、为什么快捷键适合使用 FrameSystem
快捷键系统需要长期存在,并在每帧检测输入。它不属于某个界面,也不属于某个 SceneProcedure,因此适合继承 FrameSystem。
实际项目中的 GameHotKeyManager 同时管理:
bash
移动、技能和普通攻击
快捷栏物品
拾取和 NPC 操作
召唤兽控制
界面打开和关闭
本篇只保留 UI 快捷键部分,方便理解基本用法。
二、定义快捷键功能
快捷键不要直接用 KeyCode 作为业务标识,而是先定义功能编号:
bash
public class HOT_KEY_ACTION
{
public const int NONE = 0;
public const int OPEN_MYSLEF_VIEW = 1001;
public const int OPEN_BACKPACK = 1002;
public const int OPEN_FRIEND = 1003;
public const int OPEN_GAME_SETTING = 1010;
public const int OPEN_MISSION_PANEL = 1011;
}
业务逻辑检测的是 OPEN_BACKPACK,而不是直接检测 KeyCode.Keypad1。
这样后续修改按键,或者允许玩家自定义键位时,不需要修改具体业务代码。
三、创建 GameHotKeyManager
创建文件:
bash
Assets/Scripts/HotFix/GameHotKeyManager/GameHotKeyManager.cs
基础结构如下:
bash
using System;
using System.Collections.Generic;
using UnityEngine;
using static GBR;
public class GameHotKeyManager : FrameSystem
{
protected Dictionary<int, Type> mKeyToUI = new(); // 快捷键功能到界面类型
protected Dictionary<Type, int> mUIToKey = new(); // 界面类型到快捷键功能
public override void init()
{
base.init();
setUIHotKey<UIMyselfView>(
HOT_KEY_ACTION.OPEN_MYSLEF_VIEW,
KeyCode.Keypad0,
"角色面板");
setUIHotKey<UIBackpack>(
HOT_KEY_ACTION.OPEN_BACKPACK,
KeyCode.Keypad1,
"背包");
setUIHotKey<UIFriend>(
HOT_KEY_ACTION.OPEN_FRIEND,
KeyCode.Keypad2,
"好友");
setUIHotKey<UIGameSetting>(
HOT_KEY_ACTION.OPEN_GAME_SETTING,
KeyCode.Keypad9,
"设置");
setUIHotKey<UIMissionPanel>(
HOT_KEY_ACTION.OPEN_MISSION_PANEL,
KeyCode.KeypadPeriod,
"任务");
}
}
这里没有直接打开界面,只是建立三者之间的关系:
bash
快捷键功能编号
↕
默认 KeyCode
↕
LayoutScript 类型
四、注册普通快捷键
先提供一个普通快捷键注册函数:
bash
protected void setHotKey(int mapping, KeyCode key, string name)
{
mKeyMappingSystem.setKeyMapping(mapping, key, name);
mKeyMappingSystem.setDefaultKeyMapping(mapping, key);
}
两次设置的用途不同:
bash
setKeyMapping 设置当前使用的按键
setDefaultKeyMapping 保存默认按键
保存默认按键后,设置界面可以提供"恢复默认键位"功能。
五、注册 UI 快捷键
UI 快捷键在普通快捷键基础上,还要记录对应的界面类型:
bash
protected void setUIHotKey<T>(int mapping, KeyCode key, string name)
where T : LayoutScript
{
setHotKey(mapping, key, name);
mKeyToUI.Add(mapping, typeof(T));
mUIToKey.Add(typeof(T), mapping);
}
mKeyToUI 用于按下快捷键后查找界面。
mUIToKey 用于界面查询自己的快捷键名称。
六、每帧检测快捷键
重写 update,统一处理界面的打开和隐藏:
bash
public override void update(float elapsedTime)
{
base.update(elapsedTime);
if (!FrameUtility.isWindows())
{
return;
}
foreach (var item in mKeyToUI)
{
if (!mKeyMappingSystem.isMappingKeyCurrentDown(item.Key))
{
continue;
}
GameLayout layout = mLayoutManager.getLayout(item.Value);
if (layout == null || !layout.isVisible())
{
LT.LOAD(item.Value);
}
else
{
LT.HIDE(item.Value);
}
}
}
处理规则很简单:
bash
界面未加载 加载并显示
界面已经隐藏 重新显示
界面正在显示 隐藏界面
这里使用快捷键功能编号检测输入:
bash
mKeyMappingSystem.isMappingKeyCurrentDown(item.Key)
而不是:
bash
Input.GetKeyDown(KeyCode.Keypad1)
因此玩家修改键位以后,这段逻辑不需要变化。
七、让界面显示自己的快捷键
为界面提供一个查询函数:
bash
public string getKeyName(Type layoutType)
{
int mapping = mUIToKey.get(layoutType);
return mKeyMappingSystem.getKeyMappingName(mapping);
}
界面中可以直接显示当前快捷键:
bash
using static GBR;
public class UIBackpack : LayoutScript
{
protected myUGUIText mHotKey;
public override void onGameState()
{
base.onGameState();
string keyName = mGameHotKeyILRManager.getKeyName(GetType());
mHotKey.setText("(" + keyName + ")");
}
}
如果玩家修改了背包快捷键,下次打开背包时显示的按键名称也会同步变化。
八、添加全局引用
在 GBR 中添加管理器引用:
bash
public class GBR
{
public static GameHotKeyManager mGameHotKeyILRManager;
// 其他 FrameSystem
}
需要使用它的代码只需添加:
bash
using static GBR;
然后通过 mGameHotKeyILRManager 访问。
九、注册 FrameSystem
打开:
bash
Assets/Scripts/HotFix/Game/GameHotFix.cs
在 initFrameSystem 中注册:
bash
protected override void initFrameSystem()
{
registeFrameSystem<GameHotKeyManager>((com) =>
{
mGameHotKeyILRManager = com;
});
}
注册完成后,框架会自动调用它的:
bash
init
lateInit
update
willDestroy
destroy
不需要创建单例 MonoBehaviour,也不需要单独编写一个节点负责调用 Update。
十、继续扩展其他快捷键
普通游戏操作也可以使用同一套映射:
bash
public override void init()
{
base.init();
setHotKey(HOT_KEY_ACTION.MOVE_LEFT, KeyCode.A, "向左移动");
setHotKey(HOT_KEY_ACTION.MOVE_RIGHT, KeyCode.D, "向右移动");
setHotKey(HOT_KEY_ACTION.PICK, KeyCode.X, "拾取");
setHotKey(HOT_KEY_ACTION.NORMAL_ATTACK, KeyCode.Z, "普攻");
setUIHotKey<UIBackpack>(
HOT_KEY_ACTION.OPEN_BACKPACK,
KeyCode.Keypad1,
"背包");
}
检测拾取快捷键:
bash
if (mKeyMappingSystem.isMappingKeyCurrentDown(HOT_KEY_ACTION.PICK))
{
CmdGlobalPickGold.execute(
mObjectItemManager.getItemInRange(
getMyself().getPosition(),
PICK_RANGE));
}
UI 快捷键和战斗快捷键共用 KeyMappingSystem,但具体业务仍然可以分别处理。
十一、总结
创建全局快捷键系统的步骤是:
这套设计把"业务功能"和"实际按键"分离开来,既能统一管理所有快捷键,也方便后续实现键位修改、恢复默认设置和快捷键文本显示。