¿Cuáles son tus gemas de codificación específicas de juego favoritas? [cerrado]


24

Comenzaré con la raíz cuadrada inversa rápida de John Carmack en Quake III:

float Q_rsqrt(float number) {

  long i;
  float x2, y;
  const float threehalfs = 1.5F;

  x2 = number * 0.5F;
  y = number;
  i = * ( long * ) &y;
  i = 0x5f3759df - ( i >> 1 );
  y = * ( float * ) &i;
  y = y * ( threehalfs - ( x2 * y * y ) );

  return y;

}

66
Esa no es realmente una pregunta: al menos, podría declarar que esta es una página wiki de la comunidad ...
Rachel Blum

Hecho, comunitario.
gak

3
¡Tornillo que! ¡solo ve con todos y cada uno de los códigos creados por el gran JC!
Adam Naylor

3
Por cierto, tenga en cuenta que hay un "número mágico" más preciso para usar en la función de raíz cuadrada inversa rápida: 0x5f375a86 ( en.wikipedia.org/wiki/… )
Ricket

8
También tenga en cuenta que no es de John Carmack.
Kaj

Respuestas:


25

función mapValue:

float mapValue( float inVal, float inFrom, float inTo, float outFrom, float outTo )
{
    float inScale = (inFrom != inTo) 
        ? ( ( inVal - inFrom ) / ( inTo - inFrom ) ) 
        : 0.0f;
    float outVal = outFrom + ( inScale * ( outTo - outFrom ) );
    outVal = (outFrom < outTo ) 
        ? clamp( outVal, outFrom, outTo ) 
        : clamp( outVal, outTo, outFrom );
    return outVal;
}

Toma un valor, lo convierte en una proporción dentro de un rango y luego lo escala en relación con otro rango. Como un doble lerp.

Puedes usarlo para normalizar cosas:

float minDamage = 0.0f; float maxDamage = 300.0f;
float normalisedDamage = mapValue(damange, minDamage, maxDamage, 0.0f, 1.0f);

O puede convertir de un rango a otro:

float brakeStrength = mapValue(timeToCollision, 
    0.0f, 10.0f, // seconds
    1.0f, 0.2f // brake values 
    );

Observe en el segundo ejemplo que el rango de salida es un orden diferente al rango de entrada.

No parece mucho, pero uso a este pequeño amigo por todas partes.


14

Todavía no puedo creer cuántas veces he usado el Teorema de Pitágoras en mi código de juego. Para mí, esta fórmula simple es una joya en el desarrollo del juego.

a ^ 2 + b ^ 2 = c ^ 2
(fuente: mathurl.com )

o

texto alternativo
(fuente: mathurl.com )

y cuando solo importa la distancia relativa, se puede usar sin una costosa operación de raíz cuadrada

texto alternativo
(fuente: mathurl.com )


Para ser honesto, las matemáticas pitagóricas se usan TODO sobre el código del juego, especialmente los motores, como el código de física, el código de representación, la IA.
Nick Bedford

1
Cierto. Por eso es una joya. ;)
MrValdez

44
Una variación interesante es cuando solo te interesan las distancias relativas . Luego puede omitir la sqrtllamada potencialmente costosa y simplemente calcular distance2 = x^2 + y^2.
mmyers

@mmyers - Otra cosa maravillosa: si estás trabajando en un espacio x ^ 2, la distancia no es solo relativa.
Steven Evers

Gracias por mencionar el bit de distancia relativa. He visto demasiadas operaciones innecesarias de raíz cuadrada, al igual que las personas que usan A * cuando deberían estar usando algo menos exacto.
Ricket


10

Tengo que ir con el dispositivo de Duff . Fue el primer bloque de código que literalmente hizo que se me cayera la boca. "¿¡¿Usted puede hacer eso?!?"


Dios mio. ¡Las gafas! ¡Ellos no hacen nada!
Raoul

El "do-while" anidado dentro del "switch-case" siempre me hace parpadear. Incluso 20 años después ...
Andreas

1
-1. No me gusta este Hoy no es muy útil (en su memcpylugar, usaría si desea que otras personas puedan leer su código)
bobobobo

1
@JoeWreschnig ¿por qué no? Memcpy siempre será más legible, portátil y probablemente más optimizado.
kaoD

1
@kaoD: Debido a que memcpy no es equivalente al dispositivo de Duff, no importa cuán "legible, portátil y probablemente más optimizado" sea si no hace lo mismo. Las tuberías de CPU modernas probablemente funcionarán mejor sin el dispositivo de Duff que con él debido a la predicción de ramificaciones y el almacenamiento en caché de instrucciones, pero eso no tiene nada que ver con eso memcpy.

7

Un pequeño fragmento de C / C ++ de un juego que ayudé a escribir hace muchos años:

(fill ? FillRect : DrawRect) (x, y, w, h, colour);

En mi primer juego ( este ) necesitaba acceder a más de 1Mb de RAM y, esto antes de que despegara Internet, no tenía documentación para XMS y EMS que las aplicaciones de DOS usaban para acceder a la RAM adicional.

Entonces, terminé usando una pequeña 'puerta trasera' que aparecía en el 386 con respecto a los registros de segmento. Normalmente, en modo real, la dirección se calculaba como lo seg*16+offque lo limitaba a 1Mb.

Sin embargo, puede cambiar al modo protegido, configurar un segmento para direccionar 4Mb, volver a cambiar, y siempre que no haya escrito en el registro del segmento (lo cual estaba bien ya que DOS solo usaba los registros del segmento 8086), podía acceder al conjunto 4Mb como espacio de direcciones plano. Volver al modo real era necesario si deseaba usar los servicios de DOS.

Tampoco había muchos extensores DPMI disponibles.


Casi funciona en C # (debe declarar un tipo de delegado e insertar al menos un elenco)
finnw

¿Te refieres a los modos de direccionamiento de 32 bits en modo real? (es decir, requiere un prefijo de tamaño de dirección). ¿Está seguro de que era necesario configurar un descriptor de segmento y que en realidad no utilizaba el registro de segmento como el 16 * FS + normal, lo que sea? No pensé que los segmentos tuvieran límites en el modo de 16 bits, solo la dirección base (16 * su valor), por lo que no podría simplemente configurar FS en algo en modo de 16 bits y usar [fs:esi]o lo que sea para acceder a lo que sea ¿querido?
Peter Cordes

No he intentado esto, ni he escrito ningún código real para el modo de 16 bits, solo 32 y 64 bits, pero suena extraño. Hmm, plausible sin embargo. Las CPU modernas definitivamente almacenan en caché los segmentos internamente, y solo vuelven a cargar esos cachés cuando escribes en los registros de segmentos, así que tal vez así es como mantuvo el límite base + del modo protegido (si eso fue lo que sucedió, y no solo estabas usando el 4 MB a partir de 16 * FS).
Peter Cordes

5

Personalmente, soy un gran admirador del Mersenne Twister por los números aleatorios predecibles, especialmente si necesita crear varias instancias de Rand con semillas diferentes.

http://en.wikipedia.org/wiki/Mersenne_twister


Mersenne Twister es agradable en el sentido de que es un buen generador de números aleatorios, pero no es particularmente elegante o genial (o rápido o fácil de implementar). Para eso, es posible que desee ver en.wikipedia.org/wiki/Rule_30 .

1
WELL es generalmente mejor que MT para la mayoría de los usos. En.wikipedia.org/wiki/Well_equidistributed_long-period_linear
Jari Komppa

5

Aquí hay uno mencionado por Chris Crawford (y aparentemente usado por Atari) al que llama 'Un truco gráfico':

LDA FIRST
EOR SECOND
AND CONTROL
EOR SECOND
STA OUTPUT

Lea el artículo completo para obtener una explicación.


1
Ese enlace ahora está roto, por lo que sería útil tener una explicación aquí.
finnw

Gracias. Se fue y cambió su sitio web nuevamente. Actualicé el enlace.
Anthony

4

Por alguna razón, las personas a menudo subestiman el poder de los patrones de diseño en los juegos. He visto casi todos los patrones GoF aplicados con éxito a los juegos.


1
¡Una de las cosas que me gustan de la programación de juegos es alejarse de la forma estándar de "corrección" de GoF y centrarse en una velocidad pura y encantadora! Dicho esto, he visto muchas implementaciones malas de MVC en juegos que hacen más daño que bien.
Iain

Creo que los patrones de diseño tienen su lugar. Sin embargo, no tanto en los juegos.
blissfreak

@blissfreak: No hay nada especial en la programación de juegos que lo convierta en una especie de salvaje oeste donde los patrones no son comunes. Son ridículamente comunes y aquí hay algunos ejemplos.
Steven Evers

4

Math.atan2 () es extremadamente útil (junto con todos los trigonométricos).


Es, Dios! He hecho mucho 3D y nunca lo he necesitado realmente.
Skizz

+1, es la única forma de pasar de los componentes del vector x + y a la dirección en radianes.
RCIX

1
@RCIX: Mi punto es que esa transformación es innecesaria, es decir (x, y) -> ángulo, hay una solución vectorial para cualquier problema de ángulo.
Skizz

66
Realmente no llamaría a una función de biblioteca estándar simple una "joya".

1
@Skizz: Tal vez para 3d, pero no conozco otra forma de tomar un vector normalizado y extraer un valor de dirección en radianes. Lo cual es una gran cantidad de valor en los juegos 2D.
RCIX

3

Para agregar a la gema pitagórica de arriba ...
Siempre les digo a las personas que para la programación en 3D solo necesitan saber:
- a ^ 2 + b ^ 2 = c ^ 2
- soscastoa (sin = lado opuesto / lado inclinado, cos = lado adjunto / lado inclinado, tan = lado opuesto / lado adjunto)
- a. b = | a | * | b | * cos alfa
- a * b = | a | * | b | * sin alpha * vector de unidad
Puede resolver prácticamente cualquier problema 3D (o 2d) que encuentre en el desarrollo del juego: 4 reglas.
Claro, hay maneras más agradables, pero esto puede resolverlos a todos: debo saber que soy un hackista que basó su carrera en ellos.


55
re: soscastoa Creo que la mayoría de la gente lo sabe como sohcahtoa (reemplazando el 'lado inclinado' por la 'hipotenusa' más específica, aunque ciertamente más oscura). Fluye de la lengua más fácilmente, y creo que es más fácil de recordar.
Asmor

1
Siempre preferiré 'El viejo árabe se sentó en su camello y aulló', ya que la escuela secundaria (edad de secundaria) se ha grabado en mi cerebro.
George Duckett

También hay " S ome O ld H ippy C ame A nd H ad T ripped O n A cid"
blissfreak

3

Uno de mis favoritos es la versión en lenguaje ensamblador de 'Life', y la descripción completa de cómo optimizarlo, en " The Zen of Code Optimization" de Michael Abrash.

Recomendaría cualquiera de sus libros a cualquiera que busque gemas de codificación.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.