Compartir a través de


Dispositivo de juego sin procesar

En esta página se describen los conceptos básicos de la programación para casi cualquier tipo de controlador de juego mediante Windows.Gaming.Input.RawGameController y las API relacionadas para la Plataforma universal de Windows (UWP).

Al leer esta página, aprenderá lo siguiente:

  • cómo recopilar una lista de controladores de juegos sin procesar conectados y sus usuarios
  • cómo detectar que se ha agregado o quitado un controlador de juego sin procesar
  • cómo obtener las funcionalidades de un controlador de juego sin procesar
  • cómo leer la entrada de un controlador de juego sin procesar

Información general

Un controlador de juego sin procesar es una representación genérica de un controlador de juego, con entradas encontradas en muchos tipos diferentes de controladores de juego comunes. Estas entradas se exponen como matrices simples de botones, modificadores y ejes sin nombre. Con un controlador de juego sin procesar, puedes permitir que los clientes creen asignaciones de entrada personalizadas independientemente del tipo de controlador que usen.

La clase RawGameController está realmente pensada para escenarios en los que las otras clases de entrada (ArcadeStick, FlightStick, etc.) no satisfacen tus necesidades, si quieres algo más genérico, anticipando que los clientes usarán muchos tipos diferentes de controladores de juegos, entonces esta clase es para ti.

Detectar y realizar un seguimiento de los controladores de juego sin procesar

La detección y el seguimiento de controladores de juegos sin procesar funciona exactamente de la misma manera que para los controladores para juegos, excepto con la clase RawGameController en lugar de la clase Gamepad . Consulte Controlador para juegos y vibración para obtener más información.

Obtener las funcionalidades de un controlador de juego sin procesar

Después de identificar el controlador de juego sin procesar que te interesa, puedes recopilar información sobre las funcionalidades del controlador. Puedes obtener el número de botones en el controlador de juego sin procesar con RawGameController.ButtonCount, el número de ejes analógicos con RawGameController.AxisCount y el número de modificadores con RawGameController.SwitchCount. Además, puedes obtener el tipo de un conmutador mediante RawGameController.GetSwitchKind.

En el ejemplo siguiente se obtienen los recuentos de entrada de un controlador de juego sin procesar:

auto rawGameController = myRawGameControllers->GetAt(0);
int buttonCount = rawGameController->ButtonCount;
int axisCount = rawGameController->AxisCount;
int switchCount = rawGameController->SwitchCount;

En el ejemplo siguiente se determina el tipo de cada modificador:

for (uint32_t i = 0; i < switchCount; i++)
{
    GameControllerSwitchKind mySwitch = rawGameController->GetSwitchKind(i);
}

Leer el controlador de juego sin procesar

Después de conocer el número de entradas en un controlador de juego sin procesar, estás listo para recopilar la entrada de ella. Sin embargo, a diferencia de otros tipos de entrada a los que podría usarse, un controlador de juego sin procesar no comunica el cambio de estado mediante la generación de eventos. En su lugar, se toman lecturas regulares de su estado actual sondeándolo .

Sondear el controlador de juego sin procesar

El sondeo captura una instantánea del controlador de juego sin procesar en un momento preciso. Este enfoque para la recopilación de entradas es una buena opción para la mayoría de los juegos, ya que su lógica normalmente se ejecuta en un bucle determinista en lugar de estar controlado por eventos. También suele ser más sencillo interpretar los comandos de juego de la entrada recopiladas a la vez que de muchas entradas únicas recopiladas a lo largo del tiempo.

Sondeas un controlador de juego sin procesar llamando a RawGameController.GetCurrentReading. Esta función rellena matrices para botones, modificadores y ejes que contienen el estado del controlador de juego sin procesar.

En el ejemplo siguiente se sondea un controlador de juego sin procesar por su estado actual:

Platform::Array<bool>^ currentButtonReading =
    ref new Platform::Array<bool>(buttonCount);

Platform::Array<GameControllerSwitchPosition>^ currentSwitchReading =
    ref new Platform::Array<GameControllerSwitchPosition>(switchCount);

Platform::Array<double>^ currentAxisReading = ref new Platform::Array<double>(axisCount);

rawGameController->GetCurrentReading(
    currentButtonReading,
    currentSwitchReading,
    currentAxisReading);

No hay ninguna garantía de qué posición en cada matriz contendrá el valor de entrada entre diferentes tipos de controladores, por lo que deberá comprobar qué entrada es la que usa los métodos RawGameController.GetButtonLabel y RawGameController.GetSwitchKind.

GetButtonLabel le indicará el texto o el símbolo que se imprime en el botón físico, en lugar de la función del botón; por lo tanto, se usa mejor como ayuda para la interfaz de usuario en los casos en los que desea dar sugerencias al jugador sobre qué botones realizan las funciones. GetSwitchKind le indicará el tipo de conmutador (es decir, cuántas posiciones tiene), pero no el nombre.

No hay ninguna manera estandarizada de obtener la etiqueta de un eje o conmutador, por lo que deberá probarlas usted mismo para determinar qué entrada es la que.

Si tienes un controlador específico que quieras admitir, puedes obtener rawGameController.HardwareProductId y RawGameController.HardwareVendorId y comprobar si coinciden con ese controlador. La posición de cada entrada en cada matriz es la misma para cada controlador con el mismo HardwareProductId y HardwareVendorId, por lo que no tiene que preocuparse de que la lógica sea potencialmente incoherente entre distintos controladores del mismo tipo.

Además del estado del controlador de juego sin procesar, cada lectura devuelve una marca de tiempo que indica exactamente cuándo se recuperó el estado. La marca de tiempo es útil para relacionarse con el tiempo de las lecturas anteriores o con el tiempo de la simulación del juego.

Lectura de los botones y modificadores

Cada uno de los botones del controlador de juego sin procesar proporciona una lectura digital que indica si está presionado (abajo) o liberado (arriba). Las lecturas de botón se representan como valores booleanos individuales en una sola matriz. La etiqueta de cada botón se puede encontrar mediante RawGameController.GetButtonLabel con el índice del valor booleano de la matriz. Cada valor se representa como gameControllerButtonLabel.

En el ejemplo siguiente se determina si se presiona el botón XboxA :

for (uint32_t i = 0; i < buttonCount; i++)
{
    if (currentButtonReading[i])
    {
        GameControllerButtonLabel buttonLabel = rawGameController->GetButtonLabel(i);

        if (buttonLabel == GameControllerButtonLabel::XboxA)
        {
            // XboxA is pressed.
        }
    }
}

A veces, es posible que desee determinar cuándo un botón cambia de presionado a liberado o de liberado a presionado, si se presionan o sueltan varios botones, o si un conjunto de botones se organizan de una manera determinada, algunos presionados y otros no. Para obtener información sobre cómo detectar cada una de estas condiciones, consulte Detección de transiciones de botón y Detección de arreglos de botón complejos.

Los valores switch se proporcionan como una matriz de GameControllerSwitchPosition. Dado que esta propiedad es un campo de bits, el enmascaramiento bit a bit se usa para aislar la dirección del conmutador.

En el ejemplo siguiente se determina si cada modificador está en la posición ascendente:

for (uint32_t i = 0; i < switchCount; i++)
{
    if (GameControllerSwitchPosition::Up ==
        (currentSwitchReading[i] & GameControllerSwitchPosition::Up))
    {
        // The switch is in the up position.
    }
}

Lectura de las entradas analógicas (sticks, desencadenadores, limitaciones, etc.)

Un eje analógico proporciona una lectura entre 0,0 y 1,0. Esto incluye cada dimensión en un stick como X e Y para sticks estándar o incluso X, Y y Z ejes (roll, pitch y yaw, respectivamente) para sticks de vuelo.

Los valores pueden representar desencadenadores analógicos, limitaciones o cualquier otro tipo de entrada analógica. Estos valores no se proporcionan con etiquetas, por lo que se recomienda que el código se pruebe con una variedad de dispositivos de entrada para asegurarse de que coinciden correctamente con las suposiciones.

En todos los ejes, el valor es aproximadamente 0,5 para un stick cuando está en la posición central, pero es normal que el valor preciso varíe, incluso entre las lecturas posteriores; Las estrategias para mitigar esta variación se describen más adelante en esta sección.

En el ejemplo siguiente se muestra cómo leer los valores analógicos de un mando de Xbox:

// Xbox controllers have 6 axes: 2 for each stick and one for each trigger.
float leftStickX = currentAxisReading[0];
float leftStickY = currentAxisReading[1];
float rightStickX = currentAxisReading[2];
float rightStickY = currentAxisReading[3];
float leftTrigger = currentAxisReading[4];
float rightTrigger = currentAxisReading[5];

Al leer los valores stick, observará que no producen una lectura neutra de 0,5 cuando estén en reposo en la posición central; en su lugar, generarán valores diferentes cerca de 0,5 cada vez que el stick se mueve y se devuelve a la posición central. Para mitigar estas variaciones, puede implementar una pequeña zona muerta, que es un intervalo de valores cerca de la posición central ideal que se omite.

Una manera de implementar una zona muerta es determinar hasta dónde se ha movido el stick desde el centro e ignorar las lecturas más cercanas que alguna distancia que elija. Puede calcular la distancia aproximadamente ,no es exacta porque las lecturas de stick son esencialmente polares, no planar, valores, simplemente usando el teorema pythagoriano. Esto produce una zona muerta radial.

El siguiente ejemplo demuestra una zona muerta radial básica utilizando el teorema de Pitágoras:

// Choose a deadzone. Readings inside this radius are ignored.
const float deadzoneRadius = 0.1f;
const float deadzoneSquared = deadzoneRadius * deadzoneRadius;

// Pythagorean theorem: For a right triangle, hypotenuse^2 = (opposite side)^2 + (adjacent side)^2
float oppositeSquared = leftStickY * leftStickY;
float adjacentSquared = leftStickX * leftStickX;

// Accept and process input if true; otherwise, reject and ignore it.
if ((oppositeSquared + adjacentSquared) < deadzoneSquared)
{
    // Input accepted, process it.
}

Consulte también