Volante y retroalimentación de fuerza
En esta página se describen los conceptos básicos de la programación para volantes en Xbox One con Windows.Gaming.Input.RacingWheel 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 volantes conectados y sus usuarios
- cómo detectar que se ha agregado o quitado un volante
- cómo leer la entrada de uno o más volantes
- cómo enviar comandos de retroalimentación de fuerza
- cómo se comportan los volantes como dispositivos de navegación de la interfaz de usuario
Información general sobre los volantes
Los volantes son dispositivos de entrada que asemejan la sensación de un coche de carreras. Los volantes son el dispositivo de entrada perfecto para juegos de carreras de estilo arcade y de simulación que cuentan con coches o camiones. Los volantes se admiten en las aplicaciones para UWP de Xbox One y Windows 10 o Windows 11 mediante el espacio de nombres Windows.Gaming.Input.
Los volantes se ofrecen en una variedad de precios; generalmente tienen más y mejores capacidades de entrada y retroalimentación de fuerza a medida que aumenta el precio. Todos los volantes están equipados con un volante analógico, controles analógicos de acelerador y freno, y algunos botones en el volante. Algunos volantes están equipados además con controles analógicos de embrague y freno de mano, palanca de cambios y capacidades de retroalimentación de fuerza. No todos los volantes están equipados con los mismos conjuntos de características y también pueden variar en la compatibilidad de determinadas características; por ejemplo, los volantes pueden admitir distintos rangos de rotación y las palancas de cambios pueden admitir un número distinto de marchas.
Funcionalidades del dispositivo
Los distintos volantes ofrecen diferentes conjuntos de funcionalidades del dispositivo opcionales y distintos niveles de compatibilidad para esas funcionalidades; este nivel de variación entre un solo tipo de dispositivo de entrada es único entre los dispositivos compatibles con la API Windows.Gaming.Input. Además, la mayoría de los dispositivos que encontrará admitirán al menos algunas funcionalidades opcionales u otras variaciones. Por este motivo, es importante determinar las capacidades de cada volante conectado individualmente y admitir la variación completa de las funcionalidades que tiene sentido para el juego.
Para obtener más información, consulte Determinar las funcionalidades del volante.
Retroalimentación de fuerza
Algunos volantes ofrecen verdadera retroalimentación de fuerza, es decir, pueden aplicar fuerzas reales en un eje de control, como el volante, no solo vibración simple. Los juegos usan esta capacidad para crear una mayor sensación de inmersión (daño simulado de un accidente, "sensación de carretera") y para aumentar el desafío de conducir bien.
Para obtener más información, consulte Introducción a la retroalimentación de fuerza.
Navegación de la interfaz de usuario
Para aliviar la carga de la compatibilidad con los diferentes dispositivos de entrada para la navegación de la interfaz de usuario y fomentar la coherencia entre dispositivos y juegos, la mayoría de los dispositivos de entrada física actúan simultáneamente como un dispositivo independiente de entrada lógica denominado controlador de navegación de la interfaz de usuario. El controlador de navegación de la interfaz de usuario proporciona un vocabulario común para los comandos de navegación de la interfaz de usuario en todos los dispositivos de entrada.
Debido a su enfoque único en los controles analógicos y en el grado de variación entre los distintos volantes, suelen estar equipados con una cruceta digital, y con los botones de Vista, Menú, A, B, X e Y, parecidos a los de un controlador para juegos. Estos botones no están destinados a admitir comandos de juego y no se puede acceder a ellos tan fácilmente como a los botones del volante.
Como controladores de navegación de la interfaz de usuario, los volantes asignan el conjunto necesario de comandos de navegación al stick analógico izquierdo, la cruceta y los botones de Vista, Menú, A y B.
Comando de navegación | Entrada del volante |
---|---|
Up (Arriba) | Cruceta hacia arriba |
Bajar | Cruceta hacia abajo |
Izquierda | Cruceta hacia la izquierda |
Right | Cruceta hacia la derecha |
Ver | Botón Ver |
Menú | Botón Menú |
Aceptar | Botón A |
Cancelar | Botón B |
Además, algunos volantes pueden asignar parte del conjunto opcional de comandos de navegación a otras entradas que admiten, pero las asignaciones de comandos pueden variar de dispositivo a dispositivo. Considere la posibilidad de admitir estos comandos también, pero asegúrese de que estos comandos no son esenciales para navegar por la interfaz del juego.
Comando de navegación | Entrada del volante |
---|---|
Re. pág. | varía |
Av. Pág. | varía |
Página izquierda | varía |
Página derecha | varía |
Desplazarse hacia arriba | varía |
Desplazarse hacia abajo | varía |
Desplazarse hacia la izquierda | varía |
Desplazarse hacia la derecha | varía |
Contexto 1 | Botón X (generalmente) |
Contexto 2 | Botón Y (generalmente) |
Contexto 3 | varía |
Contexto 4 | varía |
Detectar y realizar un seguimiento de los volantes
La detección y el seguimiento de volantes funciona exactamente de la misma manera que para los controladores para juegos, excepto con la clase RacingWheel en lugar de la clase Gamepad. Consulte Controlador para juegos y vibración para obtener más información.
Lectura del volante
Después de identificar los volantes que le interesan, está listo para recopilar información sobre ellos. Sin embargo, a diferencia de otros tipos de entrada a los que podría estar acostumbrado, los volantes no comunican el cambio de estado mediante la generación de eventos. En su lugar, se toman lecturas regulares de sus estados actuales mediante un sondeo.
Sondear el volante
El sondeo captura una instantánea del volante en un momento preciso. Este enfoque para la recopilación de entradas es una buena opción para la mayoría de los juegos porque su lógica normalmente se ejecuta en un bucle determinista en lugar de ser controlado por eventos; también suele ser más sencillo interpretar los comandos de juego de la entrada recopilada de una vez que de muchas entradas únicas recopiladas a lo largo del tiempo.
Para sondear un volante, llame a GetCurrentReading; esta función devuelve una RacingWheelReading que contiene el estado del volante.
En el ejemplo siguiente se sondea un volante para ver su estado actual.
auto racingwheel = myRacingWheels[0];
RacingWheelReading reading = racingwheel->GetCurrentReading();
Además del estado del volante, cada lectura incluye una marca de tiempo que indica precisamente 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.
Determinación de las funcionalidades del volante
Muchos de los controles de volante son opcionales o admiten diferentes variaciones incluso en los controles necesarios, por lo que tiene que determinar las capacidades de cada volante individualmente antes de poder procesar la entrada recopilada en cada lectura del volante.
Los controles opcionales son el freno de mano, el embrague y la palanca de cambios. Puede determinar si un volante conectado admite estos controles leyendo las propiedades HasHandbrake, HasClutch y HasPatternShifter del volante, respectivamente. El control se admite si el valor de la propiedad es true; de lo contrario, no se admite.
if (racingwheel->HasHandbrake)
{
// the handbrake is supported
}
if (racingwheel->HasClutch)
{
// the clutch is supported
}
if (racingwheel->HasPatternShifter)
{
// the pattern shifter is supported
}
Además, los controles que pueden variar son el volante y la palanca de cambios. El volante puede variar según el grado de rotación física que el volante real pueda admitir, mientras que la palanca de cambios puede variar según el número de diferentes marchas hacia delante que admite. Puede determinar el mayor ángulo de rotación que admite el volante real leyendo la propiedad MaxWheelAngle
del volante; su valor es el ángulo físico máximo admitido en grados en sentido de las manecillas de un reloj (positivo) que también se admite en la dirección contraria (grados negativos). Puede determinar la mayor marcha hacia delante que admite la palanca de cambios leyendo la propiedad MaxPatternShifterGear
del volante; su valor es la mayor marcha hacia adelante admitida, inclusive, es decir, si su valor es 4, entonces la palanca de cambios admite la marcha atrás, punto muerto, primera, segunda, tercera y cuarta marcha.
auto maxWheelDegrees = racingwheel->MaxWheelAngle;
auto maxShifterGears = racingwheel->MaxPatternShifterGear;
Por último, algunos volantes admiten la retroalimentación de fuerza a través del volante. Puede determinar si un volante conectado admite la retroalimentación de fuerza leyendo la propiedad WheelMotor del volante. La retroalimentación de fuerza se admite si WheelMotor
no es null; de lo contrario, no se admite.
if (racingwheel->WheelMotor != nullptr)
{
// force feedback is supported
}
Para obtener información sobre cómo usar la funcionalidad de retroalimentación de fuerza de los volantes que la admiten, consulte Información general sobre la retroalimentación de fuerza.
Lectura de los botones
Cada uno de los botones del volante (las cuatro direcciones de la cruceta, los botones Marcha anterior y Marcha siguiente y los 16 botones adicionales) proporcionan una lectura digital que indica si está presionado (abajo) o sin presionar (arriba). Para mejorar la eficacia, las lecturas de botón no se representan como valores booleanos individuales; en su lugar, todos están empaquetados en un único campo de bits representado por la enumeración RacingWheelButtons.
Nota:
Los volantes están equipados con botones adicionales usados para la navegación de la interfaz de usuario, como los botones Vista y Menú. Estos botones no forman parte de la enumeración RacingWheelButtons
y solo se pueden leer accediendo al volante como un dispositivo de navegación de la interfaz de usuario. Para obtener más información, consulte Dispositivo de navegación de la interfaz de usuario.
Los valores del botón se leen de la propiedad Buttons
de la estructura RacingWheelReading. Dado que esta propiedad es un campo de bits, el enmascaramiento de bits se usa para aislar el valor del botón que le interesa. El botón se presiona (abajo) cuando se establece el bit correspondiente; de lo contrario, se libera (arriba).
En el ejemplo siguiente se determina si el botón Marcha siguiente está presionado.
if (RacingWheelButtons::NextGear == (reading.Buttons & RacingWheelButtons::NextGear))
{
// Next Gear is pressed
}
En el ejemplo siguiente se determina si el botón Marcha siguiente se ha liberado.
if (RacingWheelButtons::None == (reading.Buttons & RacingWheelButtons::NextGear))
{
// Next Gear is released (not 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 estas condiciones, consulte Detección de transiciones de botón y Detección de arreglos complejos de botones.
Lectura del volante
El volante es un control necesario que proporciona una lectura analógica entre -1,0 y +1,0. Un valor de -1,0 corresponde a la posición más a la izquierda del volante; un valor de +1,0 corresponde a la posición más a la derecha. El valor del volante se lee de la propiedad Wheel
de la estructura RacingWheelReading.
float wheel = reading.Wheel; // returns a value between -1.0 and +1.0.
Aunque las lecturas del volante corresponden a diferentes grados de rotación física en el volante real en función del rango de rotación admitido por el volante físico, normalmente no querrá escalar las lecturas del volante; los volantes que admiten mayores grados de rotación simplemente proporcionan una mayor precisión.
Lectura del acelerador y el freno
El acelerador y el freno son controles necesarios que proporcionan lecturas analógicas entre 0,0 (totalmente liberado) y 1,0 (totalmente presionado) representados como valores de punto flotante. El valor del control de acelerador se lee de la propiedad Throttle
de la estructura RacingWheelReading; el valor del control de freno se lee de la propiedad Brake
.
float throttle = reading.Throttle; // returns a value between 0.0 and 1.0
float brake = reading.Brake; // returns a value between 0.0 and 1.0
Lectura del freno de mano y el embrague
El freno de mano y el embrague son controles opcionales que proporcionan lecturas analógicas entre 0,0 (totalmente liberado) y 1,0 (totalmente activado) representados como valores de punto flotante. El valor del control de freno de mano se lee de la propiedad Handbrake
de la estructura RacingWheelReading; el valor del control de embrague se lee de la propiedad Clutch
.
float handbrake = 0.0;
float clutch = 0.0;
if(racingwheel->HasHandbrake)
{
handbrake = reading.Handbrake; // returns a value between 0.0 and 1.0
}
if(racingwheel->HasClutch)
{
clutch = reading.Clutch; // returns a value between 0.0 and 1.0
}
Lectura de la palanca de cambios
La palanca de cambios es un control opcional que proporciona una lectura digital entre -1 y MaxPatternShifterGear representada como un valor entero con signo. Un valor de -1 o 0 corresponde a las marchas marcha atrás y punto muerto, respectivamente; los valores cada vez más positivos corresponden a mayores marchas hacia delante hasta MaxPatternShifterGear, inclusive. El valor de la palanca de cambios se lee de la propiedad PatternShifterGear de la estructura RacingWheelReading.
if (racingwheel->HasPatternShifter)
{
gear = reading.PatternShifterGear;
}
Nota:
La palanca de cambios, cuando se admite, existe junto con los botones Marcha anterior y Marcha siguiente, que también afectan a la marcha actual del coche del jugador. Una estrategia simple para unificar estas entradas en que ambos elementos están presentes es ignorar la palanca de cambios (y el embrague) cuando un jugador elige el cambio automático para su vehículo, o bien ignorar los botones de Marcha anterior y Marcha siguiente cuando un jugador elige el cambio manual si su volante está equipado con un control de palanca de cambios. Puede implementar una estrategia de unificación diferente si esto no es adecuado para su juego.
Ejecución del ejemplo InputInterfacing
La aplicación de ejemplo InputInterfacingUWP en GitHub muestra cómo usar volantes y diferentes tipos de dispositivos de entrada en conjunto, así como cómo se comportan estos dispositivos de entrada como controladores de navegación de la interfaz de usuario.
Introducción a la retroalimentación de fuerza
Muchos volantes tienen capacidad de retroalimentación de fuerza para proporcionar una experiencia de conducción más envolvente y desafiante. Los volantes que admiten la retroalimentación de fuerza suelen estar equipados con un solo motor que aplica fuerza al volante a lo largo de un solo eje, el eje de rotación del volante. La retroalimentación de fuerza se admite en las aplicaciones para UWP de Xbox One y Windows 10 o Windows 11 a través del espacio de nombres Windows.Gaming.Input.ForceFeedback.
Nota:
Las API de retroalimentación de fuerza son capaces de admitir varios ejes de fuerza, pero actualmente no hay ningún volante que admita ningún eje de retroalimentación distinto del de la rotación del volante.
Uso de la retroalimentación de fuerza
En estas secciones se describen los conceptos básicos de la programación de los efectos de retroalimentación de fuerza para los volantes. La retroalimentación se aplica mediante efectos, que se cargan primero en el dispositivo de retroalimentación de fuerza y, a continuación, se pueden iniciar, pausar, reanudar y detener de una manera similar a los efectos de sonido; sin embargo, primero debe determinar las funcionalidades de retroalimentación del volante.
Determinación de las funcionalidades de retroalimentación de fuerza
Puede determinar si un volante conectado admite la retroalimentación de fuerza leyendo la propiedad WheelMotor del volante. La retroalimentación de fuerza no se admite si WheelMotor
es null; de lo contrario, la retroalimentación de fuerza se admite y puede determinar las funcionalidades de retroalimentación específicas del motor, como los ejes a los que puede afectar.
if (racingwheel->WheelMotor != nullptr)
{
auto axes = racingwheel->WheelMotor->SupportedAxes;
if(ForceFeedbackEffectAxes::X == (axes & ForceFeedbackEffectAxes::X))
{
// Force can be applied through the X axis
}
if(ForceFeedbackEffectAxes::Y == (axes & ForceFeedbackEffectAxes::Y))
{
// Force can be applied through the Y axis
}
if(ForceFeedbackEffectAxes::Z == (axes & ForceFeedbackEffectAxes::Z))
{
// Force can be applied through the Z axis
}
}
Cargar efectos de retroalimentación de fuerza
Los efectos de la retroalimentación de fuerza se cargan en el dispositivo de retroalimentación donde se "juegan" de forma autónoma en el comando del juego. Se proporcionan varios efectos básicos; se pueden crear efectos personalizados a través de una clase que implementa la interfaz IForceFeedbackEffect.
Clase de efecto | Descripción del efecto |
---|---|
ConditionForceEffect | Efecto que aplica una fuerza variable en respuesta al sensor actual dentro del dispositivo. |
ConstantForceEffect | Efecto que aplica una fuerza constante a lo largo de un vector. |
PeriodicForceEffect | Efecto que aplica una fuerza variable definida por una forma de onda, a lo largo de un vector. |
RampForceEffect | Efecto que aplica una fuerza de aumento o disminución linealmente a lo largo de un vector. |
using FFLoadEffectResult = ForceFeedback::ForceFeedbackLoadEffectResult;
auto effect = ref new Windows.Gaming::Input::ForceFeedback::ConstantForceEffect();
auto time = TimeSpan(10000);
effect->SetParameters(Windows::Foundation::Numerics::float3(1.0f, 0.0f, 0.0f), time);
// Here, we assume 'racingwheel' is valid and supports force feedback
IAsyncOperation<FFLoadEffectResult>^ request
= racingwheel->WheelMotor->LoadEffectAsync(effect);
auto loadEffectTask = Concurrency::create_task(request);
loadEffectTask.then([this](FFLoadEffectResult result)
{
if (FFLoadEffectResult::Succeeded == result)
{
// effect successfully loaded
}
else
{
// effect failed to load
}
}).wait();
Uso de efectos de retroalimentación de fuerza
Una vez cargados, todos los efectos se pueden iniciar, pausar, reanudar y detener sincrónicamente mediante una llamada a funciones en la propiedad WheelMotor
del volante, o individualmente mediante una llamada a funciones en el propio efecto de retroalimentación. Normalmente, debe cargar todos los efectos que quiere usar en el dispositivo de retroalimentación antes de que comience el juego y luego usar sus respectivas funciones SetParameters
para actualizar los efectos a medida que avanza el juego.
if (ForceFeedbackEffectState::Running == effect->State)
{
effect->Stop();
}
else
{
effect->Start();
}
Por último, puede habilitar, deshabilitar o restablecer de forma asincrónica todo el sistema de retroalimentación de fuerza en un volante determinado siempre que lo necesite.