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


Элементы управления move-look для игр

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

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

Если это незнакомая концепция управления для вас, подумайте об этом следующим образом: клавиатура (или сенсорное поле ввода) управляет ноги в этом трехмерном пространстве, и ведет себя так, как если бы ваши ноги были только способны двигаться вперед или назад, или ремешки слева и справа. Указатель мыши (или сенсорный указатель) управляет головой. Вы используете голову, чтобы посмотреть в направлении - слева или вправо, вверх или вниз, или где-то в этом плоскости. Если в вашем представлении есть целевой объект, вы будете использовать мышь для центра просмотра камеры на этом целевом объекте, а затем нажмите клавишу вперед, чтобы перейти к нему, или вернуться, чтобы отойти от него. Чтобы обвести целевой объект, вы будете держать представление камеры в центре целевого объекта и перемещаться влево или вправо одновременно. Вы можете увидеть, как это очень эффективный метод управления для навигации по трехмерным средам!

Эти элементы управления обычно называются элементами управления WASD в играх, где клавиши W, A, S и D используются для фиксированного перемещения камеры на плоскости x-z, а мышь используется для управления поворотом камеры вокруг осей x и y.

Задачи

  • Добавьте в игру DirectX базовые элементы управления перемещением для мыши и клавиатуры и сенсорных экранов.
  • Реализуйте камеру первого лица, используемую для навигации по трехмерной среде.

Примечание о реализации сенсорного управления

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

Наш экран выглядит следующим образом.

Макет контроллера перемещения

При перемещении сенсорного указателя (а не мыши!) в левом нижнем левом краю экрана любое движение вверх сделает камеру двигаться вперед. Любое движение вниз приведет к перемещению камеры назад. То же самое сохраняется для перемещения в левое и правое пространство указателя контроллера перемещения. За пределами этого пространства, и он становится контроллером взгляда - вы просто коснитесь или перетащите камеру в место, где вы хотите, чтобы он был лицом.

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

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

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 MoveLookController
{
};  // class MoveLookController

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

#define ROTATION_GAIN 0.004f    // Sensitivity adjustment for the look controller
#define MOVEMENT_GAIN 0.1f      // Sensitivity adjustment for the move controller

ref class MoveLookController
{
private:
    // Properties of the controller object
    DirectX::XMFLOAT3 m_position;               // The position of the controller
    float m_pitch, m_yaw;           // Orientation euler angles in radians

    // Properties of the Move control
    bool m_moveInUse;               // Specifies whether the move control is in use
    uint32 m_movePointerID;         // Id of the pointer in this control
    DirectX::XMFLOAT2 m_moveFirstDown;          // Point where initial contact occurred
    DirectX::XMFLOAT2 m_movePointerPosition;   // Point where the move pointer is currently located
    DirectX::XMFLOAT3 m_moveCommand;            // The net command from the move control

    // Properties of the Look control
    bool m_lookInUse;               // Specifies whether the look control is in use
    uint32 m_lookPointerID;         // Id of the pointer in this control
    DirectX::XMFLOAT2 m_lookLastPoint;          // Last point (from last frame)
    DirectX::XMFLOAT2 m_lookLastDelta;          // For smoothing

    bool m_forward, m_back;         // States for movement
    bool m_left, m_right;
    bool m_up, m_down;


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
        );

    void OnKeyDown(
        _In_ Windows::UI::Core::CoreWindow^ sender,
        _In_ Windows::UI::Core::KeyEventArgs^ args
        );

    void OnKeyUp(
        _In_ Windows::UI::Core::CoreWindow^ sender,
        _In_ Windows::UI::Core::KeyEventArgs^ args
        );

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

    void Update( Windows::UI::Core::CoreWindow ^window );
    
internal:
    // Accessor to set position of controller
    void SetPosition( _In_ DirectX::XMFLOAT3 pos );

    // Accessor to set position of controller
    void SetOrientation( _In_ float pitch, _In_ float yaw );

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

    // Returns the point  which the controller is facing
    DirectX::XMFLOAT3 get_LookPoint();


};  // class MoveLookController

Наш код содержит 4 группы частных полей. Давайте рассмотрим цель каждого из них.

Во-первых, мы определяем некоторые полезные поля, которые содержат обновленные сведения о представлении камеры.

  • m_position — это позиция камеры (и, следовательно, план просмотра) в трехмерной сцене с помощью координат сцены.
  • m_pitch — это шаг камеры или его поворот вверх по оси x-плана в радианах.
  • m_yaw является рывом камеры или его левым вправо поворотом вокруг оси y в плане просмотра в радианах.

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

  • m_moveInUse указывает, используется ли контроллер перемещения.
  • m_movePointerID — это уникальный идентификатор текущего указателя перемещения. Мы используем его для различения указателя взгляда и указателя перемещения при проверке значения идентификатора указателя.
  • m_moveFirstDown — это точка на экране, где проигрыватель впервые коснулся области указателя контроллера перемещения. Мы используем это значение позже, чтобы задать мертвую зону, чтобы сохранить крошечные движения от смещение представления.
  • m_movePointerPosition — это точка на экране, на который проигрыватель в настоящее время переместил указатель. Мы используем его, чтобы определить, в каком направлении игрок хотел двигаться, проверив его относительно m_moveFirstDown.
  • m_moveCommand — это окончательная вычисленная команда для контроллера перемещения: вверх (вперед), вниз (назад), слева или справа.

Теперь мы определим поля, которые мы используем для контроллера поиска, как мышь, так и сенсорные реализации.

  • m_lookInUse указывает, используется ли элемент управления look.
  • m_lookPointerID — это уникальный идентификатор текущего указателя внешнего вида. Мы используем его для различения указателя взгляда и указателя перемещения при проверке значения идентификатора указателя.
  • m_lookLastPoint является последней точкой в координатах сцены, которая была записана в предыдущем кадре.
  • m_lookLastDelta вычисляется разница между текущим m_position и m_lookLastPoint.

Наконец, мы определим 6 логических значений для 6 градусов движения, которые мы используем для указания текущего состояния каждого действия направления перемещения (включено или выключение):

  • m_forward, m_back, m_left, m_right, m_up и m_down.

Мы используем 6 обработчиков событий для записи входных данных, которые мы используем для обновления состояния наших контроллеров:

  • OnPointerPressed. Игрок нажимал левую кнопку мыши с указателем на экране игры или касался экрана.
  • OnPointerMoved. Игрок переместил мышь с указателем на экране игры или перетащил сенсорный указатель на экран.
  • OnPointerReleased. Проигрыватель выпустил левую кнопку мыши с указателем на экране игры или перестал касаться экрана.
  • OnKeyDown. Игрок нажимал клавишу.
  • OnKeyUp. Проигрыватель выпустил ключ.

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

  • Инициализация. Приложение вызывает этот обработчик событий для инициализации элементов управления и присоединения их к объекту CoreWindow , описывающего окно отображения.
  • SetPosition. Наше приложение вызывает этот метод, чтобы задать координаты (x, y и z) наших элементов управления в пространстве сцены.
  • SetOrientation. Наше приложение вызывает этот метод, чтобы задать шаг и рывок камеры.
  • get_Position. Наше приложение обращается к этому свойству, чтобы получить текущее положение камеры в пространстве сцены. Это свойство используется в качестве метода связи текущей позиции камеры с приложением.
  • get_LookPoint. Наше приложение обращается к этому свойству, чтобы получить текущую точку, к которой сталкивается камера контроллера.
  • Обновление. Считывает состояние контроллеров перемещения и просмотра и обновляет положение камеры. Этот метод постоянно вызывается из основного цикла приложения, чтобы обновить данные контроллера камеры и положение камеры в пространстве сцены.

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

Создание основных событий ввода

Диспетчер событий среда выполнения Windows предоставляет 5 событий, которые требуется обрабатывать экземпляры класса MoveLookController:

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

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

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

OnPointerPressed

void MoveLookController::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 ( deviceType == PointerDeviceType::Mouse )
    {
        // Action, Jump, or Fire
    }

    // Check  if this pointer is in the move control.
    // Change the values  to percentages of the preferred screen resolution.
    // You can set the x value to <preferred resolution> * <percentage of width>
    // for example, ( position.x < (screenResolution.x * 0.15) ).

    if (( position.x < 300 && position.y > 380 ) && ( deviceType != PointerDeviceType::Mouse ))
    {
        if ( !m_moveInUse ) // if no pointer is in this control yet
        {
            // Process a DPad touch down event.
            m_moveFirstDown = position;                 // Save the location of the initial contact.
            m_movePointerPosition = position;
            m_movePointerID = pointerID;                // Store the id of the pointer using this control.
            m_moveInUse = TRUE;
        }
    }
    else // This pointer must be in the look control.
    {
        if ( !m_lookInUse ) // If no pointer is in this control yet...
        {
            m_lookLastPoint = position;                         // save the point for later move
            m_lookPointerID = args->CurrentPoint->PointerId;  // store the id of pointer using this control
            m_lookLastDelta.x = m_lookLastDelta.y = 0;          // these are for smoothing
            m_lookInUse = TRUE;
        }
    }
}

Этот обработчик событий проверяет, является ли указатель не мышью (в целях этого примера, который поддерживает мышь и сенсорный ввод) и находится ли он в области контроллера перемещения. Если оба критерия являются истинными, он проверяет, был ли указатель только что нажат, в частности, независимо от того, не связан ли этот щелчок с предыдущим перемещением или выглядеть входными данными, проверив , является ли m_moveInUse false. Если это так, обработчик захватывает точку в области контроллера перемещения, где произошло нажатие, и задает m_moveInUse значение true, чтобы при повторном вызове этого обработчика не перезаписывал начальную позицию входного взаимодействия контроллера перемещения. Он также обновляет идентификатор указателя контроллера перемещения до идентификатора текущего указателя.

Если указатель является мышью или если сенсорный указатель не находится в области контроллера перемещения, он должен находиться в области контроллера просмотра. Он задает m_lookLastPoint текущей позиции, в которой пользователь нажимал кнопку мыши или касался и нажимал, сбрасывает разностное значение и обновляет идентификатор указателя контроллера взгляда до текущего идентификатора указателя. Он также задает состояние контроллера внешнего вида для активного.

OnPointerMoved

void MoveLookController::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);

    // Decide which control this pointer is operating.
    if (pointerID == m_movePointerID)           // This is the move pointer.
    {
        // Move control
        m_movePointerPosition = position;       // Save the current position.

    }
    else if (pointerID == m_lookPointerID)      // This is the look pointer.
    {
        // Look control

        DirectX::XMFLOAT2 pointerDelta;
        pointerDelta.x = position.x - m_lookLastPoint.x;        // How far did pointer move
        pointerDelta.y = position.y - m_lookLastPoint.y;

        DirectX::XMFLOAT2 rotationDelta;
        rotationDelta.x = pointerDelta.x * ROTATION_GAIN;   // Scale for control sensitivity.
        rotationDelta.y = pointerDelta.y * ROTATION_GAIN;

        m_lookLastPoint = position;                     // Save for the next time through.

                                                        // Update our orientation based on the command.
        m_pitch -= rotationDelta.y;                     // Mouse y increases down, but pitch increases up.
        m_yaw -= rotationDelta.x;                       // Yaw is defined as CCW around the y-axis.

                                                        // Limit the pitch to straight up or straight down.
        m_pitch = (float)__max(-DirectX::XM_PI / 2.0f, m_pitch);
        m_pitch = (float)__min(+DirectX::XM_PI / 2.0f, m_pitch);
    }
}

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

Если это контроллер перемещения, мы просто обновим положение указателя. Мы продолжаем обновлять его до тех пор, пока событие PointerMoved продолжает стрелять, так как мы хотим сравнить окончательную позицию с первой, которую мы захватили с обработчиком событий OnPointerPressed.

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

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

OnPointerReleased

void MoveLookController::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 );


    if ( pointerID == m_movePointerID )    // This was the move pointer.
    {
        m_moveInUse = FALSE;
        m_movePointerID = 0;
    }
    else if (pointerID == m_lookPointerID ) // This was the look pointer.
    {
        m_lookInUse = FALSE;
        m_lookPointerID = 0;
    }
}

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

OnKeyDown

void MoveLookController::OnKeyDown(
                                   __in CoreWindow^ sender,
                                   __in KeyEventArgs^ args )
{
    Windows::System::VirtualKey Key;
    Key = args->VirtualKey;

    // Figure out the command from the keyboard.
    if ( Key == VirtualKey::W )     // Forward
        m_forward = true;
    if ( Key == VirtualKey::S )     // Back
        m_back = true;
    if ( Key == VirtualKey::A )     // Left
        m_left = true;
    if ( Key == VirtualKey::D )     // Right
        m_right = true;
}

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

OnKeyUp

void MoveLookController::OnKeyUp(
                                 __in CoreWindow^ sender,
                                 __in KeyEventArgs^ args)
{
    Windows::System::VirtualKey Key;
    Key = args->VirtualKey;

    // Figure out the command from the keyboard.
    if ( Key == VirtualKey::W )     // forward
        m_forward = false;
    if ( Key == VirtualKey::S )     // back
        m_back = false;
    if ( Key == VirtualKey::A )     // left
        m_left = false;
    if ( Key == VirtualKey::D )     // right
        m_right = false;
}

И когда ключ освобождается, этот обработчик событий возвращает значение false. При вызове update он проверяет эти состояния направления перемещения и перемещает камеру соответствующим образом. Это немного проще, чем реализация сенсорного ввода!

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

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

Initialize

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

    // Opt in to receive touch/mouse events.
    window->PointerPressed += 
    ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &MoveLookController::OnPointerPressed);

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

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

    window->CharacterReceived +=
    ref new TypedEventHandler<CoreWindow^, CharacterReceivedEventArgs^>(this, &MoveLookController::OnCharacterReceived);

    window->KeyDown += 
    ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &MoveLookController::OnKeyDown);

    window->KeyUp += 
    ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &MoveLookController::OnKeyUp);

    // Initialize the state of the controller.
    m_moveInUse = FALSE;                // No pointer is in the Move control.
    m_movePointerID = 0;

    m_lookInUse = FALSE;                // No pointer is in the Look control.
    m_lookPointerID = 0;

    //  Need to init this as it is reset every frame.
    m_moveCommand = DirectX::XMFLOAT3( 0.0f, 0.0f, 0.0f );

    SetOrientation( 0, 0 );             // Look straight ahead when the app starts.

}

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

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

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

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

// Accessor to set the position of the controller.
void MoveLookController::SetOrientation( _In_ float pitch, _In_ float yaw )
{
    m_pitch = pitch;
    m_yaw = yaw;
}

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

// Returns the point at which the camera controller is facing.
DirectX::XMFLOAT3 MoveLookController::get_LookPoint()
{
    float y = sinf(m_pitch);        // Vertical
    float r = cosf(m_pitch);        // In the plane
    float z = r*cosf(m_yaw);        // Fwd-back
    float x = r*sinf(m_yaw);        // Left-right
    DirectX::XMFLOAT3 result(x,y,z);
    result.x += m_position.x;
    result.y += m_position.y;
    result.z += m_position.z;

    // Return m_position + DirectX::XMFLOAT3(x, y, z);
    return result;
}

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

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

void MoveLookController::Update(CoreWindow ^window)
{
    // Check for input from the Move control.
    if (m_moveInUse)
    {
        DirectX::XMFLOAT2 pointerDelta(m_movePointerPosition);
        pointerDelta.x -= m_moveFirstDown.x;
        pointerDelta.y -= m_moveFirstDown.y;

        // Figure out the command from the touch-based virtual joystick.
        if (pointerDelta.x > 16.0f)      // Leave 32 pixel-wide dead spot for being still.
            m_moveCommand.x = 1.0f;
        else
            if (pointerDelta.x < -16.0f)
            m_moveCommand.x = -1.0f;

        if (pointerDelta.y > 16.0f)      // Joystick y is up, so change sign.
            m_moveCommand.y = -1.0f;
        else
            if (pointerDelta.y < -16.0f)
            m_moveCommand.y = 1.0f;
    }

    // Poll our state bits that are set by the keyboard input events.
    if (m_forward)
        m_moveCommand.y += 1.0f;
    if (m_back)
        m_moveCommand.y -= 1.0f;

    if (m_left)
        m_moveCommand.x -= 1.0f;
    if (m_right)
        m_moveCommand.x += 1.0f;

    if (m_up)
        m_moveCommand.z += 1.0f;
    if (m_down)
        m_moveCommand.z -= 1.0f;

    // Make sure that 45 degree cases are not faster.
    DirectX::XMFLOAT3 command = m_moveCommand;
    DirectX::XMVECTOR vector;
    vector = DirectX::XMLoadFloat3(&command);

    if (fabsf(command.x) > 0.1f || fabsf(command.y) > 0.1f || fabsf(command.z) > 0.1f)
    {
        vector = DirectX::XMVector3Normalize(vector);
        DirectX::XMStoreFloat3(&command, vector);
    }
    

    // Rotate command to align with our direction (world coordinates).
    DirectX::XMFLOAT3 wCommand;
    wCommand.x = command.x*cosf(m_yaw) - command.y*sinf(m_yaw);
    wCommand.y = command.x*sinf(m_yaw) + command.y*cosf(m_yaw);
    wCommand.z = command.z;

    // Scale for sensitivity adjustment.
    wCommand.x = wCommand.x * MOVEMENT_GAIN;
    wCommand.y = wCommand.y * MOVEMENT_GAIN;
    wCommand.z = wCommand.z * MOVEMENT_GAIN;

    // Our velocity is based on the command.
    // Also note that y is the up-down axis. 
    DirectX::XMFLOAT3 Velocity;
    Velocity.x = -wCommand.x;
    Velocity.z = wCommand.y;
    Velocity.y = wCommand.z;

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

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

}

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

При вычислении скорости мы также преобразуем координаты, полученные от перемещения и просмотра контроллеров, в движение фактической точки просмотра, которую мы отправим в метод, вычисляющий матрицу представления для сцены. Во-первых, мы превратим координату x, так как если щелкнуть или перетащить влево или вправо с контроллером взгляда, точка просмотра поворачивается в противоположном направлении в сцене, так как камера может качнуться вокруг своей центральной оси. Затем мы переключим оси y и z, так как нажатие клавиши вверх/вниз или перемещение сенсорного перетаскивания (считывается как поведение оси Y) на контроллере перемещения должно быть преобразовано в действие камеры, которое перемещает точку обзора в экран или из экрана (ось z).

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

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

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

myMoveLookController->Update( m_window );   

// Update the view matrix based on the camera position.
myFirstPersonCamera->SetViewParameters(
                 myMoveLookController->get_Position(),       // Point we are at
                 myMoveLookController->get_LookPoint(),      // Point to look towards
                 DirectX::XMFLOAT3( 0, 1, 0 )                   // Up-vector
                 ); 

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