Hurtig Tips Bruk Ring Buffer Datastruktur til jevn Jittery Verdier

Mens du utvikler et spill, kan du finne verdier som er for bråkete for dine behov. Det vanlige tilfellet er analog brukerinngang (mus, berøring eller joystick), men støyen kan også komme fra spillsystemet, som fysikk eller styringsadferd, der omtrentlige løsninger eller uavhengige endringer fører til støy. I denne opplæringen lærer du en enkel måte å glatte de støyende verdiene på. Kodeksemplene er i C #, men de er enkle å tilpasse seg til noe annet språk.


Ringbuffer

Den enkleste måten å glatte den varierende verdien på er å ta et antall av de siste prøvene og gjennomsnittlig dem. Vi bruker et konstant antall prøver, så en fast størrelse er et naturlig og effektivt valg for lagring av disse. Da, for å unngå å bytte ut det, bruker vi et triks: "ringbuffer" datastrukturen.

La oss begynne med å definere dataene som skal lagres i vår brukerklasse:

 offentlig klasse SlidingAverage float [] buffer; float sum; int sisteIndex; offentlig SlidingAverage (int num_samples, float initial_value) buffer = ny float [num_samples]; lastIndex = 0; reset (initial_value); 

Her har vi vår prøvebuffer, summen av prøvene, og den siste brukte indeksen i matrisen. Konstruktøren allokerer buffergruppe, sett lastIndex til null, og kaller tilbakestille() metode:

 offentlig ugyldig nullstilling (float-verdi) sum = verdi * buffer.Length; for (int i = 0; i 

Her fyller vi bufferen med den angitte startverdien, og setter summen som passer til den. Denne metoden kan brukes når som helst du må starte utjevningen for å unngå minneeffekter fra de siste prøvene.

Nå er hovedmetoden: å trykke en ny verdi inn i ringbufferen vår:

 public void pushValue (float value) sum- = buffer [lastIndex]; // trekke den eldste prøven fra summen summet + = verdien; // legg til den nye prøvebufferen [lastIndex] = verdi; // lagre den nye prøven // forskyv indeksen og pakk den rundt sistIndex + = 1; hvis (sistIndex> = buffer.Length) lastIndex = 0; 

Her overskriver vi den eldste prøven på lastIndex med den nye, men før det justerer vi summen ved å subtrahere den gamle prøven og legge til den nye.

Så fortsetter vi lastIndex slik at den peker til neste prøve (som nå er den eldste). Men hvis vi bare går videre lastIndex Vi løper ut av mat på kort tid, så når den kommer ut av båndet, setter vi det rundt til null.

Det er derfor det er en ringe buffer. Det er i det vesentlige det samme som å skifte arrayet og legge til den nye prøven, men mye raskere siden i stedet for å kopiere verdiene i minnet, slår vi bare inn indeksen.

Nå er det eneste som mangler, den glatte verdien:

 offentlig float getSmoothedValue () retur sum / buffer.Length; 

Det er det; vi deler bare summen av antall prøver for å få gjennomsnittet. Hvis vi ikke lagret summen, må vi beregne det her fra prøvene.


resultater

La oss se på resultatene:


Den svarte linjen er det opprinnelige signalet (sinusbølge med litt støy), den hvite linjen glattes med to prøver, og den røde linjen glattes med fire prøver.

Som du ser, til og med noen få prøver gjør det merkbart jevnere, men jo flere prøver vi bruker, jo mer ligger det bak det opprinnelige signalet. Det er forventet siden vi bare bruker tidligere prøver i sanntidssaken. Hvis du er etterbehandling, kan du skifte de jevne verdiene i tide for å unngå lagringen.


Konklusjon

Du har nå en enkel verktøysklasse som kan brukes til å glatte alle støyende innkommende verdier, enten det er brukerinngang, en objektspor eller en hastighetsindikator.

Det kan forbedres ytterligere ved å legge prøvevekter (vi brukte et enkelt gjennomsnitt med konstant 1 / N vekt), men det er et stort emne, digital signalbehandling, og bedre igjen til en fremtidig opplæring!