按键映射

按键映射

按键映射(也称按键绑定)将某项操作与鼠标点击、按键等输入关联起来。只要客户端可以接收输入,就能检查映射定义的操作。此外,每个映射都可通过控制设置菜单重新绑定到任意输入。

注册 KeyMapping

仅在物理客户端监听 Mod 事件总线上的 RegisterKeyMappingsEvent,并调用 #register:

// 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,参数包含定义映射名称的翻译键、默认输入,以及定义其在控制设置菜单中所属类别的翻译键。

建议 提供原版没有使用的类别翻译键,即可添加到自定义类别。自定义类别翻译键应包含 Mod ID,例如 key.categories.examplemod.examplecategory。

默认输入

每个按键映射都关联一个默认输入,由 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
    "key.categories.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
    "key.categories.examplemod.examplecategory" // Mapping will be in the new example category
)

KeyModifier

开发者可能希望按住修饰键时,同一按键具有不同行为,例如 G 与 Ctrl+G。NeoForge 为构造函数增加了 KeyModifier 参数,可添加 Ctrl(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
    "key.categories.misc"
)

检查 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 中,可以通过 IKeyMappingExtension#isActiveAndMatches 在某个 GuiEventListener 方法内检查映射,最常用的是 #keyPressed 与 #mouseClicked。

#keyPressed 接受 GLFW 按键标记、平台相关扫描码,以及按下的修饰键位字段。使用 InputConstants#getKey 创建输入后即可检查,映射方法本身已负责检查修饰键。

// In some Screen subclass
@Override
public boolean keyPressed(int key, int scancode, int mods) {
    if (EXAMPLE_MAPPING.get().isActiveAndMatches(InputConstants.getKey(key, scancode))) {
        // Execute logic to perform on key press here
        return true;
    }
    return super.keyPressed(x, y, button);
} 

提示 如果需要检查键盘按键但无法修改该界面,可以改为监听事件总线上 ScreenEvent.KeyPressed 的 Pre 或 Post 事件。

#mouseClicked 接受鼠标 x、y 坐标和点击的按钮。通过 InputConstants.Type#getOrCreate,使用 MOUSE 类型创建输入即可检查映射。

// In some Screen subclass
@Override
public boolean mouseClicked(double x, double y, int button) {
    if (EXAMPLE_MAPPING.get().isActiveAndMatches(InputConstants.TYPE.MOUSE.getOrCreate(button))) {
        // Execute logic to perform on mouse click here
        return true;
    }
    return super.mouseClicked(x, y, button);
} 

提示 如果需要检查鼠标按键但无法修改该界面,可以改为监听事件总线上 ScreenEvent.MouseButtonPressed 的 Pre 或 Post 事件。


来源: NeoForged/Documentation(1.21.1)。中文翻译依据提交 4a680cdd4d15。