Sdílet prostřednictvím


ovládací prvky pohybu a pohledu pro hry

Naučte se do hry DirectX přidávat tradiční ovládací prvky pro pohyb myši a klávesnice (označované také jako ovládací prvky mouselook).

Probereme také podporu dotykového ovládání pro dotyková zařízení s ovladačem pro přesunutí definovaným v levé dolní části obrazovky, který se chová jako směrový vstup, a ovladačem pohledu definovaným pro zbytek obrazovky, přičemž se fotoaparát soustředí na poslední místo na obrazovce, kde se hráč dotkl.

Pokud je tento koncept ovládání pro vás neznámý, představte si to takto: klávesnice (nebo dotykové navigační pole) ovládá vaše nohy v tomto 3D prostoru a chová se, jako by vaše nohy mohly pohybovat pouze dopředu nebo dozadu, nebo se pohybovat do stran vlevo a vpravo. Myš (nebo dotykový ukazatel) řídí vaši hlavu. Pomocí hlavy se podíváte doleva nebo doprava, nahoru nebo dolů, nebo někam v této rovině. Pokud je v zobrazení cíl, použijte myš k tomu, abyste zaměřili zobrazení kamery na tento cíl, a poté stiskněte klávesu vpřed, abyste se k němu přiblížili, nebo zpět, abyste se od něj vzdálili. Pokud chcete obejít cíl, nechte kameru soustředěnou na cíli a současně se pohybujte vlevo nebo vpravo. Můžete vidět, jak se jedná o velmi efektivní metodu řízení pro navigaci v 3D prostředích!

Tyto ovládací prvky se běžně označují jako ovládací prvky WASD při hraní, kde se klávesy W, A, S a D používají pro pohyb kamery s pevnou kamerou x-z a myš se používá k ovládání otáčení kamery kolem os x a y.

Cíle

  • Přidejte do hry DirectX základní ovládací prvky pro pohybový vzhled pro myš i klávesnici a dotykové obrazovky.
  • Implementujte kameru první osoby, která se používá k navigaci v 3D prostředí.

Poznámka o implementacích dotykového ovládání

U dotykových ovládacích prvků implementujeme dva ovladače: pohybový ovladač, který zpracovává pohyb v rovině x-z vzhledem k bodu pohledu kamery; a ovladač pohledu, který cíluje bod pohledu kamery. Náš ovladač pohybu se přiřazuje k tlačítkům klávesnice WASD a ovladač pohledu se přiřazuje k myši. U dotykových ovládacích prvků ale musíme definovat oblast obrazovky, která slouží jako směrové vstupy, nebo virtuální tlačítka WASD, přičemž zbytek obrazovky slouží jako vstupní prostor pro ovládací prvky vzhledu.

Naše obrazovka vypadá takto.

rozložení ovládání pohybu a pohledu

Když přesunete ukazatel dotykového ovládání (ne myš!) v levém dolním rohu obrazovky, přesune se kamera dopředu. Všechny pohyby směrem dolů způsobí, že se kamera posune dozadu. Totéž platí pro pohyb doleva a doprava uvnitř prostoru ukazatele na ovladači přesunutí. Mimo tuto oblast se stane ovladačem pohledu – stačí dotykem nebo přetažením přesunout kameru tam, kam chcete, aby se dívala.

Nastavení základní infrastruktury vstupních událostí

Nejprve musíme vytvořit naši třídu ovládacích prvků, kterou používáme ke zpracování vstupních událostí z myši a klávesnice, a aktualizovat perspektivu kamery na základě daného vstupu. Vzhledem k tomu, že implementujeme ovládací prvky pro pohyb a pohled, nazýváme ho 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

Teď vytvoříme hlavičku, která definuje stav ovladače pohybu a pohledu a kameru z pohledu první osoby, plus základní metody a zpracovatele událostí, které implementují ovládací prvky a aktualizují stav kamery.

#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

Náš kód obsahuje 4 skupiny privátních polí. Pojďme se podívat na účel každého z nich.

Nejprve definujeme některá užitečná pole, která obsahují aktualizované informace o zobrazení fotoaparátu.

  • m_position je umístění kamery (a proto roviny zobrazení) ve 3D scéně pomocí souřadnic scény.
  • m_pitch je natočení kamery nebo její otočení nahoru dolů kolem osy x zorné roviny v radiánech.
  • m_yaw je úhel natočení kamery, nebo její rotace zleva doprava kolem osy y pohledu, v radiánech.

Teď pojďme definovat pole, která používáme k ukládání informací o stavu a pozici kontrolerů. Nejprve definujeme pole, která potřebujeme pro ovladač pro přesunutí na základě dotykového ovládání. Pro implementaci ovladače pohybu není potřeba nic zvláštního. Stačí číst události klávesnice pomocí konkrétních obslužných rutin.

  • m_moveInUse označuje, jestli se používá kontroler přesunutí.
  • m_movePointerID je jedinečné ID aktuálního ukazatele přesunutí. Používáme ho k rozlišení mezi ukazatelem pohledu a ukazatelem pohybu při kontrole hodnoty ID ukazatele.
  • m_moveFirstDown je bod na obrazovce, na kterém se hráč poprvé dotkl oblasti ukazatele myši na pohyb. Tuto hodnotu použijeme později k nastavení mrtvé zóny, která zabrání drobným pohybům způsobovat chvění zobrazení.
  • m_movePointerPosition je bod na obrazovce, na který přehrávač právě přesunul ukazatel. Používáme ho k určení, kudy se hráč chtěl pohybovat, analýzou v souvislosti s m_moveFirstDown.
  • m_moveCommand je posledním vypočítaným příkazem pro ovladač pro přesunutí: nahoru (dopředu), dolů (zpět), doleva nebo doprava.

Teď definujeme pole, která používáme pro kontroler vzhledu, a to jak pro implementaci myši, tak dotykového ovládání.

  • m_lookInUse označuje, jestli je ovládací prvek vzhledu používán.
  • m_lookPointerID je jedinečné ID aktuálního ukazatele pohledu. Používáme ho k rozlišení mezi ukazatelem pohledu a ukazatelem pohybu při kontrole hodnoty ID ukazatele.
  • m_lookLastPoint je poslední bod v souřadnicích scény, který byl zachycen v předchozím snímku.
  • m_lookLastDelta je vypočítaný rozdíl mezi aktuálním m_position a m_lookLastPoint.

Nakonec definujeme 6 logických hodnot pro 6 stupňů pohybu, které používáme k označení aktuálního stavu jednotlivých směrových akcí pohybu (zapnuto nebo vypnuto):

  • m_vpřed, m_zpět, m_vlevo, m_vpravo, m_nahoru a m_dolů.

K zachycení vstupních dat, která používáme k aktualizaci stavu kontrolerů, používáme 6 obslužných rutin událostí:

  • OnPointerPressed. Hráč stiskl levé tlačítko myši s ukazatelem na naší herní obrazovce nebo se dotkl obrazovky.
  • OnPointerMoved . Hráč přesunul myš ukazatelem na naší herní obrazovce nebo přetáhl dotykový ukazatel na obrazovce.
  • OnPointerReleased. Hráč uvolnil levé tlačítko myši, když měl ukazatel na naší herní obrazovce, nebo přestal se dotýkat obrazovky.
  • OnKeyDown . Hráč stiskl klávesu.
  • OnKeyUp . Hráč uvolnil klávesu.

A nakonec tyto metody a vlastnosti použijeme k inicializaci, přístupu a aktualizaci informací o stavu kontrolerů.

  • Inicializovat. Naše aplikace volá tuto obslužnou rutinu události, která inicializuje ovládací prvky a připojí je k objektu CoreWindow popisující okno zobrazení.
  • SetPosition. Naše aplikace volá tuto metodu k nastavení souřadnic (x, y a z) ovládacích prvků v prostoru scény.
  • NastavitOrientaci . Naše aplikace volá tuto metodu, aby nastavila úhel naklonění a úhel natočení kamery.
  • get_Position. Naše aplikace přistupuje k této vlastnosti, aby získala aktuální pozici kamery v prostoru scény. Tuto vlastnost použijete jako metodu komunikace aktuální polohy fotoaparátu do aplikace.
  • get_LookPoint. Naše aplikace přistupuje k této vlastnosti, aby zjistila aktuální bod, na který se kamera ovladače zaměřuje.
  • Aktualizovat. Čte stav ovladačů pro pohyb a pohled a aktualizuje pozici kamery. Tuto metodu průběžně voláte z hlavní smyčky aplikace, abyste aktualizovali data kontroleru fotoaparátu a pozici kamery v prostoru scény.

Teď máte všechny komponenty, které potřebujete k implementaci ovládacích prvků pro přesunutí. Pojďme tedy tyto části spojit.

Vytvoření základních vstupních událostí

Dispečer událostí prostředí Windows Runtime poskytuje 5 událostí, které chceme, aby instance MoveLookController třídy zpracovávaly:

Tyto události jsou implementovány u typu CoreWindow. Předpokládáme, že máte objekt CoreWindow, se kterým chcete pracovat. Pokud nevíte, jak ji získat, přečtěte si téma Jak nastavit aplikaci C++ pro Univerzální platformu Windows (UPW) tak, aby zobrazovala zobrazení DirectX.

Tyto události se aktivují, když je naše aplikace spuštěná, obslužné rutiny aktualizují informace o stavu kontrolerů definované v našich privátních polích.

Nejprve nastavíme obslužné rutiny pro události myši a dotykového ukazatele. V první obslužné rutině události OnPointerPressed(), získáme souřadnice x-y ukazatele z CoreWindow, která spravuje naše zobrazení, když uživatel klikne myší nebo se dotkne obrazovky v oblasti kontroleru vzhledu.

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

Tato obslužná rutina události zkontroluje, zda ukazatel není myší (pro účely této ukázky, která podporuje myš i dotykové ovládání) a zda je v oblasti ovladače pohybu. Pokud jsou obě kritéria pravdivá, zkontroluje, zda byl ukazatel právě stisknut, konkrétně zda toto kliknutí nesouvisí s předchozím přesunutím nebo pohlídkem, testuje, zda je m_moveInUse nepravdivý. Pokud ano, obslužná rutina zachytí bod v oblasti kontroleru přesunutí, kde došlo k stisknutí, a nastaví m_moveInUse na true, takže když je tato obslužná rutina znovu volána, nepřepíše počáteční pozici vstupní interakce kontroleru přesunutí. Aktualizuje také ID ukazatele ovladače pohybu na ID současného ukazatele.

Pokud je ukazatelem myš nebo pokud dotykový ukazatel není v oblasti pohybového kontroleru, musí být v oblasti kontroleru vzhledu. Nastaví m_lookLastPoint na aktuální pozici, kde uživatel stiskl tlačítko myši nebo se dotkl a podržel, resetuje rozptyl a aktualizuje ID ukazatele ovladače vzhledu na aktuální ID ukazatele. Nastaví také stav kontroleru vzhledu na aktivní.

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 obslužná rutina události se aktivuje při každém přesunutí ukazatele (v tomto případě pokud je ukazatel na dotykové obrazovce přetažen nebo pokud se ukazatel myši přesune během stisknutí levého tlačítka). Pokud je ID ukazatele stejné jako ID ukazatele pohybu ovladače, pak se jedná o ukazatel pohybu; v opačném případě zkontrolujeme, jestli se jedná o ovladač vzhledu, který je aktivním ukazatelem.

Pokud se jedná o kontroler přesunutí, stačí aktualizovat pozici ukazatele. Aktualizujeme ho tak dlouho, dokud se událost PointerMoved neustále aktivuje, protože chceme porovnat konečnou pozici s první, kterou jsme zachytili obslužnou rutinou události OnPointerPressed.

Pokud je to kontroler vzhledu, věci jsou trochu složitější. Potřebujeme vypočítat nový bod vzhledu a vycentrovat na něj kameru, takže vypočítáme rozdíl mezi posledním bodem vzhledu a aktuální pozicí obrazovky, a pak vynásobíme naším měřítkovým faktorem, který můžeme upravit tak, aby pohyby vzhledu byly menší nebo větší vzhledem k rozsahu pohybu obrazovky. Pomocí této hodnoty vypočítáme náklon a zatáčení.

Nakonec musíme deaktivovat chování ovladače pohybu nebo vzhledu, když hráč přestane přesouvat myš nebo se dotýkat obrazovky. Používáme OnPointerReleased, což voláme při vyvolání PointerReleased, k nastavení m_moveInUse nebo m_lookInUse na FALSE, vypnutí pohybu posunu kamery a k vynulování ID ukazatele.

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

Zatím jsme zvládli všechny události dotykové obrazovky. Teď zpracujeme klíčové události vstupu pro ovladač pohybu založený na klávesnici.

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

Pokud je stisknuta jedna z těchto kláves, tato událost nastaví odpovídající stav pohybu ve směru na 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;
}

A když se klíč uvolní, tato obslužná rutina události ji nastaví zpět na false. Když zavoláme Update, zkontroluje tyto směrové stavy pohybu a odpovídajícím způsobem přesune kameru. To je trochu jednodušší než implementace dotykového ovládání!

Inicializace dotykových ovládacích prvků a stavu kontroleru

Pojďme se teď připojit k událostem a inicializovat všechna pole stavu kontroleru.

Inicializovat

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.

}

Inicializace vezme odkaz na instanci CoreWindow aplikace jako parametr a zaregistruje obslužné rutiny událostí, které jsme vyvinuli na příslušné události na tomto CoreWindow. Inicializuje ID ukazatelů pohybu a vzhledu, nastaví vektor příkazu pro náš ovladač pohybu na dotykové obrazovce na nulu a při spuštění aplikace nastaví fotoaparát tak, aby se díval přímo rovně dopředu.

Získání a nastavení polohy a orientace kamery

Pojďme definovat některé metody pro získání a nastavení polohy kamery s ohledem na oblast zobrazení.

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

Aktualizace informací o stavu kontroleru

Teď provádíme výpočty, které převádějí informace o souřadnicích ukazatele sledovaného v m_movePointerPosition na nové souřadnice příslušné našemu světovému souřadnicovému systému. Naše aplikace volá tuto metodu pokaždé, když aktualizujeme hlavní smyčku aplikace. Proto zde vypočítáme informace o pozici nového bodu pohledu, které chceme předat aplikaci pro aktualizaci matice zobrazení před projekcí do zorného pole.

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

}

Vzhledem k tomu, že nechceme trhavý pohyb při používání našeho dotykového ovladače pohybu hráčem, nastavíme virtuální mrtvou zónu kolem ukazatele o průměru 32 pixelů. Také přidáváme rychlost, která je hodnota příkazu plus míra nárůstu pohybu. (Toto chování můžete upravit podle svých představ, zpomalit nebo urychlit pohyb na základě vzdálenosti, po které se ukazatel pohybuje v oblasti ovladače pohybu.)

Při výpočtu rychlosti také převedeme souřadnice přijaté od řídicích prvků pohybu a pohledu na pohyb skutečného pohledového bodu, který pošleme metodě, jež vypočítá naši matici pohledu pro scénu. Nejprve invertujeme souřadnici x, protože když s ovladačem pohledu klikneme a táhneme doleva nebo doprava, bod pohledu se otočí opačným směrem ve scéně, jako by se kamera mohla otočit kolem své centrální osy. Potom prohodíme osy y a z, protože stisknutí klávesy nahoru/dolů nebo dotykové přetahování na ovladači pohybu by mělo vést k akci kamery, která posouvá bod pohledu do obrazovky nebo ven z ní (os z).

Konečná pozice hledacího bodu hráče je poslední pozice plus vypočítaná rychlost, a to je to, co čte renderer při volání metody get_Position (s největší pravděpodobností během nastavení každého snímku). Potom příkaz pro přesunutí resetujeme na nulu.

Aktualizace matice zobrazení s novou polohou kamery

Můžeme získat souřadnici místa scény, na které je naše kamera zaměřená, a která se aktualizuje pokaždé, když aplikaci řeknete, aby to udělala (například každých 60 sekund v hlavní smyčce aplikace). Tento pseudokód naznačuje chování volání, které můžete implementovat:

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

Gratulujeme! Implementovali jste základní ovládací prvky pro pohyb a rozhlížení pro dotykové obrazovky i klávesnici/myš ve vaší hře!