Поделиться через


Сенсорные элементы управления для игр

Узнайте, как добавить основные сенсорные элементы управления в игру C++ универсальной платформы Windows (UWP) с помощью DirectX. Мы покажем, как добавить сенсорные элементы управления для перемещения камеры фиксированной плоскости в среде Direct3D, где перетаскивание с помощью пальца или пера сдвигает перспективу камеры.

Вы можете включить эти элементы управления в игры, если вы хотите, чтобы игрок перетаскивал для прокрутки или перемещал по трехмерной обстановке, например карта или игровое поле. Например, в стратегии или головоломке вы можете использовать эти элементы управления, чтобы позволить игроку просматривать игровую среду, большую, чем экран, передвигая влево или вправо.

Примечание Наш код также работает с элементами управления сдвигом с помощью мыши. Связанные с указателем события абстрагируются API среды выполнения Windows, поэтому они могут обрабатывать события указателя на основе касания или мыши.

 

Цели

  • Создайте простой элемент управления сенсорным перетаскиванием для перемещения камеры на фиксированной плоскости в игре DirectX.

Настройка базовой инфраструктуры событий сенсорного ввода

Сначала мы определим базовый тип контроллера , CameraPanController, в данном случае. Здесь мы определяем контроллер как абстрактную идею, набор поведения, который может выполнять пользователь.

Класс CameraPanController является регулярно обновляемой коллекцией сведений о состоянии контроллера камеры и предоставляет нашему приложению способ получения этих сведений из своего цикла обновления.

using namespace Windows::UI::Core;
using namespace Windows::System;
using namespace Windows::Foundation;
using namespace Windows::Devices::Input;
#include <directxmath.h>

// Methods to get input from the UI pointers
ref class CameraPanController
{
}

Теперь создадим заголовок, определяющий состояние контроллера камеры, а также основные методы и обработчики событий, реализующие взаимодействие контроллера камеры.

ref class CameraPanController
{
private:
    // Properties of the controller object
    DirectX::XMFLOAT3 m_position;               // the position of the camera

    // Properties of the camera pan control
    bool m_panInUse;                
    uint32 m_panPointerID;          
    DirectX::XMFLOAT2 m_panFirstDown;           
    DirectX::XMFLOAT2 m_panPointerPosition;   
    DirectX::XMFLOAT3 m_panCommand;         
    
internal:
    // Accessor to set the position of the controller
    void SetPosition( _In_ DirectX::XMFLOAT3 pos );

       // Accessor to set the fixed "look point" of the controller
       DirectX::XMFLOAT3 get_FixedLookPoint();

    // Returns the position of the controller object
    DirectX::XMFLOAT3 get_Position();

public:

    // Methods to get input from the UI pointers
    void OnPointerPressed(
        _In_ Windows::UI::Core::CoreWindow^ sender,
        _In_ Windows::UI::Core::PointerEventArgs^ args
        );

    void OnPointerMoved(
        _In_ Windows::UI::Core::CoreWindow^ sender,
        _In_ Windows::UI::Core::PointerEventArgs^ args
        );

    void OnPointerReleased(
        _In_ Windows::UI::Core::CoreWindow^ sender,
        _In_ Windows::UI::Core::PointerEventArgs^ args
        );

    // Set up the Controls supported by this controller
    void Initialize( _In_ Windows::UI::Core::CoreWindow^ window );

    void Update( Windows::UI::Core::CoreWindow ^window );

};  // Class CameraPanController

Частные поля содержат текущее состояние контроллера камеры. Давайте рассмотрим их.

  • m_position положение камеры в пространстве сцены. В этом примере значение z-координаты зафиксировано на 0. Для представления этого значения можно использовать DirectX::XMFLOAT2, но для целей этого примера и будущей расширяемости мы используем DirectX::XMFLOAT3. Мы передаем это значение через свойство get_Position самому приложению, чтобы он смог соответствующим образом обновить окно просмотра.
  • m_panInUse — логическое значение, указывающее, активна ли операция сдвига; то есть, если игрок касается экрана и перемещает камеру.
  • m_panPointerID является уникальным идентификатором указателя. Мы не будем использовать это в примере, но рекомендуется связать класс состояния контроллера с определенным указателем.
  • m_panFirstDown это точка на экране, где игрок сначала коснулся экрана или щелкнул мышью во время перемещения камеры. Мы используем это значение позже, чтобы задать мертвую зону, чтобы предотвратить дрожание при касании экрана, или если мышь встряхивает немного.
  • m_panPointerPosition — это точка на экране, где проигрыватель в настоящее время переместил указатель. Мы используем его, чтобы определить, в каком направлении игрок хотел бы двигаться, проверяя его относительно m_panFirstDown.
  • m_panCommand — это окончательная вычисленная команда для контроллера камеры: вверх, вниз, слева или справа. Поскольку мы работаем с камерой, зафиксированной на плоскости x-y, вместо этого может быть значение DirectX::XMFLOAT2.

Эти 3 обработчика событий используются для обновления сведений о состоянии контроллера камеры.

  • OnPointerPressed — это обработчик событий, который вызывается нашим приложением, когда игрок нажимает пальцем на сенсорную поверхность и указатель перемещается к координатам нажатия.
  • OnPointerMoved — это обработчик событий, который вызывается приложением, когда проигрыватель проводит пальцем по сенсорной поверхности. Он обновляется с новыми координатами траектории перетаскивания.
  • OnPointerReleased — это обработчик событий, который вызывается приложением, когда игрок убирает палец с сенсорной поверхности.

Наконец, мы используем эти методы и свойства для инициализации, доступа и обновления сведений о состоянии контроллера камеры.

  • Инициализация — это обработчик событий, который вызывает приложение для инициализации элементов управления и присоединения их к объекту CoreWindow, описывающего окно отображения.
  • SetPosition — это метод, который вызывается приложением для задания координат (x, y и z) элементов управления в пространстве сцены. Обратите внимание, что наша координата z составляет 0 в рамках этого руководства.
  • get_Position — это свойство, к которому обращается наше приложение, чтобы получить текущую позицию камеры в пространстве сцены. Это свойство используется в качестве способа взаимодействия текущей позиции камеры с приложением.
  • get_FixedLookPoint — это свойство, к которому обращается наше приложение, чтобы получить текущую точку, на которую направлена камера контроллера. В этом примере он заблокирован перпендикулярно плоскости x-y.
  • Обновление — это метод, который считывает состояние контроллера и обновляет положение камеры. Вы постоянно вызываете <и> из основного цикла приложения, чтобы обновить данные контроллера камеры и положение камеры в пространстве сцены.

Теперь у вас есть все компоненты, необходимые для реализации сенсорных элементов управления. Вы можете определить, когда и где произошли события касания или указателя мыши, а также то, что такое действие. Вы можете задать положение и ориентацию камеры относительно пространства сцены и отслеживать изменения. Наконец, вы можете связать новую позицию камеры с вызывающим приложением.

Теперь давайте соединим эти части вместе.

Создать основные события касания

Диспетчер событий среды выполнения Windows предоставляет 3 события, которые мы хотим, чтобы наше приложение обрабатывалось:

Эти события реализуются в типе CoreWindow. Мы предполагаем, что у вас есть объект CoreWindow для работы. Для получения дополнительной информации см. статью Как настроить ваше приложение UWP C++ для отображения вида DirectX.

Когда эти события срабатывают во время работы нашего приложения, обработчики обновляют информацию о состоянии контроллера камеры, определенную в наших закрытых полях.

Во-первых, давайте заполняем обработчики событий указателя касания. В первом обработчике событий OnPointerPressed мы получаем координаты указателя x-y из CoreWindow , который управляет отображением, когда пользователь прикасается к экрану или щелкает мышь.

OnPointerPressed

void CameraPanController::OnPointerPressed(
                                           _In_ CoreWindow^ sender,
                                           _In_ PointerEventArgs^ args)
{
    // Get the current pointer position.
    uint32 pointerID = args->CurrentPoint->PointerId;
    DirectX::XMFLOAT2 position = DirectX::XMFLOAT2( args->CurrentPoint->Position.X, args->CurrentPoint->Position.Y );

    auto device = args->CurrentPoint->PointerDevice;
    auto deviceType = device->PointerDeviceType;
    

       if ( !m_panInUse )   // If no pointer is in this control yet.
    {
       m_panFirstDown = position;                   // Save the location of the initial contact.
       m_panPointerPosition = position;
       m_panPointerID = pointerID;              // Store the id of the pointer using this control.
       m_panInUse = TRUE;
    }
    
}

Мы используем этот обработчик, чтобы сообщить текущему экземпляру CameraPanController , что контроллер камеры должен рассматриваться как активный, задав m_panInUse значение TRUE. Таким образом, когда приложение вызывает Update , оно будет использовать текущие данные положения для обновления окна просмотра.

Теперь, когда мы установили базовые значения для движения камеры при касании экрана или нажатии на кнопку в окне отображения, мы должны определить, что делать, когда пользователь перетаскивает касание по экрану или перемещает мышь, удерживая кнопку нажатой.

Обработчик событий OnPointerMoved срабатывает всякий раз, когда указатель перемещается, в каждый момент, когда пользователь перетаскивает его по экрану. Мы должны держать приложение в курсе текущего расположения указателя, и это то, как мы это делаем.

OnPointerMoved

void CameraPanController::OnPointerMoved(
                                        _In_ CoreWindow ^sender,
                                        _In_ PointerEventArgs ^args)
{
    uint32 pointerID = args->CurrentPoint->PointerId;
    DirectX::XMFLOAT2 position = DirectX::XMFLOAT2( args->CurrentPoint->Position.X, args->CurrentPoint->Position.Y );

    m_panPointerPosition = position;
}

Наконец, необходимо отключить функцию перемещения камеры, когда игрок перестает касаться экрана. Мы используем OnPointerReleased, которая вызывается при срабатывании PointerReleased, чтобы задать m_panInUse в значение FALSE, отключить перемещение камеры и установить идентификатор указателя равным 0.

OnPointerReleased

void CameraPanController::OnPointerReleased(
                                             _In_ CoreWindow ^sender,
                                             _In_ PointerEventArgs ^args)
{
    uint32 pointerID = args->CurrentPoint->PointerId;
    DirectX::XMFLOAT2 position = DirectX::XMFLOAT2( args->CurrentPoint->Position.X, args->CurrentPoint->Position.Y );

    m_panInUse = FALSE;
    m_panPointerID = 0;
}

Инициализация сенсорных элементов управления и состояния контроллера

Давайте подключим события и инициализируем все базовые поля состояния контроллера камеры.

инициализация

void CameraPanController::Initialize( _In_ CoreWindow^ window )
{

    // Start receiving touch/mouse events.
    window->PointerPressed += 
    ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &CameraPanController::OnPointerPressed);

    window->PointerMoved += 
    ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &CameraPanController::OnPointerMoved);

    window->PointerReleased += 
    ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &CameraPanController::OnPointerReleased);


    // Initialize the state of the controller.
    m_panInUse = FALSE;             
    m_panPointerID = 0;

    //  Initialize this as it is reset on every frame.
    m_panCommand = DirectX::XMFLOAT3( 0.0f, 0.0f, 0.0f );

}

Initialize принимает ссылку на экземпляр приложения CoreWindow в качестве параметра и регистрирует созданные нами обработчики событий для соответствующих событий CoreWindow.

Получение и настройка положения контроллера камеры

Давайте определим некоторые методы, чтобы получить и задать положение контроллера камеры в пространстве сцены.

void CameraPanController::SetPosition( _In_ DirectX::XMFLOAT3 pos )
{
    m_position = pos;
}

// Returns the position of the controller object
DirectX::XMFLOAT3 CameraPanController::get_Position()
{
    return m_position;
}

DirectX::XMFLOAT3 CameraPanController::get_FixedLookPoint()
{
    // For this sample, we don't need to use the trig functions because our
    // look point is fixed. 
    DirectX::XMFLOAT3 result= m_position;
    result.z += 1.0f;
    return result;    

}

SetPosition — это общедоступный метод, который можно вызвать из нашего приложения, если необходимо установить положение контроллера камеры в определенную точку.

get_Position является нашим самым важным общедоступным свойством: это то, как наше приложение получает текущее положение контроллера камеры в пространстве сцены, чтобы он смог соответствующим образом обновить окно просмотра.

get_FixedLookPoint является общедоступным свойством, которое в этом примере получает точку обзора, перпендикулярную плоскости x-y. Этот метод можно изменить, чтобы использовать тригонометрические функции, синус и косинус, при вычислении значений координат x, y и z, если вы хотите создать более наклонные углы для фиксированной камеры.

Обновление сведений о состоянии контроллера камеры

Теперь мы выполняем наши вычисления, которые преобразуют данные о координатах указателя, отслеживаемые в m_panPointerPosition, в новые координатные данные относительно нашего 3D-пространства сцены. Наше приложение вызывает этот метод каждый раз, когда мы обновляем цикл основного приложения. В нем вычисляется новая информация о позиции, которую мы хотим передать приложению, которое используется для обновления матрицы представления перед проекцией в окно просмотра.


void CameraPanController::Update( CoreWindow ^window )
{
    if ( m_panInUse )
    {
        pointerDelta.x = m_panPointerPosition.x - m_panFirstDown.x;
        pointerDelta.y = m_panPointerPosition.y - m_panFirstDown.y;

        if ( pointerDelta.x > 16.0f )        // Leave 32 pixel-wide dead spot for being still.
            m_panCommand.x += 1.0f;
        else
            if ( pointerDelta.x < -16.0f )
                m_panCommand.x += -1.0f;

        if ( pointerDelta.y > 16.0f )        
            m_panCommand.y += 1.0f;
        else
            if (pointerDelta.y < -16.0f )
                m_panCommand.y += -1.0f;
    }

       DirectX::XMFLOAT3 command = m_panCommand;
   
    // Our velocity is based on the command.
    DirectX::XMFLOAT3 Velocity;
    Velocity.x =  command.x;
    Velocity.y =  command.y;
    Velocity.z =  0.0f;

    // Integrate
    m_position.x = m_position.x + Velocity.x;
    m_position.y = m_position.y + Velocity.y;
    m_position.z = m_position.z + Velocity.z;

    // Clear the movement input accumulator for use during the next frame.
    m_panCommand = DirectX::XMFLOAT3( 0.0f, 0.0f, 0.0f );

}

Поскольку мы не хотим, чтобы подергивания от сенсора или мыши делали панорамирование камеры рывками, мы задаем мертвую зону вокруг указателя диаметром 32 пикселя. У нас также есть значение скорости, которое в данном случае равно 1:1 с обходом пикселя указателя мимо мертвой зоны. Это поведение можно изменить, чтобы замедлить или ускорить скорость движения.

Обновление матрицы просмотра с помощью новой позиции камеры

Теперь мы можем получить координату пространства сцены, на которую ориентирована наша камера, и которая обновляется всякий раз, когда вы сообщаете приложению сделать это (каждые 60 секунд в основном цикле приложения, например). Этот псевдокод предлагает поведение вызова, который можно реализовать:

 myCameraPanController->Update( m_window ); 

 // Update the view matrix based on the camera position.
 myCamera->MyMethodToComputeViewMatrix(
        myController->get_Position(),        // The position in the 3D scene space.
        myController->get_FixedLookPoint(),      // The point in the space we are looking at.
        DirectX::XMFLOAT3( 0, 1, 0 )                    // The axis that is "up" in our space.
        );  

Поздравляю! Вы реализовали простой набор элементов управления касанием камеры в игре.