Controlador de jogo bruto

Esta página descreve os conceitos básicos de programação para praticamente qualquer tipo de controlador de jogo usando Windows.Gaming.Input.RawGameController e APIs relacionadas para a Plataforma Universal do Windows (UWP).

Ao ler esta página, você aprenderá:

  • Como reunir uma lista de controladores de jogos RAW conectados e seus usuários
  • Como detectar que um controlador de jogo RAW foi adicionado ou removido
  • Como obter os recursos de um controlador de jogo RAW
  • Como ler a entrada de um controlador de jogo RAW

Visão geral

Um controlador de jogo bruto é uma representação genérica de um controlador de jogo, com entradas encontradas em muitas variantes de controladores de jogos comuns. Essas entradas são expostas como matrizes simples de botões, alternâncias e eixos sem nome. Utilizando um controlador de jogo bruto, você pode permitir que os clientes criem mapeamentos de entrada personalizados, independentemente do tipo de controlador que estiverem utilizando.

A classe RawGameController é realmente destinada a cenários em que as outras classes de entrada (ArcadeStick, FlightStick e assim por diante) não atendem às suas necessidades — se você quiser algo mais genérico, prevendo que os clientes usarão muitos tipos diferentes de controladores de jogo, essa classe é para você.

Detecte e rastreie controladores de jogos brutos

Detectar e rastrear controladores de jogos brutos funciona exatamente da mesma maneira que para gamepads, exceto com a classe RawGameController em vez da classe Gamepad . Consulte Gamepad e vibração para obter mais informações.

Obtenha os recursos de um controlador de jogo bruto

Depois de identificar o controlador de jogo bruto no qual você está interessado, você pode coletar informações sobre os recursos do controlador. Você pode obter o número de botões no controlador de jogo bruto com RawGameController.ButtonCount, o número de eixos analógicos com RawGameController.AxisCount e o número de opções com RawGameController.SwitchCount. Além disso, você pode obter o tipo de opção usando RawGameController.GetSwitchKind.

O exemplo a seguir obtém as contagens de entrada de um controlador de jogo bruto:

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

O exemplo a seguir determina o tipo de cada switch:

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

Lendo o controlador de jogo bruto

Depois de saber o número de entradas em um controlador de jogo bruto, você está pronto para coletar informações dele. No entanto, ao contrário de alguns outros tipos de entrada com os quais você pode estar acostumado, um controlador de jogo bruto não comunica a alteração de estado gerando eventos. Em vez disso, você faz leituras regulares de seu estado atual pesquisando-o.

Sondando o controlador de jogo bruto

A sondagem captura um instantâneo do controlador de jogo bruto em um ponto preciso no tempo. Essa abordagem para coleta de entrada é uma boa opção para a maioria dos jogos porque sua lógica normalmente é executada em um loop determinístico em vez de ser orientada por eventos. Normalmente, também é mais simples interpretar comandos de jogo da entrada coletada de uma só vez do que de muitas entradas simples coletadas ao longo do tempo.

Você sonda um controlador de jogo bruto chamando RawGameController.GetCurrentReading. Essa função preenche matrizes para botões, opções e eixos que contêm o estado do controlador de jogo bruto.

O exemplo a seguir sonda um controlador de jogo bruto para seu estado atual:

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

Não há garantia de qual posição em cada matriz manterá qual valor de entrada entre diferentes tipos de controladores, portanto, você precisará verificar qual entrada é qual usando os métodos RawGameController.GetButtonLabel e RawGameController.GetSwitchKind.

GetButtonLabel informará o texto ou o símbolo impresso no botão físico, em vez da função do botão — portanto, é melhor usá-lo como um auxílio para a interface do usuário nos casos em que você deseja dar ao jogador dicas sobre quais botões executam quais funções. GetSwitchKind informará o tipo de switch (ou seja, quantas posições ele tem), mas não o nome.

Não há uma maneira padronizada de obter o rótulo de um eixo ou chave, portanto, você precisará testá-los para determinar qual entrada é qual.

Se você tiver um controlador específico ao qual deseja dar suporte, poderá obter o RawGameController.HardwareProductId e o RawGameController.HardwareVendorId e verificar se eles correspondem a esse controlador. A posição de cada entrada em cada matriz é a mesma para todos os controladores com o mesmo HardwareProductId e HardwareVendorId, portanto, você não precisa se preocupar com a possibilidade de sua lógica ser inconsistente entre diferentes controladores do mesmo tipo.

Além do estado bruto do controlador de jogo, cada leitura retorna um carimbo de data/hora que indica precisamente quando o estado foi recuperado. O carimbo de data/hora é útil para se relacionar com o tempo das leituras anteriores ou com o tempo da simulação do jogo.

Lendo os botões e interruptores

Cada um dos botões do controlador de jogo bruto fornece uma leitura digital que indica se ele está pressionado (para baixo) ou liberado (para cima). As leituras de botão são representadas como valores booleanos individuais em uma única matriz. O rótulo de cada botão pode ser encontrado usando RawGameController.GetButtonLabel com o índice do valor booliano na matriz. Cada valor é representado como um GameControllerButtonLabel.

O exemplo a seguir determina se o botão XboxA é pressionado:

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

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

Às vezes, convém determinar quando um botão passa de pressionado para liberado ou vice-versa, se vários botões foram pressionados ou liberados ou se um conjunto de botões está organizado de determinada maneira—alguns pressionados, outros, não. Para obter informações sobre como detectar cada uma dessas condições, consulte Detectando transições de botão e Detectando arranjos de botões complexos.

Os valores de opção são fornecidos como uma matriz de GameControllerSwitchPosition. Como essa propriedade é um campo de bits, o mascaramento bit a bit é usado para isolar a direção do switch.

O exemplo a seguir determina se cada chave está na posição para cima:

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

Lendo as entradas analógicas (manípulos, gatilhos, aceleradores e assim por diante)

Um eixo analógico fornece uma leitura entre 0,0 e 1,0. Isso inclui cada dimensão em um manípulo, como X e Y para manípulos padrão ou mesmo eixos X, Y e Z (rolagem, inclinação e guinada, respectivamente) para manípulos de vôo.

Os valores podem representar gatilhos analógicos, aceleradores ou qualquer outro tipo de entrada analógica. Esses valores não são fornecidos com rótulos, portanto, sugerimos que seu código seja testado com uma variedade de dispositivos de entrada para garantir que eles correspondam corretamente às suas suposições.

Em todos os eixos, o valor é de aproximadamente 0,5 para um bastão quando está na posição central, mas é normal que o valor preciso varie, mesmo entre leituras subsequentes; Estratégias para mitigar essa variação são discutidas posteriormente nesta seção.

O exemplo a seguir mostra como ler os valores analógicos de um controle do 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];

Ao ler os valores do bastão, você notará que eles não produzem de forma confiável uma leitura neutra de 0,5 quando em repouso na posição central; em vez disso, eles produzirão valores diferentes próximos a 0,5 cada vez que o stick for movido e retornado à posição central. Para atenuar essas variações, você pode implementar uma pequena zona morta, que é um intervalo de valores próximos à posição central ideal que são ignorados.

Uma maneira de implementar uma zona morta é determinar a distância que o stick se moveu do centro e ignorar as leituras que estão mais próximas do que a distância que você escolher. Você pode calcular a distância aproximadamente - não é exata porque as leituras de vara são essencialmente valores polares, não planares - apenas usando o teorema de Pitágoras. Isso produz uma zona morta radial.

O exemplo a seguir demonstra um deadzone radial básico usando o teorema pitagórico:

// 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.
}

Confira também