Dela via


Rörelse- och siktkontroller för spel

Lär dig hur du lägger till traditionella kontroller för mus- och tangentbordsflyttning (kallas även muslookkontroller) i ditt DirectX-spel.

Vi diskuterar också stöd för move-look på touch-enheter, med rörelsekontrollen definierad som den nedre vänstra delen av skärmen som fungerar som en riktningsinmatning, och med blickkontrollen definierad för resten av skärmen, där kameran centrerar sig på den sista platsen som spelaren rörde vid i det området.

Om detta koncept är främmande för dig, föreställ dig det så här: tangentbordet (eller den beröringsbaserade riktningsinmatningslådan) styr dina ben i detta 3D-utrymme och beter sig som om dina ben bara kunde röra sig framåt eller bakåt, eller strafa vänster eller höger. Musen (eller pekpekaren) styr huvudet. Du använder huvudet för att titta i en riktning – vänster eller höger, upp eller ner, eller någonstans i planet. Om det finns ett mål i vyn använder du musen för att centrera kameravyn på det målet och trycker sedan på framåtknappen för att gå mot det eller tillbaka för att flytta bort från det. Om du vill cirkla runt målet skulle du hålla kameravyn centrerad på målet och flytta åt vänster eller höger samtidigt. Du kan se hur detta är en mycket effektiv kontrollmetod för att navigera i 3D-miljöer!

Dessa kontroller kallas ofta WASD-kontroller i spel, där nycklarna W, A, S och D används för x-z-planets fasta kamerarörelse, och musen används för att styra kamerarotationen runt x- och y-axlarna.

Målsättningar

  • Lägg till grundläggande move-look-kontroller i Ditt DirectX-spel för både mus och tangentbord och pekskärmar.
  • Implementera en förstapersonskamera som används för att navigera i en 3D-miljö.

En anteckning om implementeringar av touchkontroll

För pekkontroller använder vi oss av två styrenheter: flyttstyrenheten, som hanterar rörelsen i x-z-planet i förhållande till kamerans siktpunkt. Siktkontrollen syftar på kamerans siktpunkt. Vår rörelsekontroll kartlägger till WASD-knapparna på tangentbordet, och sikteskontrollen kartlägger till musen. Men för tryckkontroller måste vi definiera ett område på skärmen som fungerar som riktningsindata, eller de virtuella WASD-knapparna, där resten av skärmen fungerar som indatautrymme för styrkontrollerna.

Vår skärm ser ut så här.

layouten för move-look kontroller

När du flyttar pekpekaren (inte musen!) längst ned till vänster på skärmen kommer alla rörelser uppåt att få kameran att gå framåt. Alla rörelser nedåt gör att kameran rör sig bakåt. Samma gäller för vänster och höger rörelse inuti rörelsekontrollerns pekutrymme. Utanför det utrymmet blir det en blickkontroll – du behöver bara röra vid eller dra kameran dit du vill att den ska peka.

Konfigurera den grundläggande infrastrukturen för indatahändelser

Först måste vi skapa vår kontrollklass som vi använder för att hantera indatahändelser från musen och tangentbordet och uppdatera kameraperspektivet baserat på dessa indata. Eftersom vi implementerar move-look-kontroller kallar vi det 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

Nu ska vi skapa en rubrik som definierar tillståndet för kontrollenheten för flytt-och-siktläget och dess förstapersonskamera, samt de grundläggande metoderna och händelsehanterarna som implementerar kontrollerna och uppdaterar kamerans tillstånd.

#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

Vår kod innehåller 4 grupper med privata fält. Nu ska vi gå igenom syftet med var och en.

Först definierar vi några användbara fält som innehåller vår uppdaterade information om vår kameravy.

  • m_position är kamerans position (och därmed visningsplanet) i 3D-scenen med hjälp av scenkoordinater.
  • m_pitch är kamerans lutning, eller dess rotation upp och ner runt visningsplanets x-axel i radianer.
  • m_yaw är kamerans gäsp, eller dess vänster-högerrotation runt vyplanets y-axel, i radianer.

Nu ska vi definiera de fält som vi använder för att lagra information om status och position för våra kontrollanter. Först definierar vi de fält vi behöver för vår touchbaserade flyttstyrenhet. (Det finns inget speciellt som behövs för tangentbordsimplementeringen av flyttstyrenheten. Vi har precis läst tangentbordshändelser med specifika hanterare.)

  • m_moveInUse anger om flyttstyrenheten används.
  • m_movePointerID är det unika ID:t för den aktuella flyttpekaren. Vi använder den för att skilja mellan lookpekaren och flyttpekaren när vi kontrollerar pekarens ID-värde.
  • m_moveFirstDown är den punkt på skärmen där spelaren först rörde vid handkontrollens pekarområde. Vi använder det här värdet senare för att ange en dödzon så att små rörelser inte stör vyn.
  • m_movePointerPosition är den punkt på skärmen som spelaren för närvarande har flyttat pekaren till. Vi använder den för att avgöra vilken riktning spelaren ville flytta genom att undersöka den i förhållande till m_moveFirstDown.
  • m_moveCommand är det slutliga beräknade kommandot för flyttstyrenheten: uppåt (framåt), nedåt (bakåt), vänster eller höger.

Nu definierar vi de fält som vi använder för vår lookkontrollant, både mus- och touchimplementeringar.

  • m_lookInUse anger om look-kontrollen används.
  • m_lookPointerID är det unika ID:t för den aktuella visningspekaren. Vi använder den för att skilja mellan lookpekaren och flyttpekaren när vi kontrollerar pekarens ID-värde.
  • m_lookLastPoint är den sista punkten, i scenkoordinater, som fångades i föregående bildruta.
  • m_lookLastDelta är den beräknade skillnaden mellan den aktuella m_position och m_lookLastPoint.

Slutligen definierar vi 6 booleska värden för 6 graders rörelse, som vi använder för att ange aktuellt tillstånd för varje riktningsåtgärd (på eller av):

  • m_framåt, m_bakåt, m_vänster, m_höger, m_upp och m_ner.

Vi använder 6 händelsehanterare för att samla in indata som vi använder för att uppdatera tillståndet för våra kontrollanter:

  • OnPointerPressed. Spelaren tryckte på den vänstra musknappen med pekaren på spelskärmen eller rörde vid skärmen.
  • OnPointer Har flyttats. Spelaren flyttade musen med pekaren på spelskärmen eller drog pekpekaren på skärmen.
  • OnPointerReleased. Spelaren släppte den vänstra musknappen med pekaren på spelskärmen eller slutade röra skärmen.
  • OnKeyDown. Spelaren tryckte på en nyckel.
  • OnKeyUp. Spelaren släppte en nyckel.

Slutligen använder vi dessa metoder och egenskaper för att initiera, komma åt och uppdatera kontrollanternas tillståndsinformation.

  • Initiera. Vår app anropar den här händelsehanteraren för att initiera kontrollerna och koppla dem till CoreWindow- objekt som beskriver vårt visningsfönster.
  • SetPosition. Vår app anropar den här metoden för att ange koordinaterna (x, y och z) för våra kontroller i scenutrymmet.
  • SetOrientation. Appen anropar den här metoden för att justera kamerans lutning och gir.
  • get_Position. Vår app använder den här egenskapen för att hämta kamerans aktuella position i scenutrymmet. Du använder den här egenskapen som metod för att kommunicera den aktuella kamerapositionen till appen.
  • get_LookPoint. Vår app använder den här egenskapen för att hämta den aktuella punkt som styrenhetskameran är riktad mot.
  • Uppdatera. Läser av tillståndet för rörelse- och blickkontroller och uppdaterar kamerapositionen. Du anropar kontinuerligt den här metoden från appens huvudloop för att uppdatera kamerastyrenhetsdata och kamerapositionen i scenutrymmet.

Nu har du här alla komponenter som du behöver för att implementera dina move-look-kontroller. Så låt oss koppla ihop de här delarna.

Skapa de grundläggande indatahändelserna

Händelseutskickaren för Windows Runtime innehåller 5 händelser som vi vill att instanser av klassen MoveLookController ska hantera:

Dessa händelser implementeras på CoreWindow typ. Vi antar att du har ett CoreWindow- objekt att arbeta med. Om du inte vet hur du skaffar en kan du läsa Så här konfigurerar du din UWP-app (Universal Windows Platform) för att visa en DirectX-vy.

När dessa händelser utlöses medan vår app körs uppdaterar hanterarna kontrollanternas tillståndsinformation som definierats i våra privata fält.

Först ska vi fylla i händelsehanterare för mus- och pekpekare. I den första händelsehanteraren OnPointerPressed()får vi x-y-koordinaterna för pekaren från CoreWindow- som hanterar vår skärm när användaren klickar med musen eller rör vid skärmen i look controller-regionen.

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

Den här händelsehanteraren kontrollerar om pekaren inte är musen (i det här exemplet, som stöder både mus och pekfunktioner) och om den finns i området för flyttstyrenhet. Om båda kriterierna är sanna kontrollerar den om pekaren precis har tryckts, specifikt om detta klick inte är relaterat till en tidigare förflyttning eller syninmatning, genom att testa om m_moveInUse är falskt. I så fall samlar hanteraren in punkten i området för flyttstyrenheten där pressen skedde och anger m_moveInUse till sant, så att den inte skriver över startpositionen för interaktionen med indata för flyttstyrenheten när den här hanteraren anropas igen. Den uppdaterar även pekar-ID:t för flyttkontrollanten till den aktuella pekarens ID.

Om pekaren är musen eller om tryckpekaren inte finns i området för rörelsekontroll måste den vara i området för synkontroll. Den ställer in m_lookLastPoint till den aktuella positionen där användaren tryckte på musknappen eller rörde vid och tryckte på, återställer delta-värdet och uppdaterar titta-kontrollens pekar-ID med det nuvarande pekar-ID:t. Den sätter också den visuella kontrollens status till aktiv.

OnPointer har flyttats

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 händelsehanterare utlöses när pekaren flyttas (i det här fallet om en pekskärmspekare dras eller om muspekaren flyttas medan den vänstra knappen trycks in). Om pekarens ID är samma som flyttkontrollantens ID, är det flyttpekaren. Annars kontrollerar vi om det är siktkontrollanten som är den aktiva pekaren.

Om det är flyttstyrenheten uppdaterar vi bara pekarpositionen. Vi fortsätter att uppdatera den så länge händelsen PointerMoved fortsätter att utlösas, eftersom vi vill jämföra den slutliga positionen med den första som vi hämtade med händelsehanteraren OnPointerPressed.

Om det är lookkontrollanten är saker lite mer komplicerade. Vi måste beräkna en ny siktpunkt och centrera kameran på den, så vi beräknar skillnaden mellan den sista siktpunkten och den aktuella skärmpositionen, och sedan multiplicerar vi med vår skalningsfaktor, som vi kan justera för att göra rörelserna av siktpunkten mindre eller större i förhållande till skärmrörelsens avstånd. Genom att använda det värdet beräknar vi stigningen och giren.

Slutligen måste vi inaktivera beteendet för att flytta eller titta på kontrollanten när spelaren slutar flytta musen eller vidrör skärmen. Vi använder OnPointerReleased, som vi anropar när PointerReleased utlöses, för att ange m_moveInUse eller m_lookInUse till FALSE och stänga av kamerans panoreringsrörelse och för att nollställa pekar-ID: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;
    }
}

Hittills har vi hanterat alla pekskärmshändelser. Nu ska vi hantera viktiga indatahändelser för en tangentbordsbaserad flyttstyrenhet.

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

Så länge en av dessa nycklar trycks in anger den här händelsehanteraren motsvarande riktningsflyttningstillstånd till sant.

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

Och när nyckeln släpps ställer den här händelsehanteraren tillbaka den till false. När vi anropar Updatekontrollerar den dessa riktningsflyttningstillstånd och flyttar kameran därefter. Det här är lite enklare än touch-implementeringen!

Initiera touch-kontrollerna och kontrollanttillståndet

Nu ska vi koppla händelserna och initiera alla kontrollanttillståndsfält.

Initiera

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.

}

Initiera tar en referens till appens CoreWindow instans som parameter och registrerar de händelsehanterare vi har utvecklat till de lämpliga händelserna på denna CoreWindow. Den initierar ID:n för flytt- och tittpekarna, nollställer kommandovektorn för vår implementation av pekskärmskontrollern och riktar kameran rakt framåt när appen startar.

Hämta och ställa in kamerans position och orientering

Nu ska vi definiera några metoder för att hämta och ange kamerans position med avseende på visningsplatsen.

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

Uppdatera statusinformationen för kontrollanten

Nu utför vi våra beräkningar som konverterar pekarkoordinatinformationen som spåras i m_movePointerPosition till ny koordinatinformation med avseende på vårt världskoordinatsystem. Vår app anropar den här metoden varje gång vi uppdaterar huvudappsloopen. Därför är det här vi beräknar den nya platsinformationen för look point som vi vill skicka till appen för att uppdatera vymatrisen före projektionen till visningsporten.

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

}

Eftersom vi inte vill ha ryckiga rörelser när spelaren använder vår touchbaserade rörelsekontroller, ställer vi in en virtuell dödzon runt pekaren med en diameter på 32 bildpunkter. Vi lägger också till hastighet, vilket är kommandovärdet plus en förflyttningsvinstfrekvens. (Du kan justera det här beteendet efter behov för att sakta ned eller påskynda rörelsehastigheten baserat på avståndet som pekaren rör sig i området för flyttstyrenheten.)

När vi beräknar hastigheten översätter vi också de koordinater som tagits emot från rörelse- och siktkontrollerna till förflyttningen av den faktiska siktpunkten, som vi skickar till metoden som beräknar vår visningsmatris för scenen. Först invertera vi x-koordinaten, för om vi klickar eller drar åt vänster eller höger med lookkontrollanten roterar lookpunkten i motsatt riktning i scenen, eftersom en kamera kan svänga runt sin centrala axel. Sedan byter vi y- och z-axlarna, eftersom en uppåt-/nedtryckningsknapp eller dragrörelse (läses som ett y-axelbeteende) på flyttstyrenheten ska översättas till en kameraåtgärd som flyttar lookpunkten till eller ut från skärmen (z-axeln).

Den slutliga positionen för blickpunkten för spelaren är den sista positionen plus den beräknade hastigheten, och det är vad som läses av renderaren när den anropar metoden get_Position (troligen under inställningen för varje bildruta). Därefter återställer vi flytta-kommandot till noll.

Uppdatera visningsmatrisen med den nya kamerapositionen

Vi kan hämta en scenrymdkoordinat som kameran fokuserar på och som uppdateras när du uppmanar din app att göra det (till exempel var 60:e sekund i huvudappsloopen). Det här pseudokoden föreslår det anropsbeteende som du kan implementera:

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

Grattis! Du har implementerat grundläggande move-look-kontroller för både pekskärmar och pekkontroller för tangentbord/musinmatning i ditt spel!