Mientras desarrolla un juego, puede encontrar valores que son demasiado ruidosos para sus necesidades. El caso común es la entrada de usuario analógica (ratón, toque o joystick), pero el ruido también puede provenir de los sistemas del juego, como la física o los comportamientos de dirección, donde las soluciones aproximadas o los cambios no contiguos producen ruido. En este tutorial, aprenderá una forma sencilla de suavizar esos valores ruidosos. Los ejemplos de código están en C #, pero son fáciles de adaptar a cualquier otro idioma.
La forma más sencilla de suavizar el valor variable es tomar una serie de muestras pasadas y promediarlas. Usaremos un número constante de muestras, por lo que una matriz de tamaño fijo es una opción natural y eficiente para almacenarlas. Luego, para evitar cambiar esa matriz, usaremos un truco: la estructura de datos del "búfer de anillo".
Empecemos definiendo los datos para almacenar en nuestra clase de utilidad:
public class SlidingAverage float [] buffer; suma flotante int lastIndex; SlidingAverage público (int num_samples, float initial_value) buffer = new float [num_samples]; lastIndex = 0; restablecer (valor_inicial);
Aquí tenemos nuestro búfer de muestras, la suma de las muestras y el último índice utilizado en la matriz. El constructor asigna la matriz de búfer, establece último índice
a cero, y llama al Reiniciar()
método:
restablecimiento público vacío (valor flotante) suma = valor * búfer.Longitud; para (int i = 0; iAquí, llenamos el búfer con el valor inicial especificado y configuramos la suma para que coincida. Este método se puede utilizar en cualquier momento en que necesite reiniciar el suavizado para evitar efectos de memoria de muestras pasadas.
Ahora, el método principal: introducir un nuevo valor en nuestro búfer de anillo:
public void pushValue (float value) sum- = buffer [lastIndex]; // restar la muestra más antigua de la suma suma + = valor; // agregar el nuevo búfer de muestra [lastIndex] = valor; // almacenar la nueva muestra // avanzar el índice y envolverlo alrededor de LastIndex + = 1; if (lastIndex> = buffer.Length) lastIndex = 0;Aquí, sobreescribimos la muestra más antigua en
último índice
con la nueva, pero antes de eso ajustamos la suma restando la muestra anterior y agregando la nueva.Entonces, avanzamos
último índice
para que apunte a la siguiente muestra (que ahora es la más antigua). Pero si solo avanzamosúltimo índice
nos quedaremos sin matriz en poco tiempo, por lo que cuando se sale del límite de la matriz, la redondeamos a cero.Es por eso que es un anillo buffer. Es esencialmente lo mismo que cambiar la matriz y agregar la nueva muestra, pero mucho más rápido, ya que en lugar de copiar los valores en la memoria, simplemente envolvemos el índice.
Ahora, lo único que falta es obtener el valor suavizado:
public float getSmoothedValue () return sum / buffer.Length;Eso es; simplemente dividimos la suma por el número de muestras para obtener el promedio. Si no almacenáramos la suma, tendríamos que calcularla aquí a partir de las muestras..
Resultados
Echemos un vistazo a los resultados:
La línea negra es la señal original (onda sinusoidal con algo de ruido), la línea blanca se suaviza utilizando dos muestras y la línea roja se suaviza utilizando cuatro muestras.Como puede ver, incluso algunas muestras lo hacen notablemente más suave, pero cuantas más muestras usamos, más se retrasa con respecto a la señal original. Eso se espera ya que solo usamos muestras pasadas en el caso en tiempo real. Si está procesando posteriormente, puede cambiar los valores suavizados a tiempo para evitar el retraso.
Conclusión
Ahora tiene una clase de utilidad simple que se puede usar para suavizar cualquier valor entrante ruidoso, ya sea una entrada del usuario, un rastro de objetos o un indicador de velocidad.
Se puede mejorar aún más agregando pesos de muestra (utilizamos un promedio simple con valores constantes).
1 / N
peso), pero ese es un gran tema, el procesamiento de la señal digital, y es mejor dejarlo para un futuro tutorial!