键盘布局
键盘布局(键映射)或键盘绑定定义了应与输入绑定的特定操作:鼠标单击、按键等。每当客户端可以进行输入时,都可以检查键盘布局定义的每个操作。此外,每个键盘布局都可以通过控制选项菜单分配给任何输入。
注册一个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
如果修改键保持不变(例如G
与CTRL + 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$KeyPressed
的Pre
或Post
事件。
#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$MouseButtonPressed
的Pre
或Post
事件。