按键映射
按键映射(key mapping,也称 key binding)定义与鼠标点击、按键等输入关联的特定操作。只要客户端能够接收输入,就可以检查映射定义的操作。用户还可以通过控制选项菜单将每个映射分配给任意输入。
注册 KeyMapping
仅在物理客户端上监听 Mod 事件总线的 RegisterKeyMappingsEvent,再调用 #register,即可注册 KeyMapping。
// In some physical client only class
// Key mapping is lazily initialized so it doesn't exist until it is registered
public static final Lazy<KeyMapping> EXAMPLE_MAPPING = Lazy.of(() -> /*...*/);
@SubscribeEvent // on the mod event bus only on the physical client
public static void registerBindings(RegisterKeyMappingsEvent event) {
event.register(EXAMPLE_MAPPING.get());
}
创建 KeyMapping
使用构造函数创建 KeyMapping。它接收定义映射名称的翻译键、默认输入,以及决定映射在控制选项菜单中所属分类的 KeyMapping.Category。
建议 要使用自定义分类,可以用
Identifier创建新的KeyMapping.Category,并仅在物理客户端的 Mod 事件总线上,通过RegisterKeyMappingsEvent#registerCategory注册。分类对应的翻译键为key.category.<namespace>.<path>。public static final KeyMapping.Category EXAMPLE_CATEGORY = new KeyMapping.Category(Identifier.fromNamespaceAndPath("examplemod", "category")); @SubscribeEvent // on the mod event bus only on the physical client public static void registerBindings(RegisterKeyMappingsEvent event) { // Register category event.registerCategory(EXAMPLE_CATEGORY); // Register binding with category used event.register(EXAMPLE_MAPPING.get()); }
默认输入
每个按键映射都有一个通过 InputConstants.Key 提供的默认输入。每种输入由 InputConstants.Type 和整数构成:前者定义提供输入的设备,后者定义设备上对应输入的标识。
原版提供三种输入类型:KEYSYM 使用 GLFW 按键常量表示键盘;SCANCODE 使用平台相关扫描码表示键盘;MOUSE 表示鼠标。
提示 强烈建议键盘使用
KEYSYM而不是SCANCODE,因为GLFW按键常量不依赖具体系统。更多信息请参阅 GLFW 文档。
整数的含义取决于提供的类型。所有输入代码均在 GLFW 中定义:KEYSYM 常量以 GLFW_KEY_* 为前缀,MOUSE 代码以 GLFW_MOUSE_* 为前缀。
new KeyMapping(
"key.examplemod.example1", // Will be localized using this translation key
InputConstants.Type.KEYSYM, // Default mapping is on the keyboard
GLFW.GLFW_KEY_P, // Default key is P
KeyMapping.Category.MISC // Mapping will be in the misc category
)
提示 如果不希望映射具有默认输入,应将其设为
InputConstants#UNKNOWN。原版构造函数要求通过InputConstants$Key#getValue提取输入代码,而 NeoForge 构造函数可以直接接收原始输入字段。
IKeyConflictContext
并非所有映射都在所有场景使用。有些映射仅用于 GUI,另一些只用于游戏内。为避免相同按键在不同场景的映射相互冲突,可以指定 IKeyConflictContext。
每个冲突上下文都包含两个方法:#isActive 判断当前游戏状态下能否使用该映射,#conflicts 判断此映射是否与相同或不同上下文中的另一个按键冲突。
NeoForge 目前通过 KeyConflictContext 定义三个基本上下文:默认的 UNIVERSAL 表示所有场景;GUI 表示仅当 Screen 打开时可用;IN_GAME 表示仅当 Screen 未打开时可用。实现 IKeyConflictContext 可以创建新的冲突上下文。
new KeyMapping(
"key.examplemod.example2",
KeyConflictContext.GUI, // Mapping can only be used when a screen is open
InputConstants.Type.MOUSE, // Default mapping is on the mouse
GLFW.GLFW_MOUSE_BUTTON_LEFT, // Default mouse input is the left mouse button
EXAMPLE_CATEGORY // Mapping will be in the new example category
)
KeyModifier
开发者可能希望按住修饰键时映射具有不同的行为,例如 G 和 CTRL + G。为此,NeoForge 在构造函数中添加了 KeyModifier 参数,可以为任何输入附加 Control(KeyModifier#CONTROL)、Shift(KeyModifier#SHIFT)或 Alt(KeyModifier#ALT)。默认的 KeyModifier#NONE 表示不使用修饰键。
在控制选项菜单中,同时按住修饰键和相应输入,就可以添加修饰键。
new KeyMapping(
"key.examplemod.example3",
KeyConflictContext.UNIVERSAL,
KeyModifier.SHIFT, // Default mapping requires shift to be held down
InputConstants.Type.KEYSYM, // Default mapping is on the keyboard
GLFW.GLFW_KEY_G, // Default key is G
KeyMapping.Category.MISC
)
检查 KeyMapping
可以检查 KeyMapping 是否被点击,并根据检查发生的时机,在条件判断中执行相应逻辑。
游戏内
在游戏内,应监听事件总线上的 ClientTickEvent.Post,并在 while 循环中检查 KeyMapping#consumeClick。该方法仅会对尚未处理的输入次数返回 true,因此不会无限循环使游戏停滞。
@SubscribeEvent // on the game event bus only on the physical client
public static void onClientTick(ClientTickEvent.Post event) {
while (EXAMPLE_MAPPING.get().consumeClick()) {
// Execute logic to perform on click here
}
}
注意 不要用
InputEvent替代ClientTickEvent.Post。这些事件只分别处理键盘和鼠标输入,无法涵盖其他输入。
GUI 内
在 GUI 中,可以在某个 GuiEventListener 方法中,通过 IKeyMappingExtension#isActiveAndMatches 检查映射。最常用的方法是 #keyPressed 和 #mouseClicked。
#keyPressed 接收 KeyEvent,其中包含 GLFW 按键常量、平台相关扫描码以及当前按住的修饰键位字段。通过 InputConstants#getKey 创建输入后,就可以与映射比较。映射方法内部已经检查修饰键。
// In some Screen subclass
@Override
public boolean keyPressed(KeyEvent event) {
if (EXAMPLE_MAPPING.get().isActiveAndMatches(InputConstants.getKey(event))) {
// Execute logic to perform on key press here
return true;
}
return super.keyPressed(event);
}
提示 如果要检查的键盘按键属于并非由你控制的界面,可以改为监听游戏事件总线上的
ScreenEvent.KeyPressed的Pre或Post事件。
#mouseClicked 接收 MouseButtonEvent(包含鼠标 x、y 位置及被点击的 MouseButtonInfo),以及一个表示是否双击的 boolean。通过 InputConstants.Type#getOrCreate 和 MOUSE 输入类型创建输入后,就可以与映射比较。
// In some Screen subclass
@Override
public boolean mouseClicked(MouseButtonEvent event, boolean doubleClick) {
if (EXAMPLE_MAPPING.get().isActiveAndMatches(InputConstants.Type.MOUSE.getOrCreate(event.button()))) {
// Execute logic to perform on mouse click here
return true;
}
return super.mouseClicked(event, doubleClick);
}
提示 如果要检查的鼠标按键属于并非由你控制的界面,可以改为监听游戏事件总线上的
ScreenEvent.MouseButtonPressed的Pre或Post事件。
来源: NeoForged/Documentation(26.1)。中文翻译依据提交
4a680cdd4d15。