Agregar controlesAdd controls

Nota

Este tema forma parte de la serie de tutoriales de creación de una plataforma universal de Windows sencilla (UWP) con DirectX .This topic is part of the Create a simple Universal Windows Platform (UWP) game with DirectX tutorial series. El tema de ese vínculo establece el contexto de la serie.The topic at that link sets the context for the series.

[ Se actualizó con aplicaciones para UWP en Windows 10.[ Updated for UWP apps on Windows 10. Para artículos de Windows 8. x, consulte el archivo]For Windows 8.x articles, see the archive ]

Una buena Plataforma universal de Windows juego (UWP) admite una amplia variedad de interfaces.A good Universal Windows Platform (UWP) game supports a wide variety of interfaces. Un posible reproductor podría tener Windows 10 en una tableta sin botones físicos, un equipo con una controladora Xbox conectada o la plataforma de juegos de escritorio más reciente con un mouse y un teclado de juegos de alto rendimiento.A potential player might have Windows 10 on a tablet with no physical buttons, a PC with an Xbox controller attached, or the latest desktop gaming rig with a high-performance mouse and gaming keyboard. En nuestro juego, los controles se implementan en la clase MoveLookController .In our game the controls are implemented in the MoveLookController class. Esta clase agrega los tres tipos de entrada (mouse y teclado, táctil y controlador para juegos) a un solo controlador.This class aggregates all three types of input (mouse and keyboard, touch, and gamepad) into a single controller. El resultado final es un cierre de primera persona que usa los controles de movimiento y búsqueda estándar del género que funcionan con varios dispositivos.The end result is a first-person shooter that uses genre standard move-look controls that work with multiple devices.

Nota

Para obtener más información sobre los controles, vea controles de movimiento y aspecto para juegos y controles táctiles para juegos.For more info about controls, see Move-look controls for games and Touch controls for games.

ObjetivoObjective

Llegados a este punto, tenemos un juego que representa, pero no se puede mover el jugador o captar los objetivos.At this point we have a game that renders, but we can't move our player around or shoot the targets. Echaremos un vistazo a cómo nuestro juego implementa los controles de movimiento de la primera persona con el dedo para los siguientes tipos de entrada en nuestro juego DirectX de UWP.We'll take a look at how our game implements first person shooter move-look controls for the following types of input in our UWP DirectX game.

  • Mouse y tecladoMouse and keyboard
  • TocarTouch
  • Controlador para juegosGamepad

Nota

Si no ha descargado el código de juego más reciente para este ejemplo, vaya a juego de ejemplo de Direct3D.If you haven't downloaded the latest game code for this sample, go to Direct3D sample game. Este ejemplo forma parte de una gran colección de ejemplos de características de UWP.This sample is part of a large collection of UWP feature samples. Para obtener instrucciones sobre cómo descargar el ejemplo, consulte obtener los ejemplos de UWP en github.For instructions on how to download the sample, see Get the UWP samples from GitHub.

Comportamientos de controles comunesCommon control behaviors

Los controles táctiles y los controles de mouse o teclado tienen una implementación principal muy parecida.Touch controls and mouse/keyboard controls have a very similar core implementation. En una aplicación para UWP, un puntero es simplemente un punto en la pantalla.In a UWP app, a pointer is simply a point on the screen. Puedes moverlo desplazando el ratón o deslizando un dedo por la pantalla táctil.You can move it by sliding the mouse or sliding your finger on the touch screen. Como resultado, puedes registrar un único conjunto de eventos y despreocuparte de si el jugador usa un mouse o una pantalla táctil para mover y presionar el puntero.As a result, you can register for a single set of events, and not worry about whether the player is using a mouse or a touch screen to move and press the pointer.

Cuando se inicializa la clase MoveLookController en el juego de ejemplo, se registra para cuatro eventos específicos del puntero y un evento específico del mouse:When the MoveLookController class in the sample game is initialized, it registers for four pointer-specific events and one mouse-specific event:

EventoEvent DescripciónDescription
CoreWindow::P ointerPressedCoreWindow::PointerPressed Se presionó (o se mantuvo presionado) el botón izquierdo o derecho del mouse o se tocó la superficie táctil.The left or right mouse button was pressed (and held), or the touch surface was touched.
CoreWindow::P ointerMovedCoreWindow::PointerMoved El mouse se movió, o se realizó una acción de arrastrar en la superficie táctil.The mouse moved, or a drag action was made on the touch surface.
CoreWindow::P ointerReleasedCoreWindow::PointerReleased Se soltó el botón izquierdo del mouse, o se retiró el objeto que estaba en contacto con la superficie táctil.The left mouse button was released, or the object contacting the touch surface was lifted.
CoreWindow::P ointerExitedCoreWindow::PointerExited El puntero se movió fuera de la ventana principal.The pointer moved out of the main window.
Windows::D evices:: Input:: MouseMovedWindows::Devices::Input::MouseMoved El mouse se desplazó una distancia determinada.The mouse moved a certain distance. Tenga en cuenta que solo nos interesan los valores Delta de movimiento del mouse, y no la posición X-Y actual.Be aware that we are only interested in mouse movement delta values, and not the current X-Y position.

Estos controladores de eventos se establecen para iniciar la escucha de datos proporcionados por el usuario en cuanto se inicializa MoveLookController en la ventana de la aplicación.These event handlers are set to start listening for user input as soon as the MoveLookController is initialized in the application window.

void MoveLookController::InitWindow(_In_ CoreWindow const& window)
{
    ResetState();

    window.PointerPressed({ this, &MoveLookController::OnPointerPressed });

    window.PointerMoved({ this, &MoveLookController::OnPointerMoved });

    window.PointerReleased({ this, &MoveLookController::OnPointerReleased });

    window.PointerExited({ this, &MoveLookController::OnPointerExited });

    ...

    // There is a separate handler for mouse-only relative mouse movement events.
    MouseDevice::GetForCurrentView().MouseMoved({ this, &MoveLookController::OnMouseMoved });

    ...
}

El código completo de InitWindow puede verse en github.Complete code for InitWindow can be seen on GitHub.

Para determinar el momento en que el juego debe estar escuchando una entrada determinada, la clase MoveLookController tiene tres Estados específicos del controlador, independientemente del tipo de controlador:To determine when the game should be listening for certain input, the MoveLookController class has three controller-specific states, regardless of the controller type:

StateState DescripciónDescription
NoneNone Es el estado inicializado para el mando.This is the initialized state for the controller. Se omiten todas las entradas, ya que el juego no prevé ninguna entrada del controlador.All input is ignored since the game is not anticipating any controller input.
WaitForInputWaitForInput El controlador está esperando a que el jugador confirme un mensaje del juego mediante un clic del mouse izquierdo, un evento Touch, y el botón de menú en un controlador de juegos.The controller is waiting for the player to acknowledge a message from the game by either using a left mouse click, a touch event, ot the menu button on a gamepad.
ActivoActive El controlador está en modo de juego activo.The controller is in active game play mode.

WaitForInput estado y pausa del juegoWaitForInput state and pausing the game

El juego entra en el estado WaitForInput cuando el juego se ha pausado.The game enters the WaitForInput state when the game has been paused. Esto sucede cuando el reproductor mueve el puntero fuera de la ventana principal del juego, o presiona el botón de pausa (tecla P o el botón Inicio del controlador de juegos).This happens when the player moves the pointer outside the main window of the game, or presses the pause button (the P key or the gamepad Start button). El MoveLookController registra la prensa e informa al bucle del juego cuando llama al método IsPauseRequested .The MoveLookController registers the press, and informs the game loop when it calls the IsPauseRequested method. En ese punto, si IsPauseRequested devuelve true, el bucle de juego llama entonces a WaitForPress en MoveLookController para pasar el controlador al estado WaitForInput .At that point if IsPauseRequested returns true, the game loop then calls WaitForPress on the MoveLookController to move the controller into the WaitForInput state.

Una vez en el estado WaitForInput , el juego detiene el procesamiento de casi todos los eventos de entrada de juego hasta que vuelve al estado activo .Once in the WaitForInput state, the game stops processing almost all gameplay input events until it returns to the Active state. La excepción es el botón pausa, con una prensa que hace que el juego vuelva al estado activo.The exception is the pause button, with a press of this causing the game to go back to the active state. Aparte del botón pausa, para que el juego vuelva al estado activo , el reproductor debe seleccionar un elemento de menú.Other than the pause button, in order for the game to go back to the Active state the player needs to select a menu item.

Estado activoThe Active state

Durante el estado activo , la instancia de MoveLookController procesa eventos de todos los dispositivos de entrada habilitados e interpreta las intenciones del jugador.During the Active state, the MoveLookController instance is processing events from all enabled input devices and interpreting the player's intentions. Como resultado, actualiza la velocidad y la dirección de la apariencia de la vista del jugador y comparte los datos actualizados con el juego una vez que se llama a Update desde el bucle del juego.As a result, it updates the velocity and look direction of the player's view and shares the updated data with the game after Update is called from the game loop.

Se realiza un seguimiento de todas las entradas de puntero en estado activo , con distintos identificadores de puntero correspondientes a las diferentes acciones de puntero.All pointer input is tracked in the Active state, with different pointer IDs corresponding to different pointer actions. Cuando se recibe un evento PointerPressed, MoveLookController obtiene el valor del identificador del puntero creado por la ventana.When a PointerPressed event is received, the MoveLookController obtains the pointer ID value created by the window. El id. de puntero represente un tipo específico de entrada.The pointer ID represents a specific type of input. Por ejemplo, en un dispositivo multitoque, puede que haya varias entradas activas diferentes al mismo tiempo.For example, on a multi-touch device, there may be several different active inputs at the same time. Los identificadores se usan para llevar un seguimiento de la entrada que está usando el jugador.The IDs are used to keep track of which input the player is using. Si un evento está en el rectángulo de movimiento de la pantalla táctil, se asigna un identificador de puntero para realizar el seguimiento de los eventos de puntero en el rectángulo de movimiento.If one event is in the move rectangle of the touch screen, a pointer ID is assigned to track any pointer events in the move rectangle. Otros eventos de puntero en el rectángulo de disparo se siguen por separado, con id. de puntero diferentes.Other pointer events in the fire rectangle are tracked separately, with a separate pointer ID.

Nota

La entrada del mouse y el stick derecho de un controlador de juegos también tienen identificadores que se administran por separado.Input from the mouse and right thumbstick of a gamepad also have IDs that are handled separately.

Una vez que los eventos de puntero se han asignado a una acción específica del juego, es hora de actualizar los datos que el objeto MoveLookController comparte con el bucle de juego principal.After the pointer events have been mapped to a specific game action, it's time to update the data the MoveLookController object shares with the main game loop.

Cuando se llama, el método Update del juego de ejemplo procesa la entrada y actualiza las variables de velocidad y apariencia ( _ velocidad m y m _ LookDirection), que el bucle del juego recupera a continuación mediante una llamada a los métodos de velocidad pública y LookDirection .When called, the Update method in the sample game processes the input and updates the velocity and look direction variables (m_velocity and m_lookdirection), which the game loop then retrieves by calling the public Velocity and LookDirection methods.

Nota

Puede ver más detalles sobre el método de actualización más adelante en esta página.More details about the Update method can be seen later on this page.

El bucle de juego puede probar si el jugador está disparando llamando al método IsFiring en la instancia MoveLookController.The game loop can test to see if the player is firing by calling the IsFiring method on the MoveLookController instance. El MoveLookController comprueba para ver si el jugador ha presionado el botón de disparo en uno de los tres tipos de entrada.The MoveLookController checks to see if the player has pressed the fire button on one of the three input types.

bool MoveLookController::IsFiring()
{
    if (m_state == MoveLookControllerState::Active)
    {
        if (m_autoFire)
        {
            return (m_fireInUse || (m_mouseInUse && m_mouseLeftInUse) || PollingFireInUse());
        }
        else
        {
            if (m_firePressed)
            {
                m_firePressed = false;
                return true;
            }
        }
    }
    return false;
}

Veamos ahora la implementación de cada uno de los tres tipos de control con un poco más detalle.Now let's look at the implementation of each of the three control types in a little more detail.

Agregar controles de mouse relativosAdding relative mouse controls

Si se detecta el movimiento del mouse, queremos usar ese movimiento para determinar el nuevo tono y el guiñante de la cámara.If mouse movement is detected, we want to use that movement to determine the new pitch and yaw of the camera. Esto se logra implementando controles de ratón relativos, con los que controlamos la distancia que el ratón ha recorrido (el delta entre el inicio del movimiento hasta que el ratón se detiene ) en contraposición a grabar las coordenadas de píxel x-y absolutas del movimiento.We do that by implementing relative mouse controls, where we handle the relative distance the mouse has moved—the delta between the start of the movement and the stop—as opposed to recording the absolute x-y pixel coordinates of the motion.

Para ello, obtenemos los cambios en las coordenadas X (movimiento horizontal) e Y (movimiento vertical) examinando los campos MouseDelta::X y MouseDelta::Y del objeto de argumento Windows::Device::Input::MouseEventArgs::MouseDelta que el evento MouseMoved devuelve.To do that, we obtain the changes in the X (the horizontal motion) and the Y (the vertical motion) coordinates by examining the MouseDelta::X and MouseDelta::Y fields on the Windows::Device::Input::MouseEventArgs::MouseDelta argument object returned by the MouseMoved event.

void MoveLookController::OnMouseMoved(
    _In_ MouseDevice const& /* mouseDevice */,
    _In_ MouseEventArgs const& args
    )
{
    // Handle Mouse Input via dedicated relative movement handler.

    switch (m_state)
    {
    case MoveLookControllerState::Active:
        XMFLOAT2 mouseDelta;
        mouseDelta.x = static_cast<float>(args.MouseDelta().X);
        mouseDelta.y = static_cast<float>(args.MouseDelta().Y);

        XMFLOAT2 rotationDelta;
        // Scale for control sensitivity.
        rotationDelta.x = mouseDelta.x * MoveLookConstants::RotationGain;
        rotationDelta.y = mouseDelta.y * MoveLookConstants::RotationGain;

        // Update our orientation based on the command.
        m_pitch -= rotationDelta.y;
        m_yaw += rotationDelta.x;

        // Limit pitch to straight up or straight down.
        float limit = XM_PI / 2.0f - 0.01f;
        m_pitch = __max(-limit, m_pitch);
        m_pitch = __min(+limit, m_pitch);

        // Keep longitude in sane range by wrapping.
        if (m_yaw > XM_PI)
        {
            m_yaw -= XM_PI * 2.0f;
        }
        else if (m_yaw < -XM_PI)
        {
            m_yaw += XM_PI * 2.0f;
        }
        break;
    }
}

Incorporación de compatibilidad táctilAdding touch support

Los controles táctiles son excelentes para admitir usuarios con tabletas.Touch controls are great for supporting users with tablets. En este juego se recopilan entradas táctiles mediante el zoning de ciertas áreas de la pantalla con cada alineación con las acciones específicas del juego.This game gathers touch input by zoning off certain areas of the screen with each aligning to specific in-game actions. La entrada táctil de este juego usa tres zonas.This game's touch input uses three zones.

cambiar el diseño de la vista táctil

Los siguientes comandos Resumen el comportamiento del control Touch.The following commands summarize our touch control behavior. Entrada de usuarioUser input | AcciónAction :------- | :-------- Cambiar rectánguloMove rectangle | La entrada táctil se convierte en un joystick virtual en el que el movimiento vertical se traducirá en el movimiento de posición hacia delante o hacia atrás y el movimiento horizontal se traducirá en el movimiento de la posición izquierda/derecha.Touch input is converted into a virtual joystick where the vertical motion will be translated into forward/backward position motion and horizontal motion will be translated into left/right position motion. Desencadenar rectánguloFire rectangle | Desencadene una esfera.Fire a sphere. Toque fuera del rectángulo de movimiento y fuegoTouch outside of move and fire rectangle | Cambie la rotación (el tono y la guiñada) de la vista de cámara.Change the rotation (the pitch and yaw) of the camera view.

El MoveLookController comprueba el id. del puntero para determinar dónde se produjo el evento y realiza una de las siguientes acciones:The MoveLookController checks the pointer ID to determine where the event occurred, and takes one of the following actions:

  • Si se produjo el evento PointerMoved en el rectángulo de disparo o movimiento, actualiza la posición de puntero para el controlador.If the PointerMoved event occurred in the move or fire rectangle, update the pointer position for the controller.
  • Si el evento PointerMoved se produjo en alguna otra parte de la pantalla (definida como los controles de apariencia), calcula el cambio en la rotación alrededor del eje x y del eje y del vector de dirección que corresponde a la vista.If the PointerMoved event occurred somewhere in the rest of the screen (defined as the look controls), calculate the change in pitch and yaw of the look direction vector.

Una vez que hemos implementado nuestros controles táctiles, los rectángulos dibujados anteriormente con Direct2D indicarán a los jugadores dónde están las zonas de movimiento, incendio y búsqueda.Once we've implemented our touch controls, the rectangles we drew earlier using Direct2D will indicate to players where the move, fire, and look zones are.

Touch (controles)

Ahora, echemos un vistazo a cómo se implementa cada control.Now let's take a look at how we implement each control.

Controlador de movimiento y activaciónMove and fire controller

El rectángulo del controlador de movimiento en el cuadrante inferior izquierdo de la pantalla se utiliza como Panel direccional.The move controller rectangle in the lower left quadrant of the screen is used as a directional pad. Deslizar el control de posición a la izquierda y a la derecha dentro de este espacio mueve el jugador hacia la izquierda y la derecha, mientras que hacia arriba y hacia abajo se mueve la cámara hacia delante y hacia atrásSliding your thumb left and right within this space moves the player left and right, while up and down moves the camera forward and backward. Después de configurar esta opción, al pulsar el controlador de incendios en el cuadrante inferior derecho de la pantalla se activa una esfera.After setting this up, tapping the fire controller in the lower right quadrant of the screen fires a sphere.

Los métodos SetMoveRect y SetFireRect crean nuestros rectángulos de entrada, tomando dos vectores de 2D para especificar las posiciones de las esquinas superior izquierda e inferior derecha de cada rectángulo en la pantalla.The SetMoveRect and SetFireRect methods create our input rectangles, taking two, 2D vectors to specify each rectangles' upper left and lower right corner positions on the screen.

Después, los parámetros se asignan a m _ fireUpperLeft y m _ fireLowerRight que nos ayudarán a determinar si el usuario está tocando dentro de los rectángulos.The parameters are then assigned to m_fireUpperLeft and m_fireLowerRight that will help us determine if the user is touching within on of the rectangles.

m_fireUpperLeft = upperLeft;
m_fireLowerRight = lowerRight;

Si se cambia el tamaño de la pantalla, estos rectángulos se vuelven a dibujar en el tamaño de approperiate.If the screen is resized, these rectangles are redrawn to the approperiate size.

Ahora que hemos dividido en zonas los controles, es el momento de determinar cuándo un usuario los está usando.Now that we've zoned off our controls, it's time to determine when a user is actually using them. Para ello, configuramos algunos controladores de eventos en el método MoveLookController:: InitWindow para cuando el usuario presiona, mueve o suelta su puntero.To do this, we set up some event handlers in the MoveLookController::InitWindow method for when the user presses, moves, or releases their pointer.

window.PointerPressed({ this, &MoveLookController::OnPointerPressed });

window.PointerMoved({ this, &MoveLookController::OnPointerMoved });

window.PointerReleased({ this, &MoveLookController::OnPointerReleased });

En primer lugar, determinamos lo que sucede cuando el usuario presiona en los rectángulos de movimiento o activación con el método OnPointerPressed .We'll first determine what happens when the user first presses within the move or fire rectangles using the OnPointerPressed method. Aquí comprobamos el lugar en el que tocan un control y si un puntero ya está en ese controlador.Here we check where they're touching a control and if a pointer is already in that controller. Si este es el primer dedo para tocar el control específico, hacemos lo siguiente.If this is the first finger to touch the specific control, we do the following.

  • Almacene la ubicación de touchdown en m _ moveFirstDown o m _ fireFirstDown como vector 2D.Store the location of the touchdown in m_moveFirstDown or m_fireFirstDown as a 2D vector.
  • Asigne el ID. de puntero a m _ movePointerID o m _ firePointerID.Assign the pointer ID to m_movePointerID or m_firePointerID.
  • Establezca la marca inuse adecuada (m _ moveInUse o m _ fireInUse) en true , ya que ahora tenemos un puntero activo para ese control.Set the proper InUse flag (m_moveInUse or m_fireInUse) to true since we now have an active pointer for that control.
PointerPoint point = args.CurrentPoint();
uint32_t pointerID = point.PointerId();
Point pointerPosition = point.Position();
PointerPointProperties pointProperties = point.Properties();
auto pointerDevice = point.PointerDevice();
auto pointerDeviceType = pointerDevice.PointerDeviceType();

XMFLOAT2 position = XMFLOAT2(pointerPosition.X, pointerPosition.Y);

...
case MoveLookControllerState::Active:
    switch (pointerDeviceType)
    {
    case winrt::Windows::Devices::Input::PointerDeviceType::Touch:
        // Check to see if this pointer is in the move control.
        if (position.x > m_moveUpperLeft.x &&
            position.x < m_moveLowerRight.x &&
            position.y > m_moveUpperLeft.y &&
            position.y < m_moveLowerRight.y)
        {
            // If no pointer is in this control yet.
            if (!m_moveInUse)
            {
                // Process a DPad touch down event.
                // Save the location of the initial contact
                m_moveFirstDown = position;
                // Store the pointer using this control
                m_movePointerID = pointerID;
                // Set InUse flag to signal there is an active move pointer
                m_moveInUse = true;
            }
        }
        // Check to see if this pointer is in the fire control.
        else if (position.x > m_fireUpperLeft.x &&
            position.x < m_fireLowerRight.x &&
            position.y > m_fireUpperLeft.y &&
            position.y < m_fireLowerRight.y)
        {
            if (!m_fireInUse)
            {
                // Save the location of the initial contact
                m_fireLastPoint = position;
                // Store the pointer using this control
                m_firePointerID = pointerID;
                // Set InUse flag to signal there is an active fire pointer
                m_fireInUse = true;
                ...
            }
        }
        ...

Ahora que hemos determinado si el usuario está tocando un control de movimiento o fuego, vemos si el jugador está realizando movimientos con el dedo presionado.Now that we've determined whether the user is touching a move or fire control, we see if the player is making any movements with their pressed finger. Con el método MoveLookController:: OnPointerMoved , comprobamos qué puntero se ha desactivado y, a continuación, almacenamos su nueva posición como vector 2D.Using the MoveLookController::OnPointerMoved method, we check what pointer has moved and then store its new position as a 2D vector.

PointerPoint point = args.CurrentPoint();
uint32_t pointerID = point.PointerId();
Point pointerPosition = point.Position();
PointerPointProperties pointProperties = point.Properties();
auto pointerDevice = point.PointerDevice();

// convert to allow math
XMFLOAT2 position = XMFLOAT2(pointerPosition.X, pointerPosition.Y);

switch (m_state)
{
case MoveLookControllerState::Active:
    // Decide which control this pointer is operating.

    // Move control
    if (pointerID == m_movePointerID)
    {
        // Save the current position.
        m_movePointerPosition = position;
    }
    // Look control
    else if (pointerID == m_lookPointerID)
    {
        ...
    }
    // Fire control
    else if (pointerID == m_firePointerID)
    {
        m_fireLastPoint = position;
    }
    ...

Una vez que el usuario ha realizado sus gestos dentro de los controles, liberará el puntero.Once the user has made their gestures within the controls, they'll release the pointer. Con el método MoveLookController:: OnPointerReleased , determinamos qué puntero se ha liberado y realizan una serie de restablecimientos.Using the MoveLookController::OnPointerReleased method, we determine which pointer has been released and do a series of resets.

Si se ha lanzado el control de movimiento, hacemos lo siguiente.If the move control has been released, we do the following.

  • Establezca la velocidad del reproductor 0 en en todas las direcciones para evitar que se muevan en el juego.Set the velocity of the player to 0 in all directions to prevent them from moving in the game.
  • Cambie m _ moveInUse a false porque el usuario ya no toca el controlador de movimiento.Switch m_moveInUse to false since the user is no longer touching the move controller.
  • Establezca el identificador de puntero de movimiento en porque ya no 0 hay un puntero en el controlador de movimiento.Set the move pointer ID to 0 since there's no longer a pointer in the move controller.
if (pointerID == m_movePointerID)
{
    // Stop on release.
    m_velocity = XMFLOAT3(0, 0, 0);
    m_moveInUse = false;
    m_movePointerID = 0;
}

En el caso del control Fire, si se ha lanzado todo lo que hacemos es cambiar el indicador de m_fireInUse a false y el identificador del puntero de activación a 0 , ya que ya no hay un puntero en el control de incendios.For the fire control, if it has been released all we do is switch the m_fireInUse flag to false and the fire pointer ID to 0 since there's no longer a pointer in the fire control.

else if (pointerID == m_firePointerID)
{
    m_fireInUse = false;
    m_firePointerID = 0;
}

Buscar controladorLook controller

Trataremos los eventos de puntero de dispositivo táctil para las regiones sin usar de la pantalla como controlador de búsqueda.We treat touch device pointer events for the unused regions of the screen as the look controller. Deslizar el dedo alrededor de esta zona cambia el tono y la guiñada (rotación) de la cámara del jugador.Sliding your finger around this zone changes the pitch and yaw (rotation) of the player camera.

Si el evento MoveLookController:: OnPointerPressed se genera en un dispositivo táctil en esta región y el estado del juego se establece en activo, se le asigna un identificador de puntero.If the MoveLookController::OnPointerPressed event is raised on a touch device in this region and the game state is set to Active, it's assigned a pointer ID.

// If no pointer is in this control yet.
if (!m_lookInUse)
{
    // Save point for later move.
    m_lookLastPoint = position;
    // Store the pointer using this control.
    m_lookPointerID = pointerID;
    // These are for smoothing.
    m_lookLastDelta.x = m_lookLastDelta.y = 0;
    m_lookInUse = true;
}

Aquí, el MoveLookController asigna el identificador de puntero para el puntero que desencadenó el evento a una variable específica que corresponde a la región de búsqueda.Here the MoveLookController assigns the pointer ID for the pointer that fired the event to a specific variable that corresponds to the look region. En el caso de que se produzca una entrada táctil en la región de búsqueda, la variable m _ lookPointerID se establece en el identificador de puntero que desencadenó el evento.In the case of a touch occurring in the look region, the m_lookPointerID variable is set to the pointer ID that fired the event. También se establece una variable booleana, m _ lookInUse, para indicar que aún no se ha liberado el control.A boolean variable, m_lookInUse, is also set to indicate that the control has not yet been released.

Ahora, echemos un vistazo a cómo el juego de ejemplo controla el evento de pantalla táctil de PointerMoved .Now, let's look at how the sample game handles the PointerMoved touch screen event.

Dentro del método MoveLookController:: OnPointerMoved , comprobamos qué tipo de identificador de puntero se ha asignado al evento.Within the MoveLookController::OnPointerMoved method, we check to see what kind of pointer ID has been assigned to the event. Si es m_lookPointerID, se calcula el cambio en la posición del puntero.If it's m_lookPointerID, we calculate the change in position of the pointer. A continuación, usamos este delta para calcular cuánto debe cambiar la rotación.We then use this delta to calculate how much the rotation should change. Por último, vamos a un punto en el que podemos actualizar el ** _ tono m** y m _ guiñada para que se use en el juego para cambiar la rotación del jugador.Finally we're at a point where we can update the m_pitch and m_yaw to be used in the game to change the player rotation.

// This is the look pointer.
else if (pointerID == m_lookPointerID)
{
    // Look control.
    XMFLOAT2 pointerDelta;
    // How far did the pointer move?
    pointerDelta.x = position.x - m_lookLastPoint.x;
    pointerDelta.y = position.y - m_lookLastPoint.y;

    XMFLOAT2 rotationDelta;
    // Scale for control sensitivity.
    rotationDelta.x = pointerDelta.x * MoveLookConstants::RotationGain;
    rotationDelta.y = pointerDelta.y * MoveLookConstants::RotationGain;
    // Save for next time through.
    m_lookLastPoint = position;

    // Update our orientation based on the command.
    m_pitch -= rotationDelta.y;
    m_yaw += rotationDelta.x;

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

La última parte que veremos es cómo el juego de ejemplo controla el evento de pantalla táctil de PointerReleased .The last piece we'll look at is how the sample game handles the PointerReleased touch screen event. Una vez que el usuario ha terminado el gesto de toque y quitado el dedo de la pantalla, se inicia MoveLookController:: OnPointerReleased .Once the user has finished the touch gesture and removed their finger from the screen, MoveLookController::OnPointerReleased is initiated. Si el identificador del puntero que desencadenó el evento PointerReleased es el identificador del puntero de movimiento grabado anteriormente, MoveLookController establece la velocidad en 0 porque el reproductor dejó de tocar el área de búsqueda.If the ID of the pointer that fired the PointerReleased event is the ID of the previously recorded move pointer, the MoveLookController sets the velocity to 0 because the player has stopped touching the look area.

else if (pointerID == m_lookPointerID)
{
    m_lookInUse = false;
    m_lookPointerID = 0;
}

Agregar compatibilidad con el mouse y el tecladoAdding mouse and keyboard support

Este juego tiene el siguiente diseño de control para el teclado y el mouse.This game has the following control layout for keyboard and mouse.

Entrada de usuarioUser input AcciónAction
WW Avanzar el reproductorMove player forward
AA Mueve el jugador a la izquierdaMove player left
SS Bajar reproductor hacia atrásMove player backward
DD Movimiento del jugador a la derechaMove player right
XX Subir vistaMove view up
Barra espaciadoraSpace bar Bajar vistaMove view down
PP Pausar el juegoPause the game
Movimiento del mouseMouse movement Cambiar la rotación (el tono y la guiñada) de la vista de cámaraChange the rotation (the pitch and yaw) of the camera view
Botón primario del mouseLeft mouse button Activar una esferaFire a sphere

Para usar el teclado, el juego de ejemplo registra dos nuevos eventos, CoreWindow:: KeyUp y CoreWindow:: KeyDown, dentro del método MoveLookController:: InitWindow .To use the keyboard, the sample game registers two new events, CoreWindow::KeyUp and CoreWindow::KeyDown, within the MoveLookController::InitWindow method. Estos eventos controlan la prensa y la liberación de una clave.These events handle the press and release of a key.

window.KeyDown({ this, &MoveLookController::OnKeyDown });

window.KeyUp({ this, &MoveLookController::OnKeyUp });

El mouse se trata de forma ligeramente diferente a los controles táctiles aunque use un puntero.The mouse is treated a little differently from the touch controls even though it uses a pointer. Para alinear con nuestro diseño de control, el MoveLookController gira la cámara cada vez que se mueve el mouse y se activa cuando se presiona el botón primario del mouse.To align with our control layout, the MoveLookController rotates the camera whenever the mouse is moved, and fires when the left mouse button is pressed.

Esto se controla en el método OnPointerPressed de MoveLookController.This is handled in the OnPointerPressed method of the MoveLookController.

En este método, comprobamos qué tipo de dispositivo de puntero se usa con la Windows::Devices::Input::PointerDeviceType enumeración.In this method we check to see what type of pointer device is being used with the Windows::Devices::Input::PointerDeviceType enum. Si el juego está activo y el PointerDeviceType no está en contacto, se supone que es la entrada del mouse.If the game is Active and the PointerDeviceType isn't Touch, we assume it's mouse input.

case MoveLookControllerState::Active:
    switch (pointerDeviceType)
    {
    case winrt::Windows::Devices::Input::PointerDeviceType::Touch:
        // Behavior for touch controls
        ...

    default:
        // Behavior for mouse controls
        bool rightButton = pointProperties.IsRightButtonPressed();
        bool leftButton = pointProperties.IsLeftButtonPressed();

        if (!m_autoFire && (!m_mouseLeftInUse && leftButton))
        {
            m_firePressed = true;
        }

        if (!m_mouseInUse)
        {
            m_mouseInUse = true;
            m_mouseLastPoint = position;
            m_mousePointerID = pointerID;
            m_mouseLeftInUse = leftButton;
            m_mouseRightInUse = rightButton;
            // These are for smoothing.
            m_lookLastDelta.x = m_lookLastDelta.y = 0;
        }
        break;
    }
    break;

Cuando el reproductor deja de presionar uno de los botones del mouse, se genera el evento CoreWindow::P ointerreleased del mouse, llamando al método MoveLookController:: OnPointerReleased y se completa la entrada.When the player stops pressing one of the mouse buttons, the CoreWindow::PointerReleased mouse event is raised, calling the MoveLookController::OnPointerReleased method, and the input is complete. En este momento, las esferas dejarán de activarse si se presiona el botón primario del mouse y ahora se libera.At this point, spheres will stop firing if the left mouse button was being pressed and is now released. Dado que la opción Buscar siempre está habilitada, el juego sigue usando el mismo puntero del mouse para realizar el seguimiento de los eventos de búsqueda en curso.Because look is always enabled, the game continues to use the same mouse pointer to track the ongoing look events.

case MoveLookControllerState::Active:
    // Touch points
    if (pointerID == m_movePointerID)
    {
        // Stop movement
        ...
    }
    else if (pointerID == m_lookPointerID)
    {
        // Stop look rotation
        ...
    }
    // Fire button has been released
    else if (pointerID == m_firePointerID)
    {
        // Stop firing
        ...
    }
    // Mouse point
    else if (pointerID == m_mousePointerID)
    {
        bool rightButton = pointProperties.IsRightButtonPressed();
        bool leftButton = pointProperties.IsLeftButtonPressed();

        // Mouse no longer in use so stop firing
        m_mouseInUse = false;

        // Don't clear the mouse pointer ID so that Move events still result in Look changes.
        // m_mousePointerID = 0;
        m_mouseLeftInUse = leftButton;
        m_mouseRightInUse = rightButton;
    }
    break;

Ahora veamos el último tipo de control que vamos a admitir: los controladores de juegos.Now let's look at the last control type we'll be supporting: gamepads. Los controladores de juegos se controlan de forma independiente de los controles táctiles y del mouse, ya que no usan el objeto de puntero.Gamepads are handled separately from the touch and mouse controls since they doesn't use the pointer object. Por este motivo, es necesario agregar algunos nuevos métodos y controladores de eventos.Because of this, a few new event handlers and methods will need to be added.

Agregar compatibilidad con el controlador para juegosAdding gamepad support

Para este juego, se agrega compatibilidad con el controlador de juegos mediante llamadas a las API de Windows. Gaming. Input .For this game, gamepad support is added by calls to the Windows.Gaming.Input APIs. Este conjunto de API proporciona acceso a entradas del controlador de juego, como ruedas de carreras y bastones de vuelo.This set of APIs provides access to game controller inputs like racing wheels and flight sticks.

Los siguientes son los controles del controlador para juegos.The following will be our gamepad controls.

Entrada de usuarioUser input AcciónAction
Stick analógico izquierdoLeft analog stick Movimiento del reproductorMove player
Stick analógico derechoRight analog stick Cambiar la rotación (el tono y la guiñada) de la vista de cámaraChange the rotation (the pitch and yaw) of the camera view
Gatillo derechoRight trigger Activar una esferaFire a sphere
Botón Inicio/menúStart/Menu button Pausar o reanudar el juegoPause or resume the game

En el método InitWindow , se agregan dos nuevos eventos para determinar si se ha agregado o quitadoun controlador de juegos.In the InitWindow method, we add two new events to determine if a gamepad has been added or removed. Estos eventos actualizan la propiedad m_gamepadsChanged .These events update the m_gamepadsChanged property. Se usa en el método UpdatePollingDevices para comprobar si la lista de controladores de mandos conocidos ha cambiado.This is used in the UpdatePollingDevices method to check if the list of known gamepads has changed.

// Detect gamepad connection and disconnection events.
Gamepad::GamepadAdded({ this, &MoveLookController::OnGamepadAdded });

Gamepad::GamepadRemoved({ this, &MoveLookController::OnGamepadRemoved });

Nota

Las aplicaciones UWP no pueden recibir la entrada desde un controlador de Xbox One mientras la aplicación no está en el foco.UWP apps cannot receive input from an Xbox One Controller while the app is not in focus.

El método UpdatePollingDevicesThe UpdatePollingDevices method

El método UpdatePollingDevices de la instancia de MoveLookController comprueba inmediatamente si hay un controlador de juegos conectado.The UpdatePollingDevices method of the MoveLookController instance immediately checks to see if a gamepad is connected. Si es así, comenzaremos a leer su estado con el controlador para juegos. GetCurrentReading.If one is, we'll start reading its state with Gamepad.GetCurrentReading. Esto devuelve el struct GamepadReading , lo que nos permite comprobar qué botones se han pulsado o thumbsticks.This returns the GamepadReading struct, allowing us to check what buttons have been clicked or thumbsticks moved.

Si el estado del juego es WaitForInput, solo escuchamos el botón de inicio o menú del controlador para que se pueda reanudar el juego.If the state of the game is WaitForInput, we only listen for the Start/Menu button of the controller so that the game can be resumed.

Si está activo, comprobamos la entrada del usuario y determinamos qué acción del juego debe ocurrir.If it's Active, we check the user's input and determine what in-game action needs to happen. Por ejemplo, si el usuario moviera el stick analógico izquierdo en una dirección específica, esto permite que el juego sepa que necesitamos trasladar el jugador en la dirección en la que se mueve el stick.For instance, if the user moved the left analog stick in a specific direction, this lets the game know we need to move the player in the direction the stick is being moved. El movimiento del palo en una dirección específica debe registrarse como mayor que el radio de la zona muerta; de lo contrario, no se producirá nada.The movement of the stick in a specific direction must register as larger than the radius of the dead zone; otherwise, nothing will happen. Este radio de zona muerta es necesario para evitar la "derivación", que es cuando el controlador recoge movimientos pequeños desde el pulgar del jugador a medida que se sitúa sobre el palo.This dead zone radius is necessary to prevent "drifting," which is when the controller picks up small movements from the player's thumb as it rests on the stick. Sin zonas muertas, los controles pueden parecer demasiado confidenciales para el usuario.Without dead zones, the controls can appear too sensitive to the user.

La entrada del stick analógico está entre-1 y 1 para el eje x e y.Thumbstick input is between -1 and 1 for both the x and y axis. El siguiente consant especifica el radio de la zona muerta del Stick.The following consant specifies the radius of the thumbstick dead zone.

#define THUMBSTICK_DEADZONE 0.25f

Con esta variable, comenzaremos a procesar la entrada de interbloqueo accionable.Using this variable, we'll then begin processing actionable thumbstick input. El movimiento se produciría con un valor de [-1,-.26] o [. 26, 1] en cualquiera de los dos ejes.Movement would occur with a value from [-1, -.26] or [.26, 1] on either axis.

zona muerta para thumbsticks

Esta parte del método UpdatePollingDevices controla las thumbsticks izquierda y derecha.This piece of the UpdatePollingDevices method handles the left and right thumbsticks. Se comprueban los valores X e y de cada Stick para ver si están fuera de la zona muerta.Each stick's X and Y values are checked to see if they are outside of the dead zone. Si uno o ambos son, actualizaremos el componente correspondiente.If one or both are, we'll update the corresponding component. Por ejemplo, si el stick izquierdo se mueve a la izquierda a lo largo del eje X, agregaremos-1 al componente X del vector de m_moveCommand .For example, if the left thumbstick is being moved left along the X axis, we'll add -1 to the x component of the m_moveCommand vector. Este vector es lo que se usará para agregar todos los movimientos en todos los dispositivos y se usará más adelante para calcular dónde debe moverse el reproductor.This vector is what will be used to aggregate all movements across all devices and will later be used to calculate where the player should move.

// Use the left thumbstick to control the eye point position
// (position of the player).

// Check if left thumbstick is outside of dead zone on x axis
if (reading.LeftThumbstickX > THUMBSTICK_DEADZONE ||
    reading.LeftThumbstickX < -THUMBSTICK_DEADZONE)
{
    // Get value of left thumbstick's position on x axis
    float x = static_cast<float>(reading.LeftThumbstickX);
    // Set the x of the move vector to 1 if the stick is being moved right.
    // Set to -1 if moved left. 
    m_moveCommand.x -= (x > 0) ? 1 : -1;
}

// Check if left thumbstick is outside of dead zone on y axis
if (reading.LeftThumbstickY > THUMBSTICK_DEADZONE ||
    reading.LeftThumbstickY < -THUMBSTICK_DEADZONE)
{
    // Get value of left thumbstick's position on y axis
    float y = static_cast<float>(reading.LeftThumbstickY);
    // Set the y of the move vector to 1 if the stick is being moved forward.
    // Set to -1 if moved backwards.
    m_moveCommand.y += (y > 0) ? 1 : -1;
}

De forma similar a cómo se mueve el stick izquierdo, el stick derecho controla el giro de la cámara.Similar to how the left stick controls movement, the right stick controls the rotation of the camera.

El comportamiento del stick de control deslizante derecho se alinea con el comportamiento del movimiento del mouse en la configuración del mouse y del control del teclado.The right thumb stick behavior aligns with the behavior of mouse movement in our mouse and keyboard control setup. Si el palo está fuera de la zona muerta, se calcula la diferencia entre la posición actual del puntero y el lugar en el que el usuario está intentando buscar.If the stick is outside of the dead zone, we calculate the difference between the current pointer position and where the user is now trying to look. Este cambio en la posición del puntero (pointerDelta) se usa para actualizar el tono y la guiñada de la rotación de la cámara que posteriormente se aplica en nuestro método de actualización .This change in pointer position (pointerDelta) is then used to update the pitch and yaw of the camera rotation that later get applied in our Update method. El vector pointerDelta puede resultar familiar porque también se usa en el método MoveLookController:: OnPointerMoved para realizar un seguimiento del cambio en la posición del puntero para nuestras entradas táctiles y del mouse.The pointerDelta vector may look familiar because it's also used in the MoveLookController::OnPointerMoved method to keep track of change in pointer position for our mouse and touch inputs.

// Use the right thumbstick to control the look at position

XMFLOAT2 pointerDelta;

// Check if right thumbstick is outside of deadzone on x axis
if (reading.RightThumbstickX > THUMBSTICK_DEADZONE ||
    reading.RightThumbstickX < -THUMBSTICK_DEADZONE)
{
    float x = static_cast<float>(reading.RightThumbstickX);
    // Register the change in the pointer along the x axis
    pointerDelta.x = x * x * x;
}
// No actionable thumbstick movement. Register no change in pointer.
else
{
    pointerDelta.x = 0.0f;
}
// Check if right thumbstick is outside of deadzone on y axis
if (reading.RightThumbstickY > THUMBSTICK_DEADZONE ||
    reading.RightThumbstickY < -THUMBSTICK_DEADZONE)
{
    float y = static_cast<float>(reading.RightThumbstickY);
    // Register the change in the pointer along the y axis
    pointerDelta.y = y * y * y;
}
else
{
    pointerDelta.y = 0.0f;
}

XMFLOAT2 rotationDelta;
// Scale for control sensitivity.
rotationDelta.x = pointerDelta.x * 0.08f;
rotationDelta.y = pointerDelta.y * 0.08f;

// Update our orientation based on the command.
m_pitch += rotationDelta.y;
m_yaw += rotationDelta.x;

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

Los controles del juego no se completarán sin la capacidad de activar los esferas.The game's controls wouldn't be complete without the ability to fire spheres!

Este método UpdatePollingDevices también comprueba si se está presionando el desencadenador derecho.This UpdatePollingDevices method also checks if the right trigger is being pressed. Si es así, la propiedad m_firePressed se voltea a true, señalando al Juego de que las esferas deben empezar a desencadenarse.If it is, our m_firePressed property is flipped to true, signaling to the game that spheres should start firing.

if (reading.RightTrigger > TRIGGER_DEADZONE)
{
    if (!m_autoFire && !m_gamepadTriggerInUse)
    {
        m_firePressed = true;
    }

    m_gamepadTriggerInUse = true;
}
else
{
    m_gamepadTriggerInUse = false;
}

El método UpdateThe Update method

Para envolver cosas, vamos a profundizar en el método de actualización .To wrap things up, let's dig deeper into the Update method. Este método combina los movimientos o los giros que el jugador realizó con cualquier entrada admitida para generar un vector de velocidad y actualizar los valores de paso y guiñada para que el bucle del juego tenga acceso.This method merges any movements or rotations that the player made with any supported input to generate a velocity vector and update our pitch and yaw values for our game loop to access.

El método Update inicia las cosas mediante una llamada a UpdatePollingDevices para actualizar el estado del controlador.The Update method kicks things off by calling UpdatePollingDevices to update the state of the controller. Este método también recopila cualquier entrada de un controlador de juegos y agrega sus movimientos al vector m_moveCommand .This method also gathers any input from a gamepad and adds its movements to the m_moveCommand vector.

En nuestro método de actualización , realizamos las siguientes comprobaciones de entrada.In our Update method we then perform the following input checks.

  • Si el reproductor usa el rectángulo del controlador de movimiento, determinaremos el cambio en la posición del puntero y lo usaremos para calcular si el usuario ha movido el puntero fuera de la zona muerta del controlador.If the player is using the move controller rectangle, we'll then determine the change in pointer position and use that to calculate if the user has moved the pointer out of the controller's dead zone. Si está fuera de la zona muerta, la propiedad vector de m_moveCommand se actualiza con el valor del joystick virtual.If outside of the dead zone, the m_moveCommand vector property is then updated with the virtual joystick value.
  • Si se presiona cualquiera de las entradas de teclado de movimiento, se agrega un valor de 1.0f o -1.0f en el componente correspondiente del vector de m_moveCommand1.0f para reenviar y -1.0f hacia atrás.If any of the movement keyboard inputs are pressed, a value of 1.0f or -1.0f are added in the corresponding component of the m_moveCommand vector—1.0f for forward, and -1.0f for backward.

Una vez que se ha tenido en cuenta toda la entrada de movimiento, se ejecuta el vector de m_moveCommand a través de algunos cálculos para generar un nuevo vector que representa la dirección del jugador con respecto al mundo de juego.Once all movement input has been taken into account, we then run the m_moveCommand vector through some calculations to generate a new vector that represents the direction of the player with regards to the game world. A continuación, tomamos nuestros movimientos en relación con el mundo y los aplicamos al reproductor como velocidad en esa dirección.We then take our movements in relation to the world and apply them to the player as velocity in that direction. Por último, se restablece el vector de m_moveCommand en para (0.0f, 0.0f, 0.0f) que todo esté listo para el siguiente fotograma del juego.Finally we reset the m_moveCommand vector to (0.0f, 0.0f, 0.0f) so that everything is ready for the next game frame.

void MoveLookController::Update()
{
    // Get any gamepad input and update state
    UpdatePollingDevices();

    if (m_moveInUse)
    {
        // Move control.
        XMFLOAT2 pointerDelta;

        pointerDelta.x = m_movePointerPosition.x - m_moveFirstDown.x;
        pointerDelta.y = m_movePointerPosition.y - m_moveFirstDown.y;

        // Figure out the command from the virtual joystick.
        XMFLOAT3 commandDirection = XMFLOAT3(0.0f, 0.0f, 0.0f);
        // Leave 32 pixel-wide dead spot for being still.
        if (fabsf(pointerDelta.x) > 16.0f)
            m_moveCommand.x -= pointerDelta.x / fabsf(pointerDelta.x);

        if (fabsf(pointerDelta.y) > 16.0f)
            m_moveCommand.y -= pointerDelta.y / fabsf(pointerDelta.y);
    }

    // Poll our state bits 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 45deg cases are not faster.
    if (fabsf(m_moveCommand.x) > 0.1f ||
        fabsf(m_moveCommand.y) > 0.1f ||
        fabsf(m_moveCommand.z) > 0.1f)
    {
        XMStoreFloat3(&m_moveCommand, XMVector3Normalize(XMLoadFloat3(&m_moveCommand)));
    }

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

    // Scale for sensitivity adjustment.
    // Our velocity is based on the command. Y is up.
    m_velocity.x = -wCommand.x * MoveLookConstants::MovementGain;
    m_velocity.z = wCommand.y * MoveLookConstants::MovementGain;
    m_velocity.y = wCommand.z * MoveLookConstants::MovementGain;

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

Pasos siguientesNext steps

Ahora que hemos agregado nuestros controles, hay otra característica que necesitamos agregar para crear un juego envolvente: sonido.Now that we have added our controls, there's another feature we need to add to create an immersive game: sound! La música y los efectos sonoros son importantes para cualquier juego, así que vamos a analizar Cómo agregar el sonido siguiente.Music and sound effects are important to any game, so let's discuss adding sound next.