UI 导航控制器

本页介绍使用 Windows.Gaming.Input.UINavigationController 和通用 Windows 平台 (UWP) 的相关 API 进行 UI 导航设备编程的基础知识。

在本页中,你将了解如下内容:

  • 如何收集连接的 UI 导航设备及其用户的列表
  • 如何检测是否已添加或删除导航设备
  • 如何从一个或多个 UI 导航设备读取输入
  • 游戏板和街机摇杆作为导航设备的行为方式

UI 导航控制器概述

几乎所有游戏至少有一些用户界面与游戏分开,即使它只是游戏前菜单或游戏内对话。 玩家需要能够使用他们选择的任何输入设备来导航此 UI,但开发人员需要为每种输入设备添加特定的支持,还可以在游戏和输入设备之间引入混淆玩家的不一致。 出于这些原因, 创建了 UINavigationController API。

UI 导航控制器是逻辑输入设备,可用于提供各种物理输入设备支持的常见 UI 导航命令的词汇。 UI 导航控制器只是查看物理输入设备的不同方式;我们使用导航设备来引用作为导航控制器查看的任何物理输入设备。 通过针对导航设备而不是特定输入设备的编程,开发人员可以避免支持不同输入设备的负担,并默认实现一致性。

由于每种输入设备支持的数量和各种控件可能有所不同,并且某些输入设备可能想要支持更丰富的导航命令集,因此导航控制器接口将命令的词汇划分为 包含最常见和基本命令的必需集 ,以及一个 包含方便但无用命令的可选集 。 所有导航设备都支持所需集中的每个命令,并且可能支持可选集中的所有命令、部分命令或所有命令

必需集

导航设备必须支持所需集中的所有导航命令;这些命令是方向(向上、向下、左和右)、视图、菜单、接受和取消命令。

方向命令适用于单个 UI 元素之间的主要 XY 焦点导航 。 视图和菜单命令用于显示游戏信息(通常是瞬间的,有时是模式),以及分别在游戏和菜单上下文之间切换。 接受和取消命令分别用于肯定(是)和负(否)响应。

下表汇总了这些命令及其预期用途,并提供了示例。 | 命令 | 预期用途 | -------:| --------------- | 上 | XY-聚焦导航上 | 下 | XY-聚焦导航下 | 左 | XY-聚焦导航左 | 右 | XY-聚焦导航右 | 视图 | 显示游戏玩法信息(记分牌、游戏统计信息、目标、世界地图或区域地图)| 菜单 | 主菜单/暂停(设置、状态、装备、背包、暂停)| 接受 | 肯定响应(接受、前进、确认、开始、是)| 取消 | 否定响应(拒绝、倒退、谢绝、停止、否)

可选集

导航设备还可能支持可选集中的所有、部分或无导航命令;这些是分页(向上、向下、左和右)、滚动(向上、向下、左和右),以及上下文(上下文 1-4)命令。

上下文命令显式适用于特定于应用程序的命令和导航快捷方式。 分页和滚动命令用于在页面或 UI 元素组之间快速导航,以及分别在 UI 元素中进行细粒度导航。

下表汇总了这些命令及其预期用途。 | 命令 | 预期用途 | -----------:| ------------ | PageUp | 向上跳转(至上面的/前一个垂直页面或群组)| PageDown | 向下跳转(至下面的/下一个垂直页面或群组)| PageLeft | 向左跳转(至左面的/前一个水平页面或群组)| PageRight | 向右跳转(至右面的/下一个水平页面或群组)| ScrollUp | 向上滚动(在聚焦的 UI 元素或可滚动的组内)| ScrollDown | 向下滚动(在聚焦的 UI 元素或可滚动的组内)| ScrollLeft | 向左滚动(在聚焦的 UI 元素或可滚动的组内)| ScrollRight | 向右滚动(在聚焦的 UI 元素或可滚动的组内)| Context1 | 主要上下文操作 | Context2 | 次要上下文操作 | Context3 | 第三上下文操作 | Context4 | 第四上下文操作

注意:虽然游戏可以自由地响应具有与其预期用途不同的实际功能的任何命令,但应当避免出人意料的行为。 具体而言,如果需要命令的预期用途,请不要更改命令的实际函数,请尝试将新函数分配给最有意义的命令,并将对应函数分配给对应命令(如 PageUp/PageDown)。 最后,考虑每种输入设备支持哪些命令,以及它们映射到哪些控件,确保可从每个设备访问关键命令。

游戏板、街机摇杆和赛车方向盘导航

Windows.Gaming.Input 命名空间支持的所有输入设备都是 UI 导航设备。

下表总结了所需的导航命令集如何映射到各种输入设备。

导航命令 游戏手柄输入 街机摇杆输入 赛车方向盘输入
向上 左控制杆向上/方向键向上 摇杆向上 方向键向上
向下 左控制杆向下/方向键向下 摇杆向下 方向键向下
Left 左控制杆向左/方向键向左 摇杆向左 方向键向左
Right 左控制杆向右/方向键向右 摇杆向右 方向键向右
视图 视图按钮 视图按钮 视图按钮
菜单 “菜单”按钮 菜单按钮 “菜单”按钮
Accept A 按钮 操作 1 按钮 A 按钮
Cancel B 按钮 操作 2 按钮 B 按钮

下表总结了可选导航命令集如何映射到各种输入设备。

导航命令 游戏手柄输入 街机摇杆输入 赛车方向盘输入
PageUp 左扳机键 不支持 不定
PageDown 右扳机键 不支持 不定
PageLeft 左缓冲键 不支持 不定
PageRight 右缓冲键 不支持 不定
ScrollUp 右操纵杆向上 不支持 不定
ScrollDown 右操纵杆向下 不支持 不定
ScrollLeft 右操纵杆向左 不支持 不定
ScrollRight 右操纵杆向右 不支持 不定
Context1 X 按钮 不支持 X 按钮 (通常
Context2 Y 按钮 不支持 Y 按钮(通常
Context3 左控制杆按键 不支持 不定
Context4 右控制杆按键 不支持 不定

检测和跟踪 UI 导航控制器

尽管 UI 导航控制器是逻辑输入设备,但它们是物理设备的表示形式,并且以相同的方式由系统管理。 无需创建或初始化它们;系统提供连接的 UI 导航控制器和事件列表,用于在添加或删除 UI 导航控制器时通知你。

UI 导航控制器列表

UINavigationController 类提供静态属性 UINavigationControllers,它是当前连接的 UI 导航设备的只读列表。 由于你可能只对某些连接的导航设备感兴趣,因此建议你维护自己的集合,而不是通过 UINavigationControllers 属性访问它们。

以下示例将所有连接的 UI 导航控制器复制到新集合中。

auto myNavigationControllers = ref new Vector<UINavigationController^>();

for (auto device : UINavigationController::UINavigationControllers)
{
    // This code assumes that you're interested in all navigation controllers.
    myNavigationControllers->Append(device);
}

添加和删除 UI 导航控制器

添加或删除 UI 导航控制器时, 将引发 UINavigationControllerAddedUINavigationControllerRemoved 事件。 可以为这些事件注册事件处理程序,以跟踪当前连接的导航设备。

以下示例开始跟踪已添加的 UI 导航设备。

UINavigationController::UINavigationControllerAdded += ref new EventHandler<UINavigationController^>(Platform::Object^, UINavigationController^ args)
{
    // This code assumes that you're interested in all new navigation controllers.
    myNavigationControllers->Append(args);
}

以下示例停止跟踪已删除的街机摇杆。

UINavigationController::UINavigationControllerRemoved += ref new EventHandler<UINavigationController^>(Platform::Object^, UINavigationController^ args)
{
    unsigned int indexRemoved;

    if(myNavigationControllers->IndexOf(args, &indexRemoved))
	{
        myNavigationControllers->RemoveAt(indexRemoved);
    }
}

用户和耳机

每个导航设备都可以与用户帐户相关联,以将其标识链接到其输入,并且可以附加头戴显示设备以方便语音聊天或导航功能。 要了解有关使用用户和耳机的详细信息,请参阅《跟踪用户及其设备》和《耳机》。

读取 UI 导航控制器

确定你感兴趣的 UI 导航设备后,即可从中收集输入。 但是,与可能用于的某些其他类型的输入不同,导航设备不会通过引发事件来传达状态更改。 相反,你需要通过对它们进行“轮询”来定期读取其当前状态。

轮询 UI 导航控制器

轮询可在精确时间点捕获导航设备的快照。 输入收集的此方法非常适合大多数游戏,因为它们的逻辑通常在确定性循环中运行,而不是事件驱动;它通常也更容易解释从一次性收集的输入的游戏命令,而不是从随着时间的推移收集的许多单个输入。

通过调用 UINavigationController.GetCurrentReading 来轮询导航设备;此函数返回 包含导航设备的状态的 UINavigationReading

auto navigationController = myNavigationControllers[0];

UINavigationReading reading = navigationController->GetCurrentReading();

读取按钮

每个 UI 导航按钮都提供一个布尔读取,对应于其按下(向下)还是释放(向上)。 为了提高效率,按钮读取不表示为单个布尔值;而是全部打包成由 RequiredUINavigationButtons 和 OptionalUINavigationButtons 枚举表示的两个位字段之一。

属于所需集的按钮从 RequiredButtons UINavigationReading 结构的属性中读取;属于可选集OptionalButtons按钮从属性中读取。 由于这些属性是位字段,因此使用按位掩码来隔离你感兴趣的按钮的值。 设置相应位时按下按钮(向下);否则,其释放(上)。

以下示例确定是否按下所需集中“接受”按钮。

if (RequiredUINavigationButtons::Accept == (reading.RequiredButtons & RequiredUINavigationButtons::Accept))
{
    // Accept is pressed
}

以下示例确定是否释放所需集中“接受”按钮。

if (RequiredUINavigationButtons::None == (reading.RequiredButtons & RequiredUINavigationButtons::Accept))
{
    // Accept is released (not pressed)
}

在读取可选集中按钮时,请务必使用OptionalButtons属性和OptionalUINavigationButtons枚举。

以下示例确定是否按下可选集中上下文 1 按钮。

if (OptionalUINavigationButtons::Context1 == (reading.OptionalButtons & OptionalUINavigationButtons::Context1))
{
    // Context 1 is pressed
}

有时,你可能想要确定按钮何时从按下过渡到按下或释放到按下,是按下还是释放多个按钮,或者是否按特定方式排列一组按钮,有些按钮未按下。 有关如何检测这些条件的信息,请参阅检测按钮切换检测复杂按钮排列

运行 UI 导航控制器示例

InputInterfacingUWP 示例 (github) 演示了不同的输入设备如何充当 UI 导航控制器。

另请参阅

Windows.Gaming.Input.GamepadWindows.Gaming.Input.ArcadeStickWindows.Gaming.Input.RacingWheelWindows.Gaming.Input.IGameController