Come rimuovere il jitter dall'input di movimento?


9

Sto scrivendo una mod di Minecraft che supporta input dal Razer Hydra . È una coppia di controller di movimento (uno per ogni mano) che forniscono informazioni di posizione e rotazione incredibilmente accurate.

Ai fini di questa domanda, la rotazione del controller destro sull'asse Y fa apparire il personaggio del giocatore a sinistra o a destra (imbardata) e la rotazione sull'asse X fa apparire il giocatore su e giù (inclinazione).

L'input dal controller è mappato direttamente all'intestazione del personaggio. Se il controller viene ruotato di 30 gradi a sinistra, il personaggio gira di 30 gradi a sinistra.

Il problema è che l'ingresso "nervosismo". Se provo a tenere il controller perfettamente fermo, l'intestazione del personaggio si sposta in modo irregolare all'interno di un cono molto piccolo (forse 1 grado).

Ciò è probabilmente dovuto alle mie mani tremanti, poiché i dati del controller sono apparentemente esatti.

Ho provato a filtrare l'input calcolando la media dei dati degli ultimi X frame, ma questo fa sembrare l'input burroso.

La mia domanda è: come posso filtrare i dati di rotazione per rimuovere il jitter senza perdere precisione?


hai considerato di ignorare cambiamenti molto piccoli nei movimenti?
Philipp

1
Questo Razer Hyrdra è interessante ... la prima volta che ne ho sentito parlare, ancora più interessante è scrivere una mod di Minecraft per completarla ... La mia ipotesi è: proprio come un'immagine pixelata, per "ridurre il rumore" è necessario per sfocare l'immagine ... Fondamentalmente puoi vivere con l'immagine pixelata o vivere con l'immagine sfocata ... Sento che questo è lo stesso principio, devi scegliere quale preferisci, gioco traballante o meno precisione ... ... È proprio quello che penso ...
Luke San Antonio Bialecki,

1
Puoi trarre il meglio da entrambi. Memorizza sempre l'ultimo dire 50 frame. Ma in media solo alcuni di essi, a seconda della quantità di movimento in ingresso. Per i grandi movimenti, basarsi solo su diciamo gli ultimi 5 fotogrammi e per i piccoli movimenti (altrimenti jittering) fare affidamento sugli ultimi 30 fotogrammi.
danijar,

@sharethis È un'idea interessante. Potrei finire per implementare qualcosa del genere. Il jitter non è un problema una volta che l'ingresso supera una certa soglia, quindi potrei semplicemente fare la media dei piccoli input per rimuovere il jitter e non fare la media di nulla per input di grandi dimensioni.
Mele

Risposte:


8

Questo problema di ritardo rispetto alla reattività è la situazione con praticamente tutti i controller di movimento, che siano qualcosa come Hydra, il telecomando Wii, Kinect o PlayStation Move.

Il problema è questo:

Quando arriva un flusso di input, prendi una decisione fotogramma per fotogramma sull'affidabilità o meno dei dati di input; se le tendenze che stai vedendo ora continueranno nei dati che ricevi tra una dozzina di millisecondi da adesso. Ad esempio, se c'è un improvviso spostamento a destra di questo frame, non sai se si tratta di un vero bit di dati di input (e quindi dovresti agire su di esso) o se è semplicemente jitter (e quindi dovresti ignorarlo). Qualunque cosa tu scelga, se in seguito scopri di aver sbagliato, hai permesso al jitter di input di entrare nel tuo gioco (nel primo caso) o hai introdotto un ritardo nel tuo gioco (nel secondo caso).

Non esiste una buona soluzione a questo. Una soluzione "corretta" per determinare se l'input è reale o jitter richiede di sapere cosa farà il flusso di input in futuro, nonché cosa ha fatto in passato. Non possiamo farlo nei giochi, per ovvie ragioni. Quindi no, non c'è modo di filtrare i dati di rotazione per rimuovere il jitter senza perdere precisione, nel contesto di un gioco che sta lavorando con i dati di input in tempo reale.

Ho visto un produttore importante raccomandare agli sviluppatori di affrontare questo problema facendo in modo che i giocatori tenessero premuto un pulsante mentre ruotavano il controllo, in modo che il gioco potesse disattivare il suo codice anti-jitter a quel punto, quindi è senza ritardi. (Non lo consiglio, ma è un approccio).

Ho visto alcune librerie di middleware di input di movimento che affrontano questo problema introducendo un ritardo artificiale nell'input: c'è un buffer di un quarto di secondo in cui entrano i dati di input e il tuo gioco sente l'input solo un quarto di secondo dopo, in modo che la libreria possa appianare il jitter per te, sapendo cosa succede sia prima che dopo il "presente" dal punto di vista del gioco. Funziona benissimo, a parte l'introduzione di un quarto di secondo di ritardo su tutto. Ma è un modo per risolvere il problema e può fare un ottimo lavoro nel rappresentare accuratamente un movimento con jitter rimosso, a scapito di un ritardo costante.

Ma senza andare all'estremo, ci sono ancora alcune cose che possiamo fare per dare un comportamento notevolmente migliorato, anche se sappiamo che ci saranno sempre "scenari peggiori" che si comporteranno in modo non ideale.

L'intuizione fondamentale è che ci preoccupiamo davvero del jitter solo quando il controller è per lo più stazionario, e ci interessa davvero solo il ritardo quando il controller viene spostato. Quindi la nostra strategia dovrebbe essere quella di cercare di affrontare le cose in modo da avere un ritardo quando il controller è fermo e avere jitter quando il controller è in movimento.

Ecco due modi possibili per farlo:

Un approccio comune è un sistema "bloccato / sbloccato", in cui si tiene traccia dell'orientamento del dispositivo e, se non cambia per un breve periodo (mezzo secondo circa), si 'blocca' tale orientamento, senza prendere azione basata sull'orientamento riportato sul dispositivo fino a quando non differisce abbastanza da "sbloccare" di nuovo. Questo può eliminare completamente il jitter basato sull'orientamento, senza introdurre lag quando l'orientamento sta cambiando attivamente. Potrebbe esserci un accenno di ritardo prima che il codice decida che deve passare alla modalità "sbloccata", ma sarà molto meglio che avere un ritardo ovunque.

Un altro approccio è la media insieme dei dati di input dai frame. Il punto importante qui è solo la media dei dati di input provenienti da frame in cui i dati di input erano vagamente simili. abbastanza simile ai dati dei frame precedenti.

Esistono anche altri modi per ottenere un effetto simile. L'intuizione fondamentale è che non puoi avere sia jitter che non lag nel tuo gioco in tempo reale allo stesso tempo, perché per farlo sarebbe necessaria la conoscenza del futuro. Quindi è necessario scegliere quando deviare il comportamento del controllo verso l'accettazione del jitter e quando orientarlo verso l'accettazione del ritardo, al fine di rendere l'esperienza complessiva il più non male possibile.


Ho appena pubblicato una risposta, puoi per favore esprimere la tua opinione sulla mia soluzione?
Mele

2

Sviluppo software che converte l'input di movimento in input del mouse reattivo e preciso, oltre a mantenere un sito Web che cerca di aiutare gli sviluppatori a implementare soluzioni ugualmente valide. In genere sconsiglio le soglie di movimento, anche se dipende da quanta reattività e precisione desiderano i giocatori, sono contento che funzioni per te nella tua situazione. Ma qui offrirò una soluzione diversa:

Uso qualcosa che si chiama Smooth Tiered Smoothing . L'idea è che deviamo l'input attraverso diversi algoritmi di smoothing a seconda della grandezza attuale della velocità del giroscopio (in pratica, uno di quegli algoritmi di smoothing è semplicemente "nessun smoothing"). Questa è la parte "a più livelli". La parte "soft" è che possiamo effettivamente dividere uniformemente l'input tra diversi algoritmi di smoothing a seconda di come si confronta con 2 soglie.

Conserva lo spostamento correttamente e non aggiunge alcun ritardo ai movimenti rapidi.

In pratica, hai due soglie. Quando l'entità della velocità di input è inferiore alla soglia inferiore, stiamo usando un semplice algoritmo di smoothing (media su più frame). Quando è maggiore dell'altra soglia, non utilizziamo affatto alcun algoritmo di smoothing. Ma in questo caso, stiamo ancora passando zeri all'algoritmo di livellamento della soglia inferiore.

Quando la velocità di input è tra le due soglie, dividiamo di conseguenza l'input tra i due algoritmi.

Ecco un frammento dell'articolo sopra:

GetSoftTieredSmoothedInput(Vec2 input, float threshold1, float threshold2) {
    // this will be length(input) for vectors
    float inputMagnitude = Abs(input);

    float directWeight = (inputMagnitude - threshold1) / (threshold2 - threshold1);
    directWeight = clamp(directWeight, 0, 1);

    return GetDirectInput(input * directWeight) +
        GetSmoothedInput(input * (1.0 - directWeight));
}

GetDirectInput restituisce semplicemente ciò che gli è stato dato, ma è per mostrare che qui potrebbe essere usato un altro algoritmo di smoothing. GetSmoothedInput prende una velocità e restituisce una velocità levigata.

Con Soft Smoothed Smoothing non viene applicato alcun livellamento a movimenti ovviamente intenzionali (sopra la soglia maggiore), viene applicato un livellamento per coprire piccole quantità di jitter, che influirà anche su movimenti molto piccoli, ma quando si ottengono le soglie giuste non è molto evidente. E c'è una transizione molto fluida tra i due (senza la quale, il jitter può essere effettivamente amplificato).

Mentre le altre risposte hanno ragione a dire che è difficile riconoscere il jitter nell'istante in cui viene ricevuto un input, è anche vero che il jitter è quasi sempre una velocità molto bassa e il ritardo di input che viene fornito con il smoothing è molto meno evidente per gli input a bassa velocità .

Come menziona l'articolo, questo è usato in un paio di punti nel mio strumento open source JoyShockMapper , un mappatore di input che trasforma l'input giroscopico in input del mouse. Anche per le persone che usano altri strumenti di rimappatura come Steam o reWASD, alcuni usano JoyShockMapper allo stesso tempo solo per i suoi controlli giroscopici.

Questa risposta presuppone che l'ingresso sia dato in velocità angolare (che è comune con i controller che hanno controlli di movimento), non un orientamento assoluto (che suona come il Razer Hydra ti sta dando). Con l'orientamento assoluto, la mia speranza è che tu possa usare la differenza tra l'orientamento corrente e l'orientamento precedentemente riportato per ottenere una velocità, ma non so per certo se funzionerà così come con i controller che riportano da sé la velocità angolare .

Una soluzione di livellamento comune quando si ha a che fare con una posizione / orientamento assoluto piuttosto che con le velocità è interpolare nel tempo verso l'orientamento dell'obiettivo - questo è descritto in dettaglio molto utile in questo articolo di Gamasutra. Questo può funzionare anche con il livellamento graduale morbido. Calcolerai la grandezza della velocità usando la differenza tra questo input e quello precedente riportato. Applica la differenza di orientamento tra questo frame e l'ultimo frame moltiplicata per il valore "directWeight" calcolato nello snippet sopra. L'ultimo passaggio consiste nell'aggiungere l'input livellato, ma a causa del modo in cui funziona il livellamento dell'orientamento interpolato, basta applicare la modifica dell'orientamento interpolato come al solito - non è necessario considerare "directWeight". Basta impostare l'orientamento del target (questo è ciò a cui si interpola con il livellamento descritto nell'articolo Gamasutra) a qualsiasi orientamento si stia ottenendo dal dispositivo e interpolare l'orientamento verso di esso come descritto in quell'articolo.


1

È strano rispondere alla mia domanda, ma penso di aver trovato la mia soluzione.

//Pseudo-Java

update()
{
    //deltaYaw is the change in yaw of the controller since last update
    //yawBuffer is initialized to zero, and only modified here
    //coneAngle is the stabilizing cone

    deltaYaw = getData().yaw;

    yawBuffer += deltaYaw;
    if (abs(yawBuffer) >= coneAngle)
    {
        player.yaw += (abs(yawBuffer)-coneAngle) * sign(yawBuffer);
        yawBuffer = coneAngle * sign(yawBuffer);
    }
}

Invece di modificare direttamente l'intestazione del giocatore, semplicemente "spingo" un cono di un determinato angolo (nel mio caso, 2,5 gradi). Ho realizzato una piccola demo HTML5 di questa tecnica.

Una volta che si inizia a spingere il cono, non v'è nulla di ritardo e pieno di precisione. Tuttavia, se spingi il cono a sinistra, e poi vuoi mirare a destra, devi attraversare l'intero angolo del cono per vedere un effetto.

Quindi risolve i problemi del ritardo temporale e del livellamento orribile, ma introduce il nuovo problema di una soglia di movimento. Tuttavia, se il cono di stabilizzazione è regolato correttamente, la soglia è impercettibile.


Questo sembra un altro modo ragionevole per farlo. C'erano videocamere (costose) che stabilizzavano la loro immagine usando questo approccio; ti dà precisione mentre continui un movimento in una direzione, ma ha un ritardo quando cambi direzione. Se funziona bene per il tuo gioco, allora fallo assolutamente. Non troverai una soluzione unica che funzioni meglio per ogni gioco; è sempre un compromesso in cui è necessario valutare i lati negativi di ogni approccio rispetto alle esigenze specifiche del gioco specifico che stai realizzando. :)
Trevor Powell
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.