Condividi tramite


Volante da corsa e Force feedback

Questa pagina descrive le nozioni di base della programmazione per volanti su Xbox One utilizzando Windows.Gaming.Input.RacingWheel e le API correlate per la piattaforma UWP (Universal Windows Platform).

Leggendo questa pagina, si apprenderà quanto segue:

  • come raccogliere un elenco di volanti da corsa collegati e i relativi utenti
  • come rilevare che un volante da corsa è stato aggiunto o rimosso
  • come leggere l'input da uno o più volanti da corsa
  • come inviare comandi Force feedback
  • come i volanti da corsa si comportano come dispositivi di spostamento interfaccia utente

Panoramica del volante da corsa

I volanti da corsa sono dispositivi di input che assomigliano all'aspetto di una vera cabina di guida da corsa. I volanti da corsa sono il dispositivo di input perfetto per giochi di corse in stile Arcade e in stile simulazione caratterizzati da automobili o camion. I volanti da corsa sono supportati nelle app UWP di Windows 10 o Windows 11 e Xbox One tramite lo spazio dei nomi Windows.Gaming.Input.

I volanti da corsa sono offerti a una varietà di punti di prezzo, in genere avendo più e migliori funzionalità di input e Force feedback man mano che aumentano i loro punti di prezzo. Tutti i volanti da corsa sono dotati di volante analogico, controlli analogici per acceleratore e freno e alcuni pulsanti. Alcuni volanti da corsa sono inoltre dotati di controlli analogici per frizione e freno a mano, variatori di pattern e funzionalità Force feedback. Non tutti i volanti da corsa sono dotati delle stesse serie di funzionalità e possono anche variare nel supporto per determinate funzionalità: ad esempio, i volanti potrebbero supportare diversi intervalli di rotazione e i variatori di pattern potrebbero supportare diversi numeri di marce.

Funzionalità del dispositivo

Volanti da corsa diversi offrono set diversi di funzionalità opzionali del dispositivo e livelli diversi di supporto per tali funzionalità; questo livello di variazione tra un singolo tipo di dispositivo di input è univoco tra i dispositivi supportati dall'API Windows.Gaming.Input. Inoltre, la maggior parte dei dispositivi che si incontreranno supporteranno almeno alcune funzionalità opzionali o altre varianti. Per questo motivo, è importante determinare individualmente le funzionalità di ogni volante da corsa collegato e supportare la completa variazione di funzionalità che hanno senso per il gioco.

Per ulteriori informazioni, vedere Determinazione delle funzionalità del volante da corsa.

Force feedback

Alcuni volanti da corsa offrono un Force feedback reale, ovvero possono applicare forze effettive su un asse di controllo quale il volante, e non solo semplici vibrazioni. I giochi utilizzano questa funzionalità per creare un maggiore senso di immersione (danno simulato da incidente, "sensazione di strada") e per aumentare la sfida di una buona guida.

Per maggiori informazioni, vedere Panoramica del Force feedback.

Navigazione dell'interfaccia utente

Per alleviare l'onere di supporto dei diversi dispositivi di input per la navigazione dell'interfaccia utente e incoraggiare la coerenza tra giochi e dispositivi, la maggior parte dei dispositivi di input fisici agisce contemporaneamente come un dispositivo di input logico separato denominato controller di navigazione dell'interfaccia utente. Il controller di navigazione dell'interfaccia utente fornisce un vocabolario comune per i comandi di navigazione dell'interfaccia utente tra i dispositivi di input.

Grazie alla loro attenzione unica ai controlli analogici e al grado di variazione tra i diversi volanti da corsa, sono generalmente dotati di D-pad digitale e dei pulsanti Visualizza, Menu, A, B, X e Y che assomigliano a quelli di un gamepad; questi pulsanti non sono destinati a supportare controlli di gioco e non sono facilmente accessibili come pulsanti del volante da corsa.

In quanto controller di navigazione dell'interfaccia utente, i volanti da corsa mappano il set richiesto di controlli di navigazione sulla levetta sinistra, sul D-pad e sui pulsanti Visualizza, Menu, A e B.

Comando di navigazione Input del volante da corsa
Su D-pad su
Giù D-pad giù
Sinistra D-pad a sinistra
Destra D-pad a destra
Visualizza Pulsante Visualizza
Menu Pulsante Menu
Accetta Pulsante A
Annulla Pulsante B

Inoltre, alcuni volanti da corsa potrebbero mappare alcuni dei set opzionali di comandi di navigazione su altri input supportati, ma le mappature dei comandi possono variare da un dispositivo all'altro. Considerare il supporto anche di questi comandi, ma accertarsi che tali comandi non siano essenziali per navigare nell'interfaccia del gioco.

Comando di navigazione Input del volante da corsa
Pagina su variabile
Pagina giù variabile
Pagina a sinistra variabile
Pagina a destra variabile
Scorrimento verso l'alto variabile
Scorrimento verso il basso variabile
Scorrimento a sinistra variabile
Scorrimento a destra variabile
Contesto 1 Pulsante X (comunemente)
Contesto 2 Pulsante Y (comunemente)
Contesto 3 variabile
Contesto 4 variabile

Rilevamento e tracciamento dei volanti da corsa

Rilevamento e tracciamento dei volanti da corsa funzionano esattamente come per i gamepad, con l'unica differenza che viene utilizzata la classe RacingWheel anziché la classe Gamepad. Per maggiori informazioni, vedere Gamepad e vibrazione.

Lettura del volante da corsa

Dopo aver identificato i volanti da corsa di interesse, si è pronti a raccogliere l'input da loro proveniente. Tuttavia, a differenza di altri tipi di input a cui si potrebbe essere abituati, i volanti da corsa non comunicano il cambiamento di stato generando eventi. È invece possibile eseguire letture regolari dei loro stati correnti tramite polling.

Polling del volante da corsa

Il polling acquisisce un'istantanea del volante da corsa in un momento preciso. Questo approccio alla raccolta degli input è ottimale per la maggior parte dei giochi in quanto la loro logica viene in genere eseguita in un ciclo deterministico anziché essere guidata dagli eventi; è anche in genere più semplice interpretare i comandi di gioco dagli input raccolti contemporaneamente piuttosto che da molti input singoli raccolti nel tempo.

Il polling di un volante da corsa si esegue chiamando GetCurrentReading; questa funzione restituisce un RacingWheelReading che contiene lo stato del volante da corsa.

Nell'esempio seguente viene eseguito il polling di un volante da corsa per il suo stato corrente.

auto racingwheel = myRacingWheels[0];

RacingWheelReading reading = racingwheel->GetCurrentReading();

Oltre allo stato del volante da corsa, ogni lettura include un timestamp che indica esattamente quando è stato recuperato lo stato. Il timestamp è utile per rapportarsi alla tempistica delle letture precedenti o a quella della simulazione del gioco.

Determinazione delle funzionalità del volante da corsa

Molti dei controlli del volante da corsa sono opzionali o supportano variazioni diverse anche nei controlli richiesti, quindi è necessario determinare le funzionalità di ciascun volante da corsa singolarmente prima di poter elaborare l'input raccolto in ogni lettura del volante da corsa.

I controlli opzionali sono il freno a mano, la frizione e il variatore di pattern; è possibile determinare se un volante da corsa collegato supporti tali comandi leggendo rispettivamente le proprietà HasHandbrake, HasClutch e HasPatternShifter del volante da corsa. Il controllo è supportato se il valore della proprietà è true, altrimenti non è supportato.

if (racingwheel->HasHandbrake)
{
    // the handbrake is supported
}

if (racingwheel->HasClutch)
{
    // the clutch is supported
}

if (racingwheel->HasPatternShifter)
{
    // the pattern shifter is supported
}

Inoltre, i controlli che possono variare sono il volante e il variatore di pattern. Il volante può variare in base al grado di rotazione fisica che il volante effettivo può supportare, mentre il variatore di pattern può variare in base al numero di marce in avanti distinte che supporta. È possibile determinare l'angolo di rotazione massimo supportato dal volante effettivo leggendo la proprietà MaxWheelAngle del volante da corsa. Il suo valore è l'angolo fisico massimo supportato in gradi in senso orario (positivo) che è supportato in modo analogo in senso antiorario (gradi negativi). È possibile determinare la marcia in avanti massima supportata dal variatore di pattern leggendo la proprietà MaxPatternShifterGear del volante da corsa; il suo valore è la marcia in avanti più alta supportata, inclusiva, ovvero se il suo valore è 4, allora il variatore di pattern supporta la retromarcia, il folle, la prima, la seconda, la terza e la quarta marcia.

auto maxWheelDegrees = racingwheel->MaxWheelAngle;
auto maxShifterGears = racingwheel->MaxPatternShifterGear;

Infine, alcuni volanti da corsa supportano il Force feedback attraverso il volante. È possibile determinare se un volante da corsa collegato supporta il Force feedback leggendo la proprietà WheelMotor del volante da corsa. Il Force feedback è supportato se WheelMotor non è null, altrimenti non è supportato.

if (racingwheel->WheelMotor != nullptr)
{
    // force feedback is supported
}

Per informazioni su come utilizzare la funzionalità Force feedback dei volanti da corsa che la supportano, vedere Panoramica del force feedback.

Lettura dei pulsanti

Ciascuno dei pulsanti del volante da corsa, ovvero le quattro direzioni del D-pad, i pulsanti Marcia precedente e Marcia successiva e 16 pulsanti aggiuntivi, fornisce una lettura digitale che indica se sia premuto (giù) o rilasciato (su). Per ragioni di efficienza, le letture dei pulsanti non sono rappresentate come singoli valori booleani; sono invece tutti compressi in un singolo campo di bit rappresentato dall'enumerazione RacingWheelButtons.

Nota

I volanti da corsa sono dotati di pulsanti aggiuntivi utilizzati per la navigazione dell'interfaccia utente, ad esempio i pulsanti Visualizza e Menu. Questi pulsanti non fanno parte dell'enumerazione RacingWheelButtons ed è possibile leggerli solo accedendo al volante da corsa come dispositivo di navigazione dell'interfaccia utente. Per maggiori informazioni, vedere Dispositivo di navigazione dell'interfaccia utente.

I valori dei pulsanti vengono letti dalla proprietà Buttons dello struct RacingWheelReading. Poiché questa proprietà è un campo di bit, viene utilizzata la mascheratura bitwise per isolare il valore del pulsante a cui si è interessati. Il pulsante viene premuto (giù) quando è impostato il bit corrispondente; altrimenti viene rilasciato (su).

Nell'esempio seguente viene determinato se il pulsante Marcia successiva sia premuto o meno.

if (RacingWheelButtons::NextGear == (reading.Buttons & RacingWheelButtons::NextGear))
{
    // Next Gear is pressed
}

Nell'esempio seguente viene determinato se il pulsante Marcia successiva sia rilasciato o meno.

if (RacingWheelButtons::None == (reading.Buttons & RacingWheelButtons::NextGear))
{
    // Next Gear is released (not pressed)
}

A volte si potrebbe voler determinare quando un pulsante passa da premuto a rilasciato o da rilasciato a premuto, se più pulsanti vengono premuti o rilasciati o se una serie di pulsanti è disposta in un modo particolare, es. alcuni premuti e altri no. Per informazioni su come rilevare tali condizioni, vedere Rilevamento di transizioni di pulsanti e Rilevamento di disposizioni complesse di pulsanti.

Lettura del volante

Il volante è un dispositivo di controllo obbligatorio che fornisce una lettura analogica compresa tra -1,0 e +1,0. Il valore -1,0 corrisponde alla posizione all'estrema sinistra del volante; il valore +1,0 corrisponde alla posizione all'estrema destra. Il valore del volante viene letto dalla proprietà Wheel dello struct RacingWheelReading.

float wheel = reading.Wheel;  // returns a value between -1.0 and +1.0.

Anche se le letture del volante corrispondono a diversi gradi di rotazione fisica nel volante effettivo a seconda dell'intervallo di rotazione supportato dal volante da corsa fisico, di solito non è necessario ridimensionare le letture del volante; i volanti che supportano gradi di rotazione maggiori offrono una maggiore precisione.

Lettura di acceleratore e freno

Acceleratore e freno sono controlli necessari che forniscono letture analogiche tra 0,0 (completamente rilasciato) e 1,0 (completamente premuto) rappresentati come valori in virgola mobile. Il valore del controllo dell'acceleratore viene letto dalla proprietà Throttle dello struct RacingWheelReading. Il valore del controllo del freno viene letto dalla proprietà 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

Lettura di freno a mano e frizione

Freno a mano e frizione sono controlli opzionali che forniscono letture analogiche tra 0,0 (completamente rilasciato) e 1,0 (completamente innestato) rappresentati come valori in virgola mobile. Il valore del controllo del freno a mano viene letto dalla proprietà Handbrake dello struct RacingWheelReading; Il valore del controllo della frizione viene letto dalla proprietà 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
}

Lettura del variatore di pattern

Il variatore di pattern è un controllo opzionale che fornisce una lettura digitale tra -1 e MaxPatternShifterGear rappresentata come valore intero con segno. Un valore pari a -1 o 0 corrisponde rispettivamente a retromarcia e folle; valori sempre più positivi corrispondono a marce sempre maggiori fino alla MaxPatternShifterGear, compresa. Il valore del variatore di pattern viene letto dalla proprietà PatternShifterGear dello struct RacingWheelReading.

if (racingwheel->HasPatternShifter)
{
    gear = reading.PatternShifterGear;
}

Nota

Il variatore di pattern, dove supportato, esiste insieme ai pulsanti Marcia precedente e Marcia successiva necessari, che influenzano anche la marcia corrente dell'auto del giocatore. Una semplice strategia per unificare questi input in cui entrambi sono presenti consiste nell'ignorare il variatore di pattern (e la frizione) quando un giocatore sceglie una trasmissione automatica per la propria auto, e di ignorare i pulsanti Marcia precedente e Marcia successiva quando un giocatore sceglie una trasmissione manuale per la propria auto solo se il volante da corsa è dotato di un controllo per variatore di pattern. È possibile implementare una diversa strategia di unificazione se questa non è adatta per il gioco.

Esecuzione dell'esempio InputInterfacing

L'app di esempio InputInterfacingUWP su GitHub illustra come utilizzare i volanti da corsa e diversi tipi di dispositivi di input in parallelo, nonché il modo in cui questi dispositivi di input si comportano come controller di navigazione dell'interfaccia utente.

Panoramica del Force feedback

Molti volanti da corsa sono dotati di funzionalità Force feedback per offrire un'esperienza di guida più coinvolgente e stimolante. I volanti da corsa che supportano il Force feedback sono in genere dotati di un singolo motore che applica forza al volante lungo un singolo asse, l'asse di rotazione del volante. Force feedback è supportato nelle app UWP di Windows 10 o Windows 11 e Xbox One tramite lo spazio dei nomi Windows.Gaming.Input.ForceFeedback.

Nota

Le API di Force feedback sono in grado di supportare diversi assi di forza, ma attualmente nessun volante da corsa supporta un asse di feedback diverso da quello di rotazione del volante.

Uso del Force feedback

Queste sezioni descrivono le nozioni di base di programmazione degli effetti di Force feedback per i volanti da corsa. Il feedback viene applicato utilizzando effetti, che vengono prima caricati sul dispositivo di Force feedback e quindi possono essere avviati, messi in pausa, ripresi e arrestati in modo simile agli effetti sonori; Tuttavia, è necessario prima determinare le funzionalità di feedback del volante da corsa.

Determinazione delle funzionalità di Force feedback

È possibile determinare se un volante da corsa collegato supporta il Force feedback leggendo la proprietà WheelMotor del volante da corsa. Il Force feedback non è supportato se WheelMotor è null; in caso contrario, il Force feedback è supportato ed è possibile continuare a determinare le funzionalità di feedback specifiche del motore, come ad esempio gli assi che può influenzare.

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

Caricamento degli effetti di Force feedback

Gli effetti di Force feedback vengono caricati sul dispositivo di feedback dove vengono "riprodotti" in modo autonomo al comando del gioco. Viene fornito un certo numero di effetti di base; è possibile creare effetti personalizzati tramite una classe che implementa l'interfaccia IForceFeedbackEffect.

Classe effetto Descrizione effetto
ConditionForceEffect Effetto che applica una forza variabile in risposta al sensore corrente all'interno del dispositivo.
ConstantForceEffect Effetto che applica una forza costante lungo un vettore.
PeriodicForceEffect Effetto che applica una forza variabile definita da una forma d'onda, lungo un vettore.
RampForceEffect Effetto che applica una forza che aumenta/diminuisce in modo lineare lungo un vettore.
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 degli effetti di Force feedback

Una volta caricati, tutti gli effetti possono essere avviati, messi in pausa, ripresi e arrestati in modo sincrono chiamando funzioni sulla proprietà WheelMotor del volante da corsa o singolarmente chiamando le funzioni sull'effetto di feedback stesso. In genere, è necessario caricare tutti gli effetti che si desidera utilizzare sul dispositivo di feedback prima dell'inizio del gioco e quindi utilizzare le rispettive funzioni SetParameters per aggiornare gli effetti man mano che il gioco procede.

if (ForceFeedbackEffectState::Running == effect->State)
{
    effect->Stop();
}
else
{
    effect->Start();
}

Infine, è possibile abilitare, disabilitare o reimpostare in modo asincrono l'intero sistema di Force feedback su un particolare volante da corsa ogni volta che è necessario.

Vedere anche