Megosztás a következőn keresztül:


Játékok mozgás- és nézetvezérlői

Ismerje meg, hogyan adhat hozzá hagyományos egér- és billentyűzetmozgatási vezérlőket (más néven egérgombokat) a DirectX-játékhoz.

A mozgás- és nézetvezérlés támogatásáról is szó esik érintéses eszközök esetén. A mozgásvezérlő úgy van definiálva, hogy a képernyő bal alsó részén helyezkedik el, és irányított bemenetként működik, míg a képernyő többi részén a nézetvezérlő van definiálva. A kamera arra a helyre középre igazodik, ahol a játékos utoljára érintette meg ezt a területet.

Ha ez egy ismeretlen kontroll fogalom, tekintse úgy: a billentyűzet (vagy az érintésalapú irányító beviteli mező) vezérli a lábait ebben a 3D térben, és úgy viselkedik, mintha a lábai csak előre vagy hátra, illetve balra és jobbra mozgásra képesek lennének. Az egér (vagy érintéses mutató) irányítja a fej mozgását. A fejét használva egy irányba néz – balra vagy jobbra, felfelé vagy lefelé, vagy valahol abban a síkban. Ha van egy cél a nézetben, akkor az egérrel középre helyezheti a kameranézetet a célhoz, majd az előrebillentyűt lenyomva mozoghat felé, vagy visszaléphet tőle. A cél körbekarikázásához a kameranézetet középre kell helyeznie a célon, és egyszerre balra vagy jobbra kell mozognia. Láthatja, hogy ez egy nagyon hatékony vezérlési módszer a 3D-környezetek navigálásához!

Ezeket a vezérlőket általában WASD vezérlőkként ismerik a játékokban, ahol a W, A, S és D billentyűket használják az x-z síkban a kamera mozgatására, míg az egérrel a kamera forgatását szabályozzák az x és y tengelyek körül.

Célkitűzések

  • Az egér és a billentyűzet, valamint az érintőképernyők esetében is felvehet alapszintű mozgásvezérlőket a DirectX-játékba.
  • Implementáljon egy 3D környezetben való navigáláshoz használt elsőszemélyes kamerát.

Megjegyzés az érintésvezérlés implementációiról

Az érintésvezérléshez két vezérlőt valósítunk meg: a mozgásvezérlőt, amely az x-z síkban kezeli a mozgást a kamera nézési pontjához viszonyítva; és a nézetvezérlőt, amely a kamera nézési pontját célozza meg. A mozgásvezérlő hozzárendeli a billentyűzet WASD gombjait, a nézet vezérlő pedig az egér mozgatásához. Érintésvezérlés esetén azonban meg kell határoznunk a képernyő egy olyan régióját, amely iránybemenetként vagy a virtuális WASD gombként szolgál, a képernyő többi része pedig a megjelenésvezérlők bemeneti területeként szolgál.

A képernyő így néz ki.

a mozgás-nézés vezérlő elrendezés

Ha az érintőmutatót (nem az egeret!) a képernyő bal alsó sarkában helyezi el, a felfelé irányuló bármilyen mozgás előremozdíthatja a kamerát. Bármilyen lefelé irányuló mozgás a kamerát hátrafelé mozgatja. Ugyanez vonatkozik a bal és a jobb mozgásra az áthelyezésvezérlő mutatóterében. ** Amikor ezen a téren kívül tartózkodik, ez lesz egy nézetvezérlő – csak érintse meg vagy húzza a kamerát arra, amerre szeretné, hogy nézzen.

Az alapszintű bemeneti eseményinfrastruktúra beállítása

Először létre kell hoznunk a vezérlőosztályt, amelyet az egér és a billentyűzet bemeneti eseményeinek kezelésére használunk, és ezen bemenet alapján frissíteni kell a kamera perspektíváját. Mivel a move-look vezérlőket implementáljuk, MoveLookControllernevezzük.

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

Hozzunk létre most egy fejlécet, amely meghatározza a move-look vezérlő és az első személyű kamera állapotát, valamint azokat az alapvető metódusokat és eseménykezelőket, amelyek implementálják a vezérlőket és frissítik a kamera állapotát.

#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

A kód 4 privát mezőcsoportot tartalmaz. Tekintsük át mindegyik célját.

Először is meghatározunk néhány hasznos mezőt, amelyek a kameranézet frissített adatait tárolják.

  • m_position a 3D jelenetben a kamera (és így a nézetsík) pozíciója, jelenetkoordinátákat használva.
  • m_pitch a kamera dőlésszöge, vagyis a nézetsík x tengelye körüli felfelé és lefelé forgás szögének mértéke radiánban.
  • m_yaw a kamera yaw-ja, vagy annak bal-jobb forgatása a nézetsík y tengelye körül radiánban.

Most definiáljuk azokat a mezőket, amelyeket a vezérlőink állapotával és helyzetével kapcsolatos információk tárolására használunk. Először meg fogjuk határozni az érintésvezérelt áthelyezésvezérlőhöz szükséges mezőket. A mozgásvezérlő billentyűzetes implementálásához nincs szükség semmi különösre. Billentyűzetes eseményeket olvasunk adott kezelőkkel.

  • m_moveInUse jelzi, hogy az áthelyezésvezérlő használatban van-e.
  • m_movePointerID az aktuális áthelyezési mutató egyedi azonosítója. A mutatóazonosító értékének ellenőrzésekor megkülönböztetjük a nézési mutatót és a mozgatási mutatót.
  • m_moveFirstDown az a pont a képernyőn, ahol a játékos először megérintette az áthelyezésvezérlő mutatóját. Ezt az értéket később arra használjuk, hogy beállítsunk egy holtzónát, amely megakadályozza az apró mozgások által okozott remegést a nézetben.
  • m_movePointerPosition az a pont a képernyőn, amelyre a játékos éppen áthelyezte a mutatót. Arra használjuk, hogy megállapítsuk, milyen irányban szeretne a játékos mozogni azáltal, hogy megvizsgáljuk azt a m_moveFirstDownviszonyítva.
  • m_moveCommand az áthelyezésvezérlő utolsó számított parancsa: fel (előre), lefelé (vissza), balra vagy jobbra.

Most definiáljuk a keresési vezérlőhöz használt mezőket, az egér- és érintéses implementációkat is.

  • m_lookInUse jelzi, hogy a megjelenésvezérlő használatban van-e.
  • m_lookPointerID az aktuális megjelenésmutató egyedi azonosítója. A mutatóazonosító értékének ellenőrzésekor megkülönböztetjük a nézési mutatót és a mozgatási mutatót.
  • m_lookLastPoint az előző keretben rögzített utolsó pont a jelenetkoordinátákban.
  • m_lookLastDelta az aktuális m_position és m_lookLastPointközötti számított különbség.

Végül 6 logikai értéket határozunk meg a 6 mozgásfokhoz, amelyet az egyes irányátmozgatási műveletek aktuális állapotának jelzésére használunk (be- vagy kikapcsolt):

  • m_forward, m_back, m_left, m_right, m_up és m_down.

A 6 eseménykezelővel rögzítjük a vezérlőink állapotának frissítéséhez használt bemeneti adatokat:

  • OnPointerPressed. A játékos lenyomta a bal egérgombot a mutatóval a játék képernyőjén, vagy megérintette a képernyőt.
  • OnPointerMoved. A játékos áthelyezte az egérmutatót a játék képernyőjén, vagy elhúzta az érintőmutatót a képernyőn.
  • OnPointerReleased. A játékos felengedte a bal egérgombot a mutatóval a játék képernyőjén, vagy abbahagyta a képernyő érintését.
  • OnKeyDown. A játékos lenyomott egy billentyűt.
  • OnKeyUp. A játékos kiadott egy kulcsot.

Végül ezeket a metódusokat és tulajdonságokat használjuk a vezérlők állapotinformációinak inicializálására, elérésére és frissítésére.

  • inicializálása:. Az alkalmazás meghívja ezt az eseménykezelőt, hogy inicializálja a vezérlőket, és csatolja őket a megjelenítési ablakot leíró CoreWindow objektumhoz.
  • PozícióBeállítása. Az alkalmazás meghívja ezt a metódust, hogy beállítsa a vezérlők (x, y és z) koordinátáit a jelenettérben.
  • SetOrientation. Az alkalmazás ezt a módszert hívja meg a kamera dőlésének és elfordulásának beállításához.
  • get_Position. Az alkalmazás hozzáfér ehhez a tulajdonsághoz, hogy megkapja a kamera aktuális pozícióját a jelenettérben. Ezt a tulajdonságot használja az aktuális kamera helyzetének az alkalmazással való kommunikációjára.
  • get_LookPoint. Az alkalmazás hozzáfér ehhez a tulajdonsághoz, hogy megkapja a vezérlő kamera által nézett irányt.
  • Frissítés. Beolvassa az áthelyezési és nézési vezérlők állapotát, és frissíti a kamera helyzetét. Ezt a metódust folyamatosan az alkalmazás fő hurokjából hívhatja fel a kameravezérlő adatainak és a kamera helyzetének frissítéséhez a jelenettérben.

Most már itt van minden összetevő, ami a mozgás- és nézetvezérlők megvalósításához szükséges. Tehát kapcsoljuk össze ezeket a darabokat.

Az alapszintű bemeneti események létrehozása

A Windows Futtatókörnyezet esemény-diszpécser 5 eseményt biztosít, amelyeket a MoveLookController osztály példányainak kezelni szeretnénk:

Ezek az események a CoreWindow típusán vannak implementálva. Feltételezzük, hogy rendelkezik egy CoreWindow objektummal, amellyel dolgozni szeretne. Ha nem tudja, hogyan szerezhet be egyet, olvassa el Az Univerzális Windows Platform (UWP) C++ alkalmazás beállítása DirectX-nézet megjelenítéséhez.

Mivel ezek az események az alkalmazás futtatása közben aktiválódik, a kezelők frissítik a vezérlők privát mezőkben definiált állapotinformációit.

Először állítsuk be az egér és az érintőmutató eseménykezelőit. Az első eseménykezelőben, OnPointerPressed(), megszerezzük a mutató x-y koordinátáit a CoreWindow-től, amely kezeli a kijelzőnket, amikor a felhasználó az egérrel kattint vagy megérinti a képernyőt a megtekintésvezérlő régiójában.

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

Ez az eseménykezelő ellenőrzi, hogy a mutató nem az egér-e (a példa támogatja mind az egeret, mind az érintést), és hogy a mozgatásvezérlő területén van-e. Ha mindkét feltétel igaz, akkor ellenőrzi, hogy az egérmutatót csak lenyomták-e, pontosabban azt, hogy ez a kattintás nem kapcsolódik-e egy korábbi áthelyezéshez vagy megjelenési bemenethez, és teszteli, hogy m_moveInUse hamis-e. Ha igen, a kezelő rögzíti az áthelyezésvezérlő azon pontját, ahol a lenyomás történt, és igaz értékre állítja m_moveInUse, így a kezelő ismételt meghívásakor nem írja felül az áthelyezésvezérlő bemeneti interakciójának kezdő pozícióját. Emellett frissíti az áthelyezésvezérlő mutatóazonosítóját az aktuális mutató azonosítójára.

Ha a mutató az egér, vagy ha az érintőmutató nem a mozgatásvezérlő területén van, akkor a nézetvezérlő területén kell lennie. Beállítja a m_lookLastPoint arra a pozícióra, ahol a felhasználó lenyomta az egérgombot, vagy megérintette és lenyomta, alaphelyzetbe állítja a deltát, és frissíti a look controller mutatóazonosítóját az aktuális mutatóazonosítóra. A nézetvezérlő állapotát is aktívra állítja.

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

Az OnPointerMoved eseménykezelő a mutató áthelyezésekor aktiválódik (ebben az esetben, ha egy érintőképernyős mutatót húz, vagy ha az egérmutatót a bal gomb lenyomása közben mozgatja). Ha a mutató azonosítója megegyezik az áthelyezésvezérlő mutatójának azonosítójával, akkor az áthelyezési mutató; ellenkező esetben ellenőrizzük, hogy az aktív mutató a nézetvezérlő-e.

Ha ez az áthelyezésvezérlő, csak frissítjük a mutató pozícióját. Mindaddig frissítjük, amíg az PointerMoved esemény folyamatosan aktiválódik, mert a végső pozíciót az OnPointerPressed eseménykezelővel rögzített elsővel szeretnénk összehasonlítani.

Ha a megjelenésvezérlőről van szó, a dolgok kicsit bonyolultabbak. Ki kell számolnunk egy új megjelenési pontot, és középre kell helyeznünk a kamerát, így kiszámítjuk az utolsó és az aktuális képernyőpozíció közötti különbséget, majd szorzunk a méretezési tényezővel szemben, amelyet úgy módosíthatunk, hogy a képernyő mozgásának távolságához képest kisebb vagy nagyobb legyen a megjelenési mozgás. Ezzel az értékkel kiszámítjuk a pályát és a jakot.

Végül inaktiválnunk kell az áthelyezési vagy megjelenési vezérlő viselkedését, amikor a játékos nem mozgatja az egeret, vagy megérinti a képernyőt. OnPointerReleasedhasználjuk, amelyet akkor hívunk meg, amikor PointerReleased aktiválódik, hogy a m_moveInUse vagy m_lookInUse értéket FALSE-ra állítsuk, megállítsuk a kamera pásztamozgását, és nullára állítsuk a mutatóazonosítót.

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

Eddig az összes érintőképernyős eseményt kezeltük. Most kezeljük a billentyűzetalapú áthelyezésvezérlő legfontosabb bemeneti eseményeit.

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

Amíg az egyik billentyű lenyomva van, az eseménykezelő a megfelelő irányátlépési állapotot igaz értékre állítja.

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

Amikor a kulcs ki van adva, az eseménykezelő visszaállítja hamisra. Amikor meghívjuk Update, ellenőrzi ezeket az irányított áthelyezési állapotokat, és ennek megfelelően helyezi át a kamerát. Ez egy kicsit egyszerűbb, mint az érintéses implementáció!

Az érintésvezérlés és a vezérlő állapotának inicializálása

Most kapcsoljuk össze az eseményeket, és inicializáljuk az összes vezérlőállapot-mezőt.

inicializál

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.

}

A inicializálása egy paraméterként kapja az alkalmazás CoreWindow példányára tett hivatkozást, és regisztrálja az általunk fejlesztett eseménykezelőket a megfelelő eseményekre azon a CoreWindow-n. Inicializálja a mozgató és néző tájolópontok azonosítóit, nullára állítja az érintőképernyős mozgatásvezérlő implementáció parancsvektorát, és beállítja a kamerát, hogy egyenesen előre nézzen, amikor az alkalmazás elindul.

A kamera helyzetének és tájolásának lekérése és beállítása

Határozzunk meg néhány módszert a kamera helyzetének lekérésére és beállítására a nézetporthoz képest.

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

A vezérlő állapotinformációinak frissítése

Most elvégezzük a számításainkat, amelyek a m_movePointerPosition nyomon követett mutatókoordináta-adatokat a világkoordináta-rendszerünk új koordináta-információivá alakítják. Az alkalmazás minden alkalommal meghívja ezt a metódust, amikor frissítjük a fő alkalmazáshurkot. Ezért itt számítjuk ki az új nézési pont pozíció információját, amelyet át szeretnénk adni az alkalmazásnak a nézetmátrix frissítéséhez a nézetportba való vetítés előtt.

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

}

Mivel nem szeretnénk jittery mozgást, amikor a játékos az érintésalapú mozgásvezérlőt használja, 32 képpont átmérőjű virtuális holtzónát állítunk be a mutató körül. Emellett sebességet is hozzáadunk, amely a parancs értéke plusz egy mozgásgyarapodási arány. (Ezt a viselkedést tetszés szerint módosíthatja, hogy lelassítsa vagy felgyorsítsa a mozgás sebességét attól függően, hogy a mutató mennyi távolságra mozog az áthelyezésvezérlő területén.)

A sebesség kiszámításakor a mozgás és a nézet vezérlőktől kapott koordinátákat is átfordítjuk a tényleges nézési pont mozgásává, amelyet a jelenethez tartozó nézetmátrixot kiszámító függvénynek küldünk. Először is invertáltuk az x koordinátát, mert ha a look controllerrel balra vagy jobbra húzunk, a look point a jelenet ellentétes irányában forog, mivel a kamera a központi tengelye körül mozoghat. Ezután felcseréljük az y és z tengelyeket, mert egy fel/le billentyű lenyomása vagy érintőképernyős húzás (y tengelyes viselkedés) a mozgásvezérlőn olyan kameraműveletre kell leforduljon, amely a nézési pontot a képernyő síkjába viszi, vagy onnan kihúzza (a z tengely).

A játékos nézési pontjának végső pozíciója az utolsó pozíció és a számított sebesség összege, és ezt olvassa be a renderelő, amikor meghívja a get_Position metódust (valószínűleg az egyes képkockák beállítása során). Ezután nullára állítjuk vissza az áthelyezési parancsot.

A nézetmátrix frissítése az új kamerapozícióval

Beszerezhetünk egy jelenettérkoordinátát, amelyre a kameránk összpontosít, és amely minden alkalommal frissül, amikor az alkalmazásnak ezt mondja (például 60 másodpercenként a főalkalmazás-ciklusban). Ez a pszeudokód a megvalósítható hívási viselkedésre utal:

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

Gratulálok! Az érintőképernyőkhöz és a billentyűzet/egér beviteli érintővezérlőkhöz is alapszintű mozgásvezérlőket vezetett be a játékban!