Skip to main content
Version: 1.20.x

键盘布局

键盘布局(键映射)或键盘绑定定义了应与输入绑定的特定操作:鼠标单击、按键等。每当客户端可以进行输入时,都可以检查键盘布局定义的每个操作。此外,每个键盘布局都可以通过控制选项菜单分配给任何输入。

注册一个KeyMapping

KeyMapping可以通过仅在物理客户端上监听模组事件总线上的RegisterKeyMappingsEvent并调用#register来注册。

// 在某个仅物理客户端的类中

// 键盘布局是延迟初始化的,因此在注册之前它不存在
public static final Lazy<KeyMapping> EXAMPLE_MAPPING = Lazy.of(() -> /*...*/);

// 事件仅在物理客户端上的模组事件总线上
@SubscribeEvent
public void registerBindings(RegisterKeyMappingsEvent event) {
event.register(EXAMPLE_MAPPING.get());
}

创建一个KeyMapping

KeyMapping可以使用其构造函数创建。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", // 将使用该翻译键进行本地化
InputConstants.Type.KEYSYM, // 在键盘上的默认映射
GLFW.GLFW_KEY_P, // 默认键为P
"key.categories.misc" // 映射将在杂项(misc)类别中
)

!!! 注意 如果键映射不应映射到默认值,则应将输入设置为InputConstants#UNKNOWN。原版构造函数将要求你通过InputConstants$Key#getValue提取输入代码,而Forge构造函数可以提供原始输入字段。

IKeyConflictContext

并非所有映射都用于每个上下文。有些映射仅在GUI中使用,而另一些映射仅在游戏中使用。为了避免在不同上下文中使用的同一键的映射相互冲突,可以分配IKeyConflictContext

每个冲突上下文包含两种方法:#isActive,定义映射是否可以在当前游戏状态下使用;#conflicts,定义在相同或不同的冲突上下文中映射是否与键冲突。

目前,Forge通过KeyConflictContext定义了三个基本上下文:UNIVERSAL,这是默认的,意味着密钥可以在每个上下文中使用;GUI,这意味着映射只能在Screen打开时使用;IN_GAME,意味着映射只有在Screen未打开时才能使用。可以通过实现IKeyConflictContext来创建新的冲突上下文。

new KeyMapping(
"key.examplemod.example2",
KeyConflictContext.GUI, // 映射只能在当一个屏幕打开时使用
InputConstants.Type.MOUSE, // 在鼠标上的默认映射
GLFW.GLFW_MOUSE_BUTTON_LEFT, // 在鼠标左键上的默认鼠标输入
"key.categories.examplemod.examplecategory" // 映射将在新的示例类别中
)

KeyModifier

如果修改键保持不变(例如GCTRL + G),则修改器可能不希望映射具有相同的行为。为了解决这个问题,Forge在构造函数中添加了一个额外的参数来接受一个KeyModifier,它可以将control(KeyModifier#CONTROL)、shift(KeyModifier#SHIFT)或alt(KeyModifier#ALT)映射到任何输入。KeyModifier#NONE是默认值,不会应用任何修改器。

通过接纳修饰符键和相关输入,可以在控制选项菜单中添加修改器。

new KeyMapping(
"key.examplemod.example3",
KeyConflictContext.UNIVERSAL,
KeyModifier.SHIFT, // 默认映射要求shift被按下
InputConstants.Type.KEYSYM, // 默认映射在键盘上
GLFW.GLFW_KEY_G, // 默认键为G
"key.categories.misc"
)

检查一个KeyMapping

可以检查KeyMapping以查看它是否已被单击。根据时间的不同,可以在条件中使用映射来应用关联的逻辑。

在游戏内

在游戏内,应通过在Forge事件总线上监听ClientTickEvent并在while循环中检查KeyMapping#consumeClick来检查映射。#consumeClick仅当输入已执行但之前尚未处理的次数时才会返回true,因此不会无限拖延游戏。

// 事件仅在物理客户端上的Forge事件总线上
public void onClientTick(ClientTickEvent event) {
if (event.phase == TickEvent.Phase.END) { // 仅调用代码一次,因为tick事件在每个tick调用两次
while (EXAMPLE_MAPPING.get().consumeClick()) {
// 在此处执行单击时的逻辑
}
}
}

!!! 警告 不要将InputEvent用作ClientTickEvent的替代项。只有键盘和鼠标输入有单独的事件,所以它们不会处理任何额外的输入。

Inside a GUI

在GUI内,可以使用IForgeKeyMapping#isActiveAndMatches在其中一个GuiEventListener方法中检查映射。可以检查的最常见方法是#keyPressed#mouseClicked

#keyPressed接收GLFW键令牌、特定于平台的扫描代码和按下的修改器的位字段。通过使用InputConstants#getKey创建输入,可以根据映射检查键。修改器已经在映射方法本身中进行了检查。

// 在某个Screen子类中
@Override
public boolean keyPressed(int key, int scancode, int mods) {
if (EXAMPLE_MAPPING.get().isActiveAndMatches(InputConstants.getKey(key, scancode))) {
// 在此处执行按键时的逻辑
return true;
}
return super.keyPressed(x, y, button);
}

!!! 注意 如果你不拥有要检查的屏幕,你可以在Forge事件总线上监听ScreenEvent$KeyPressedPrePost事件。

#mouseClicked获取鼠标的x位置、y位置和单击的按钮。通过使用带有MOUSE输入的InputConstants$Type#getOrCreate创建输入,可以根据映射检查鼠标按钮。

// 在某个Screen子类中
@Override
public boolean mouseClicked(double x, double y, int button) {
if (EXAMPLE_MAPPING.get().isActiveAndMatches(InputConstants.TYPE.MOUSE.getOrCreate(button))) {
// 在此处执行鼠标单击时的逻辑
return true;
}
return super.mouseClicked(x, y, button);
}

!!! 注意 如果你不拥有要检查鼠标的屏幕,你可以在Forge事件总线上监听ScreenEvent$MouseButtonPressedPrePost事件。