键盘、鼠标和控制器输入 (C++)

本文与旧版 WinRT 原生 API 相关。 对于新的本机应用项目,建议使用 OpenXR API

本文中的代码片段当前演示了如何使用 C++/CX,而不是 C++17 兼容的 C++/WinRT,后者在 C++ 全息项目模板中使用。 这些概念与 C++/WinRT 项目等同,但将需要转换代码。

订阅 CoreWindow 输入事件

键盘输入

和其他任何 UWP 应用一样,在 Windows 全息应用模板中包含一个用于键盘输入的事件处理程序。 应用在 Windows Mixed Reality 中以相同的方式使用键盘输入数据。

从 AppView.cpp:

// Register for keypress notifications.
   window->KeyDown +=
       ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &AppView::OnKeyPressed);

    …

   // Input event handlers

   void AppView::OnKeyPressed(CoreWindow^ sender, KeyEventArgs^ args)
   {
       //
       // TODO: Respond to keyboard input here.
       //
   }

虚拟键盘输入

对于沉浸式桌面头戴显示设备,可以通过实现 CoreTextEditContext 来支持 Windows 在沉浸式视图上呈现的虚拟键盘。 这使 Windows 了解应用呈现文本框的状态,因此虚拟键盘可以正确地参与到文本中。

有关实现 CoreTextEditContext 支持的详细信息,请参阅 CoreTextEditContext 示例

鼠标输入

还可以通过 UWP CoreWindow 输入事件处理程序再次使用鼠标输入。 下面介绍如何修改 Windows 全息应用程模板,使其以与按下手势相同的方式支持鼠标单击。 进行此修改后,在戴上沉浸式头戴显示设备时,单击鼠标将重新定位该多维数据集。

注意

UWP 应用还可以通过使用 MouseDevice API 获取鼠标的原始 XY 数据。

首先在 AppView 中声明新的 OnPointerPressed 处理程序:

protected:
       void OnPointerPressed(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::PointerEventArgs^ args);

在 AppView.cpp 中,将以下代码添加到 SetWindow:

// Register for pointer pressed notifications.
   window->PointerPressed +=
       ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &AppView::OnPointerPressed);

然后将此定义 OnPointerPressed 到文件底部:

void AppView::OnPointerPressed(CoreWindow^ sender, PointerEventArgs^ args)
   {
       // Allow the user to interact with the holographic world using the mouse.
       if (m_main != nullptr)
       {
           m_main->OnPointerPressed();
       }
   }

我们刚刚添加的事件处理程序是模板主类的直通。 让我们修改主类以支持此直通。 将此公用方法声明添加到标头文件中:

// Handle mouse input.
       void OnPointerPressed();

还需要此私有成员变量:

// Keep track of mouse input.
       bool m_pointerPressed = false;

最后,我们将利用新的逻辑更新主类,以支持鼠标单击。 首先添加此事件处理程序。 请确保更新类名称:

void MyHolographicAppMain::OnPointerPressed()
   {
       m_pointerPressed = true;
   }

现在,在 Update 方法中,将获取指针姿势的现有逻辑替换为以下内容:

SpatialInteractionSourceState^ pointerState = m_spatialInputHandler->CheckForInput();
   SpatialPointerPose^ pose = nullptr;
   if (pointerState != nullptr)
   {
       pose = pointerState->TryGetPointerPose(currentCoordinateSystem);
   }
   else if (m_pointerPressed)
   {
       pose = SpatialPointerPose::TryGetAtTimestamp(currentCoordinateSystem, prediction->Timestamp);
   }
   m_pointerPressed = false;

重新编译和重新部署。 请注意,现在鼠标单击将在沉浸式头戴显示设备中重新定位到立方体,或连接了蓝牙鼠标的 HoloLens。

游戏控制器支持

游戏控制器允许用户控制沉浸式 Windows Mixed Reality,是一种有趣且方便的体验方式。

将以下私有成员声明添加到主文件的标头类:

// Recognize gamepads that are plugged in after the app starts.
       void OnGamepadAdded(Platform::Object^, Windows::Gaming::Input::Gamepad^ args);
// Stop looking for gamepads that are unplugged.
       void OnGamepadRemoved(Platform::Object^, Windows::Gaming::Input::Gamepad^ args);
Windows::Foundation::EventRegistrationToken                     m_gamepadAddedEventToken;
       Windows::Foundation::EventRegistrationToken                     m_gamepadRemovedEventToken;
// Keeps track of a gamepad and the state of its A button.
       struct GamepadWithButtonState
       {
           Windows::Gaming::Input::Gamepad^ gamepad;
           bool buttonAWasPressedLastFrame = false;
       };
       std::vector<GamepadWithButtonState>                             m_gamepads;

在主类的构造函数中初始化游戏板事件,以及当前附加的任何游戏板:

// If connected, a game controller can also be used for input.
   m_gamepadAddedEventToken = Gamepad::GamepadAdded +=
       ref new EventHandler<Gamepad^>(
           bind(&$safeprojectname$Main::OnGamepadAdded, this, _1, _2)
           );
m_gamepadRemovedEventToken = Gamepad::GamepadRemoved +=
       ref new EventHandler<Gamepad^>(
           bind(&$safeprojectname$Main::OnGamepadRemoved, this, _1, _2)
           );
for (auto const& gamepad : Gamepad::Gamepads)
   {
       OnGamepadAdded(nullptr, gamepad);
   }

将此类事件处理程序添加到主类。 请确保更新类名称:

void MyHolographicAppMain::OnGamepadAdded(Object^, Gamepad^ args)
   {
       for (auto const& gamepadWithButtonState : m_gamepads)
       {
           if (args == gamepadWithButtonState.gamepad)
           {
               // This gamepad is already in the list.
               return;
           }
       }
       m_gamepads.push_back({ args, false });
   }
void MyHolographicAppMain::OnGamepadRemoved(Object^, Gamepad^ args)
   {
       m_gamepads.erase(
           std::remove_if(m_gamepads.begin(), m_gamepads.end(), [&](GamepadWithButtonState& gamepadWithState)
               {
                   return gamepadWithState.gamepad == args;
               }),
           m_gamepads.end());
   }

最后,更新输入逻辑,以识别控制器状态的更改。 此处,我们使用上一节中讨论的相同 m_pointerPressed 变量来添加鼠标事件。 将此添加到 Update 方法,在检查 SpatialPointerPose 之前:

// Check for new input state since the last frame.
   for (auto& gamepadWithButtonState : m_gamepads)
   {
       bool buttonDownThisUpdate = ((gamepadWithButtonState.gamepad->GetCurrentReading().Buttons & GamepadButtons::A) == GamepadButtons::A);
       if (buttonDownThisUpdate && !gamepadWithButtonState.buttonAWasPressedLastFrame)
       {
           m_pointerPressed = true;
       }
       gamepadWithButtonState.buttonAWasPressedLastFrame = buttonDownThisUpdate;
   }
// For context.
   SpatialInteractionSourceState^ pointerState = m_spatialInputHandler->CheckForInput();
   SpatialPointerPose^ pose = nullptr;
   if (pointerState != nullptr)
   {
       pose = pointerState->TryGetPointerPose(currentCoordinateSystem);
   }
   else if (m_pointerPressed)
   {
       pose = SpatialPointerPose::TryGetAtTimestamp(currentCoordinateSystem, prediction->Timestamp);
   }
   m_pointerPressed = false;

清理主类时,记得取消注册事件:

if (m_gamepadAddedEventToken.Value != 0)
   {
       Gamepad::GamepadAdded -= m_gamepadAddedEventToken;
   }
   if (m_gamepadRemovedEventToken.Value != 0)
   {
       Gamepad::GamepadRemoved -= m_gamepadRemovedEventToken;
   }

重新编译和重新部署。 现在可以附加或配对游戏控制器,并使用它来重新定位旋转立方体。

另请参阅