Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
O XInput permite que os aplicativos do Windows processem interações do controlador (incluindo efeitos de ruído do controlador e entrada e saída de voz).
Este tópico fornece uma breve visão geral dos recursos do XInput e como configurá-lo em um aplicativo. Inclui o seguinte:
Introdução ao XInput
Os aplicativos podem usar a API XInput para se comunicar com controladores de jogos quando eles estão conectados a um PC Windows (até quatro controladores exclusivos podem ser conectados ao mesmo tempo).
Usando esta API, qualquer controlador conectado compatível pode ser consultado quanto ao seu estado, e os efeitos de vibração podem ser definidos. Os controladores que têm o fone de ouvido conectado também podem ser consultados para dispositivos de entrada e saída de som que podem ser usados com o fone de ouvido para processamento de voz.
Layout do controlador
Os controladores compatíveis têm dois manípulos direcionais analógicos, cada um com um botão digital, dois gatilhos analógicos, um direcional digital com quatro direções e oito botões digitais. Os estados de cada uma dessas entradas são retornados na estrutura XINPUT_GAMEPAD quando a funçãoXInputGetState é chamada.
O controlador também tem dois motores de vibração para fornecer efeitos de feedback de força para o usuário. As velocidades destes motores são especificadas na estrutura XINPUT_VIBRATION que é passada para a funçãoXInputSetStatedefinir efeitos de vibração.
Opcionalmente, um fone de ouvido pode ser conectado ao controle. O fone de ouvido tem um microfone para entrada de voz e um fone de ouvido para saída de som. Você pode chamar a função XInputGetAudioDeviceIds ou a função herdada XInputGetDSoundAudioDeviceGuids para obter os identificadores de dispositivo que correspondem aos dispositivos para o microfone e fone de ouvido. Em seguida, você pode usar as APIs do Core Audio para receber entrada de voz e enviar saída de som.
Usando XInput
Usar XInput é tão simples quanto chamar as funções XInput conforme necessário. Usando as funções XInput, você pode recuperar o estado do controlador, obter IDs de áudio do fone de ouvido e definir efeitos de ruído do controlador.
Vários controladores
A API XInput suporta até quatro controladores conectados a qualquer momento. Todas as funções XInput requerem um parâmetro dwUserIndex que é passado para identificar o controlador que está sendo definido ou consultado. Este ID estará no intervalo de 0-3 e é definido automaticamente pelo XInput. O número corresponde à porta à qual o controlador está conectado e não é modificável.
Cada controlador indica o ID que está a usar ao iluminar um quadrante no "anel de luz" no centro do controlador. Um dwUserIndex valor de 0 corresponde ao quadrante superior esquerdo; A numeração prossegue em torno do anel em ordem horária.
Os aplicativos devem suportar vários controladores.
Obtendo o estado do controlador
Ao longo da duração de um aplicativo, obter o estado de um controlador provavelmente será feito com mais frequência. De quadro para quadro em um aplicativo de jogo, o estado deve ser recuperado e as informações do jogo atualizadas para refletir as mudanças no controle.
Para recuperar o estado, use a função XInputGetState:
DWORD dwResult;
for (DWORD i=0; i< XUSER_MAX_COUNT; i++ )
{
XINPUT_STATE state;
ZeroMemory( &state, sizeof(XINPUT_STATE) );
// Simply get the state of the controller from XInput.
dwResult = XInputGetState( i, &state );
if( dwResult == ERROR_SUCCESS )
{
// Controller is connected
}
else
{
// Controller is not connected
}
}
Observe que o valor de retorno de XInputGetState pode ser usado para determinar se o controlador está conectado. As aplicações devem definir uma estrutura para armazenar informações do controlador interno; essas informações devem ser comparadas com os resultados de XInputGetState para determinar quais alterações, como introdução de botões ou deltas de controladores analógicos, foram feitas naquele frame. No exemplo acima, g_Controllers representa tal estrutura.
Depois que o estado tiver sido recuperado em uma estrutura XINPUT_STATE, você poderá verificá-lo em busca de alterações e obter informações específicas sobre o estado do controlador.
O dwPacketNumber membro da estrutura XINPUT_STATE pode ser usado para verificar se o estado do controlador foi alterado desde a última chamada para XInputGetState. Se dwPacketNumber não mudar entre duas chamadas sequenciais para XInputGetState, então não houve nenhuma alteração no estado. Se for diferente, então o aplicativo deve verificar o Gamepad membro da estrutura XINPUT_STATE para obter informações de estado mais detalhadas.
Por razões de desempenho, não invoque XInputGetState para um slot de utilizador 'vazio' em cada frame. Em vez disso, recomendamos que intercale as verificações de novos controladores a cada alguns segundos.
Zona morta
Para que os usuários tenham uma experiência de jogo consistente, seu jogo deve implementar a zona morta corretamente. A zona morta são os valores de "movimento" relatados pelo controlador, mesmo quando os thumbsticks analógicos estão intocados e centrados. Há também uma zona morta para os 2 gatilhos analógicos.
Observação
Jogos que usam XInput que não filtram a zona morta de todo terão uma jogabilidade precária. Por favor, note que alguns controladores são mais sensíveis do que outros, portanto, a zona morta pode variar de unidade para unidade. Recomenda-se que teste os seus jogos com vários controladores diferentes em sistemas diferentes.
As aplicações devem usar "zonas mortas" em entradas analógicas (gatilhos, manípulos) para indicar quando um movimento foi feito o suficiente no stick ou gatilho para ser considerado válido.
Seu aplicativo deve verificar se há zonas mortas e responder adequadamente, como neste exemplo:
XINPUT_STATE state = g_Controllers[i].state;
float LX = state.Gamepad.sThumbLX;
float LY = state.Gamepad.sThumbLY;
//determine how far the controller is pushed
float magnitude = sqrt(LX*LX + LY*LY);
//determine the direction the controller is pushed
float normalizedLX = LX / magnitude;
float normalizedLY = LY / magnitude;
float normalizedMagnitude = 0;
//check if the controller is outside a circular dead zone
if (magnitude > INPUT_DEADZONE)
{
//clip the magnitude at its expected maximum value
if (magnitude > 32767) magnitude = 32767;
//adjust magnitude relative to the end of the dead zone
magnitude -= INPUT_DEADZONE;
//optionally normalize the magnitude with respect to its expected range
//giving a magnitude value of 0.0 to 1.0
normalizedMagnitude = magnitude / (32767 - INPUT_DEADZONE);
}
else //if the controller is in the deadzone zero out the magnitude
{
magnitude = 0.0;
normalizedMagnitude = 0.0;
}
//repeat for right thumb stick
Este exemplo calcula o vetor de direção do controlador e a distância ao longo do vetor em que o controlador foi empurrado. Isso permite a imposição de uma zona morta circular simplesmente verificando se a magnitude do controlador é maior do que o valor da zona morta. Além disso, o código normaliza a magnitude do controlador, que pode ser multiplicada por um fator específico do jogo para converter a posição do controlador em unidades relevantes para o jogo.
Observe que você pode definir suas próprias zonas mortas para os sticks e triggers (em qualquer lugar de 0-65534), ou você pode usar as zonas mortas fornecidas definidas como XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE e XINPUT_GAMEPAD_TRIGGER_THRESHOLD em XInput.h:
#define XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE 7849
#define XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE 8689
#define XINPUT_GAMEPAD_TRIGGER_THRESHOLD 30
Uma vez que a zona morta é imposta, você pode achar útil dimensionar o intervalo resultante [0.0..1.0] ponto flutuante (como no exemplo acima) e, opcionalmente, aplicar uma transformação não linear.
Por exemplo, em jogos de condução, pode ser útil elevar o resultado ao cubo para proporcionar uma melhor sensação ao conduzir os carros usando um comando de jogo. Elevar o resultado ao cubo oferece mais precisão nas faixas mais baixas, o que é desejável, uma vez que os jogadores normalmente aplicam uma força suave para obter movimentos subtis ou exercem uma força intensa numa única direção para obter uma resposta rápida.
Definindo efeitos de vibração
Além de obter o estado do controlador, você também pode enviar dados de vibração para o controlador para alterar o feedback fornecido ao usuário do controlador. O controlador contém dois motores de vibração que podem ser controlados de forma independente, passando valores para a função XInputSetState .
A velocidade de cada motor pode ser especificada com um valor WORD na estrutura XINPUT_VIBRATION, que é passada da seguinte forma para a função XInputSetState.
XINPUT_VIBRATION vibration;
ZeroMemory( &vibration, sizeof(XINPUT_VIBRATION) );
vibration.wLeftMotorSpeed = 32000; // use any value between 0-65535 here
vibration.wRightMotorSpeed = 16000; // use any value between 0-65535 here
XInputSetState( i, &vibration );
Note que o motor direito é o motor de alta frequência, o motor esquerdo é o motor de baixa frequência. Nem sempre precisam de ser fixados na mesma quantidade, uma vez que produzem efeitos diferentes.
Obtendo identificadores de dispositivo de áudio
O fone de ouvido para um controle tem estas funções:
- Gravar som usando um microfone
- Reproduzir som através de auriculares
Use este código para obter os identificadores de dispositivo para o fone de ouvido:
WCHAR renderId[ 256 ] = {0};
WCHAR captureId[ 256 ] = {0};
UINT rcount = 256;
UINT ccount = 256;
XInputGetAudioDeviceIds( i, renderId, &rcount, captureId, &ccount );
Depois de obter os identificadores de dispositivo, você pode criar as interfaces apropriadas. Por exemplo, se você usar XAudio 2.8, use este código para criar uma voz de masterização para este dispositivo:
IXAudio2* pXAudio2 = NULL;
HRESULT hr;
if ( FAILED(hr = XAudio2Create( &pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR ) ) )
return hr;
IXAudio2MasteringVoice* pMasterVoice = NULL;
if ( FAILED(hr = pXAudio2->CreateMasteringVoice( &pMasterVoice, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE, 0, renderId, NULL, AudioCategory_Communications ) ) )
return hr;
Para obter informações sobre como usar o identificador de dispositivo captureId, consulte Capturando um fluxo.
Obtendo GUIDs do DirectSound (somente SDK DirectX legado)
O fone de ouvido que pode ser conectado a um controlador tem duas funções: pode gravar som usando um microfone e pode reproduzir som usando um fone de ouvido. Na API XInput, essas funções são realizadas por meio DirectSound , usando o IDirectSound8 e IDirectSoundCapture8 interfaces.
Para associar o microfone e os auscultadores às interfaces DirectSound apropriadas, deve-se obter os DirectSoundGUIDs para os dispositivos de captura e renderização chamando XInputGetDSoundAudioDeviceGuids.
Observação
O uso do sistema de som legado DirectSound não é recomendado e não está disponível em aplicações da loja do Windows. As informações nesta seção só se aplicam à versão do SDK do DirectX do XInput (XInput 1.3). A versão Windows 8 do XInput (XInput 1.4) usa exclusivamente identificadores de dispositivo do Windows Audio Session API (WASAPI) que são obtidos através XInputGetAudioDeviceIds.
XInputGetDSoundAudioDeviceGuids( i, &dsRenderGuid, &dsCaptureGuid );
Depois de recuperar os GUIDs, você pode criar as interfaces apropriadas chamando DirectSoundCreate8 e DirectSoundCaptureCreate8 da seguinte forma:
// Create IDirectSound8 using the controller's render device
if( FAILED( hr = DirectSoundCreate8( &dsRenderGuid, &pDS, NULL ) ) )
return hr;
// Set coop level to DSSCL_PRIORITY
if( FAILED( hr = pDS->SetCooperativeLevel( hWnd, DSSCL_NORMAL ) ) )
return hr;
// Create IDirectSoundCapture using the controller's capture device
if( FAILED( hr = DirectSoundCaptureCreate8( &dsCaptureGuid, &pDSCapture, NULL ) ) )
return hr;