¿Cómo funcionan las balas en los videojuegos?

64

Me encontré con esta pregunta cuando estaba diseñando un videojuego en C #.

Si consideramos juegos como Battlefield o Call of Duty , cientos o incluso miles de balas vuelan al mismo tiempo. Los eventos se desencadenan constantemente, y por lo que sé, esto absorbe mucha potencia de procesamiento ... ¿o no? Quiero saber cómo varios desarrolladores de juegos manejan las viñetas (2D y 3D) y cuál es el método más eficiente para cada uno.

Leí la pregunta ¿Cómo se simulan las balas en los videojuegos? pero no toca cómo funcionan las viñetas desde la perspectiva del diseño del programa.

Tenía un par de ideas, pero cada una tiene sus inconvenientes:


El método más eficiente que se me ocurrió (para juegos 2D):

Digamos que debía crear una clase llamada Bullet, y durante el tiempo que el usuario mantenga presionado un botón, cada 0.01 segundos se crearía un objeto Bullet. Esta bala tiene:

  • 1 velocidad

  • 2 Posición inicial de donde se está disparando

  • Textura de 3 sprites

  • 4 Un efecto de golpe

Dado que la viñeta sería su propia clase, podría administrar los oyentes de dibujo, movimiento y acción.

¿No sería difícil para el procesador procesar miles de estos objetos que se instancian y luego destruyen (cuando se activa el efecto de impacto)? Espacio RAM?


Método eficiente para juegos 3D: otro pensamiento que tuve fue:

Digamos que creo una clase de arma. Esta arma tiene varias características, algunas de las cuales:

  • 1 Detecta a dónde apunta el arma y determina si está mirando a un objetivo

  • 2 Activa una animación del tiroteo

  • 3 Tiene un método doDamage () que indica algo para restar salud de lo que sea que apunte el arma

  • 4 Notifica una clase de animación de viñeta cuando se presiona el botón

Entonces podría crear una clase estática, digamos BulletAnimation, que podría recibir una notificación de dónde está el arma que la activó, dónde apunta el arma (para el destino de la bala) e información sobre un sprite y velocidad apropiados para usar para la bala . Esta clase luego dibuja sprites (tal vez en un nuevo hilo, idk) basado en ambas posiciones y el sprite deseado, para simular una bala disparada desde una pistola.


Esto último parece mucho más difícil de codificar, y ¿no se necesitaría mucha potencia de procesamiento para llamar constantemente a la estática para hacer esto por miles de balas a la vez? También sería difícil obtener actualizaciones constantes tanto en las posiciones iniciales como finales.

Mi pregunta es, ¿cuál es la forma más eficiente en que los creadores de juegos lo hacen? ¿Este método cambia de juegos 2D a 3D?

Eric
fuente
16
Tenga en cuenta que incluso hoy, la mayoría de los juegos no simulan el vuelo de las balas. Para todo lo que se considere "lo suficientemente rápido", se realiza un simple hitcan en su lugar, básicamente, se supone que el impacto se produce al mismo tiempo que presiona el gatillo. En cualquier caso, "cientos o miles de balas" no es realmente una gran cantidad, eso es algo que los juegos tenían desde las primeras consolas (varios juegos de infierno de balas), miles de veces menos potentes que las máquinas actuales. Solo debes asegurarte de hacer solo el menor trabajo posible por bala :)
Luaan
42
Las balas generalmente funcionan a través de la pew-pew-pewtecnología :)
MonkeyZeus
55
Nunca hay cientos o miles de balas volando al mismo tiempo. No hay arma que los dispare tan rápido. Incluso la poderosa Phalanx alcanza 75 balas por segundo. Según el "campo de tiro efectivo" que figura en Wikipedia, las balas vuelan durante unos 3 segundos como máximo, por lo que una Phalanx puede poner 225 balas en el aire a la vez. Un M16 alcanza un máximo de alrededor de 12 rondas / segundo y no puede mantener esa velocidad (el máximo para fuego sostenido es 0.25 vueltas / segundo). ¡Simplemente no hay tantas armas disparando a la vez!
Cort Ammon
3
Solo para señalar esto, nunca es bueno hacer objetos de clases individuales cuando son tan simples. Es mucho mejor tener una instancia de bulletField para cada tipo de bala. La ligera sobrecarga en la longitud del código y otras cosas le ahorrará una palabra adicional de 4 bytes por viñeta (si el tipo es un entero). Además, un objeto puede escanear fácilmente una lista.
El gran pato
44
@Cort: eso es cierto, suponiendo que solo haya una arma de fuego en el espacio del juego. El OP mencionó juegos como Battlefield y CoD, donde docenas de jugadores podrían disparar armas automáticas simultáneamente. No es irrazonable que haya un número ridículo si cada ronda se contabilizó físicamente en el espacio.
Jesse Williams el

Respuestas:

78

Ciertamente, puedo ver por qué pensarías que sería difícil simularlos, pero hay suficientes restricciones en las balas (todos los proyectiles, realmente) para que sean más fáciles.

  1. Generalmente se simulan como un punto único, en lugar de como algo con volumen. Esto hace que la detección de colisiones sea significativamente más fácil, ya que ahora solo necesito hacer colisiones contra superficies muy simples, como una línea contra un círculo.

  2. Sabemos cómo se moverán, por lo que no hay mucha información que necesitemos almacenar o calcular para ellos. Su lista fue razonablemente precisa, generalmente tendremos algunas cosas más asociadas, como quién disparó la bala y cuál es su tipo.

  3. Como todos los proyectiles serán muy similares, podemos preasignarlos para evitar toda la sobrecarga de crearlos dinámicamente. Puedo asignar una matriz de 1000 proyectiles, y ahora se puede acceder a ellos con solo un índice, y todos son secuenciales en la memoria, por lo que el procesamiento será rápido.

  4. Tienen una vida útil / rango fijo, por lo que puedo expirar viñetas viejas y reciclar la memoria en viñetas nuevas muy rápidamente.

  5. Una vez que golpean algo, también puedo expirarlos, por lo que tienen una vida limitada.

  6. Como sabemos cuándo se crearon, si necesitamos otros nuevos y no tenemos ninguno gratuito en nuestra lista preasignada, puedo tomar los más antiguos y reciclarlos, y la gente no se dará cuenta si las balas caducan un poco antes .

  7. Se representan como sprites (generalmente) o como modelos de baja poli, y ocupan muy poco espacio en la pantalla, por lo que son rápidos de renderizar.

Teniendo en cuenta todas esas cosas, las balas tienden a ser relativamente baratas. Si nuestro presupuesto alguna vez fue consumido por las balas y renderizándolas, generalmente lo rediseñamos para limitar la cantidad de disparos que puede disparar a la vez (lo verá en muchos juegos de arcade antiguos), use armas de haz que se muevan instantáneamente , o reduzca la velocidad de disparo para asegurarse de que nos mantengamos dentro del presupuesto.

Tom K
fuente
12
Debo estar en desacuerdo con 5), lo que en realidad hace que todo sea complicado en los juegos modernos. En tiradores anteriores esto era aceptable, hoy en día incluso COD permite a los jugadores disparar a través de paredes de madera. 6) sería inaceptable para cualquier sistema competitivo, aunque sería un problema raro.
SBoss 01 de
17
@SBoss luego reformula: "Una vez que golpean algo que no pueden penetrar, también puedo expirarlos, por lo que tienen una vida limitada". Y para 6 puede obtener el peor de los casos al limitar la velocidad máxima de disparo por personaje y luego mantener una variedad de longitudnum_characters * max_bullets_per_character
monstruo de trinquete el
14
@SBoss Creo que # 6 es más, por ejemplo. juegos espaciales de arriba hacia abajo, donde una bala de movimiento lento puede viajar una gran distancia fuera de la pantalla antes de golpear algo / desaparecer. Obviamente, no es un problema en los juegos tipo CoD, donde las balas se mueven rápido y alcanzan rápidamente los límites del mundo.
BlueRaja - Danny Pflughoeft 01 de
1
La gran mayoría de los juegos NO modelan balas (balística externa) en absoluto. La mayoría de los juegos emplean una técnica llamada "hit-scan".
Aron
44

Probablemente una de las formas más eficientes de implementar viñetas es usar lo que se conoce como hitscan . Su implementación es bastante simple: cuando disparas, verificas a qué apunta el arma (posiblemente usando un rayo para encontrar la entidad / objeto / malla más cercana), y luego lo 'golpeas', haciendo daño. Si desea que parezca que se disparó una bala invisible real y de rápido movimiento, puede simularla agregando un ligero retraso dependiendo de la distancia antes de hacer daño.

Este enfoque esencialmente asume que el proyectil disparado tiene una velocidad infinita, y generalmente se usa para tipos de armas como láseres y haces de partículas / cañones y tal vez algunas formas de rifles de francotirador .

El siguiente enfoque sería modelar la bala disparada como un proyectil, que se modela como su propia entidad / objeto que está sujeto a colisión, y posiblemente la resistencia a la gravedad y / o al aire que altera su dirección y velocidad. Es más complejo que el enfoque hitscan debido a las ecuaciones físicas adicionales, y requiere más recursos debido a que existe un objeto de bala real, pero puede proporcionar viñetas más realistas.

En cuanto a la gestión de las colisiones entre las balas con base de proyectiles y otros objetos en el juego, detección de colisiones se puede simplificar en gran medida por ordenar los objetos en quad o octrees . Los octrees se usan principalmente en juegos 3d, mientras que los quadtrees se pueden usar en juegos 2d o 3d. Las ventajas de usar uno de estos árboles es que puede reducir en gran medida el número de posibles controles de colisión. Por ejemplo, si tiene 20 objetos activos en el nivel, sin usar uno de estos árboles, deberá verificar que los 20 tengan una colisión con la bala. Al dividir los 20 objetos entre las hojas (nodos finales) del árbol, puede reducir el número de controles a la cantidad de entidades presentes en la misma hoja que la viñeta.

En cuanto a estos enfoques: hitscan y proyectiles, ambos se pueden usar libremente en juegos 2d o 3d. Depende más de qué es el arma y de cómo el creador ha decidido que el arma funcionará.

Seta
fuente
La información sobre el patrón de diseño, Hitscan y quad / octrees realmente ayuda. Además, gracias por la información!
Eric
8
Si no demanda un hitcan pero simula los proyectiles, pueden deformarse a través de objetos delgados porque se mueven muy rápido. En ese caso, recuerda que todo en los juegos es falso. Aunque una bala tiene solo unos pocos centímetros de largo, puede hacer la detección de colisión como si la bala tuviera un metro de largo. De esta manera, aún puede hacer simulaciones agradables de caída de bala y tiempo de vuelo sin tener que preocuparse demasiado por las balas que se deforman a través de los objetos sin golpearlos :).
Roy T.
2
¿Hay juegos en los que la física de las balas (a diferencia de, por ejemplo, proyectiles de cañón) respete cosas como la gravedad (caída de balas), la resistencia al aire? (Es decir, juegos además de juegos especializados donde el enfoque es el tiro al blanco de precisión o algo así: FPS, etc.) No soy un jugador, pero me sorprende que ese nivel de fidelidad sea necesario (incluso a veces).
davidbak 01 de
3
@davidbak: depende en gran medida del encuentro típico en el juego y del realismo esperado del género del juego. Si estás luchando principalmente (¿solo?) En combate cuerpo a cuerpo, entonces, de hecho, ese nivel de fidelidad no es necesario. Pero si existe la opción de combate de largo alcance (por ejemplo, francotiradores o arqueros en un entorno más parecido a un juego de rol), hoy en día se espera que los misiles que afectan a la gravedad. Si apuntas tu lanzacohetes hacia arriba, aún esperarías que el cohete aterrice y explote en algún lado, ¿no? Aún así, las trayectorias no siempre se calculan a partir de la física real, solo una aproximación (por razones de rendimiento)
hoffmale
1
@davidbak Battlefield desde que Bad Company 2 tuvo una caída de bala. Tanto para rifles, pistola, proyectiles de tanques, cohetes, todo. Battlefield 3 es gratis en Origin, puedes comprobarlo (IIRC). Battlefield 4, por supuesto, también tiene esta 'característica'. Otro juego donde puedes ver esto es "Sniper Elite". 2 o 3 son los títulos más nuevos. La física juega un papel importante en ese juego.
Apache
7

De ninguna manera soy un experto, pero para responder a su pregunta, sí, necesitaría muchas de esas cosas que menciona.

Para su ejemplo 2D, podría tener una posición y velocidad para una bala. (Es posible que también necesite una distancia máxima o de por vida, dependiendo de cómo implemente sus viñetas). Eso generalmente implicaría 2 valores (x, y). Si fueran flotadores, eso es 16 bytes. Si tiene 100 balas, eso es solo 1600bytes o aproximadamente 1.5k. Eso no es nada en una máquina hoy.

A continuación, mencionas los sprites. Solo necesitarías un solo sprite para representar cada viñeta. Su tamaño dependerá de la profundidad de bits en la que esté dibujando y de cuán grande debería aparecer en la pantalla. Incluso sin comprimir, digamos, 256x256 en flotación total de 32 bits por canal, eso es 1 MB para el sprite. (¡Y eso sería muy grande!) Dibujaría el mismo sprite en cada ubicación de viñeta, pero no requiere memoria adicional para cada copia del sprite. Sería similar para un efecto de impacto.

Usted menciona disparar cada 0.01 segundos. Eso sería 100 balas por segundo de tu arma. ¡Incluso para un arma futurista que es bastante! De acuerdo con este artículo de Wikipedia :

Cuando se aprieta el gatillo, la velocidad a la que se disparan las rondas es la velocidad cíclica. Las velocidades de disparo cíclicas típicas son 600–900 RPM para fusiles de asalto, 1,000-1,100 RPM en algunos casos, 900-1,200 RPM para metralletas y ametralladoras, y 600-1,200 RPM para ametralladoras. Las ametralladoras M134 montadas en helicópteros de ataque y otros vehículos de combate pueden alcanzar velocidades de disparo de más de 100 disparos por segundo (6,000 RPM).

¡Entonces esa sería la velocidad de un helicóptero de ataque!

Para un mundo grande como el que mencionas en Battlefield / Call of Duty / etc., pueden calcular todas esas posiciones de bala, pero no sacarlas todas si la acción está muy lejos. O pueden no simularlos hasta que te acerques. (Tengo que admitir que estoy adivinando un poco sobre esta parte, ya que no he trabajado en nada tan grande).

usuario1118321
fuente
6

¿No sería difícil para el procesador procesar miles de estos objetos que se instancian y luego destruyen (cuando se activa el efecto de impacto)? Espacio RAM?

Creo que estás subestimando lo rápido que son las computadoras. Esto fue a veces un problema en los sistemas de los años 80 y 90. Es en parte por qué los Space Invaders originales no te permitirán disparar otra bala hasta que la actual haya alcanzado. Algunos juegos sufrían de "desaceleración" si había demasiados sprites en la pantalla.

¿Pero hoy en día? Tiene suficiente potencia de procesamiento para miles de operaciones por píxel que se requieren para texturizar e iluminar. No hay problema con miles de objetos en movimiento; Esto le permite hacer un terreno destructible (por ejemplo, Red Faction) donde cada fragmento procesa la colisión con otros fragmentos y sigue una curva balística.

Debe tener un poco de cuidado algorítmicamente: no puede hacer el enfoque ingenuo de verificar cada objeto contra cualquier otro objeto cuando tiene miles de objetos. Las balas generalmente no verifican las colisiones con otras balas.

Una pequeña anécdota: la primera versión de Doom en red (el original de los 90) envió un paquete a través de la red por cada bala disparada. Cuando uno o más jugadores obtuvieron la ametralladora, esto podría abrumar fácilmente la red. Los años 90 estaban llenos de personas que jugaban ilícitamente Doom en la universidad o en redes de trabajo que se metían en problemas con sus administradores de red cuando la red se volvía inutilizable.

pjc50
fuente
Me pregunto cómo funcionó la motosierra en este contexto
reas0n
1
IIRC, el verdadero problema con la primera perdición de la red es que evitó la necesidad de enviar cada paquete por separado a cada jugador contrario mediante el uso de paquetes de transmisión. Eso redujo la cantidad de paquetes enviados, pero desafortunadamente planteó una carga considerable de CPU en cada máquina de la red, incluidas las que no estaban jugando.
supercat
1

Estoy lejos de ser un experto, pero he estado trabajando en un juego de disparos 2D multijugador en mi tiempo libre.

Mi metodo

Hay diferentes clases de viñetas entre el cliente y el servidor (incluso cuando se juega sin conexión, una instancia del servidor se inicia en un proceso separado y se conecta mediante el juego 'principal').

En cada tic (60 por segundo), el cliente calcula el rumbo entre el puntero del mouse del jugador y el centro de la pantalla (donde está su personaje) y es parte de la información enviada al servidor. Si el jugador también está disparando en ese momento (suponiendo que el arma esté cargada y lista), se crea una instancia de bala del lado del servidor, simplemente con algunas coordenadas y un daño base (que se deriva de las estadísticas del arma que disparó eso). La instancia de viñeta luego usa algunas funciones matemáticas para calcular una velocidad X e Y a partir del rumbo que reunimos del cliente.

Por cada tic posterior, la bala se mueve por esas coordenadas y reduce su daño base en una cantidad predefinida. Si este valor cae por debajo de 1, o si golpea un objeto sólido en el mundo, la instancia de bala se elimina, y como las colisiones de puntos de prueba son increíblemente baratas en 2D, incluso las armas de disparo rápido tienen un impacto insignificante en el rendimiento.

En cuanto al cliente, la información de la viñeta en realidad no se recibe a través de la red (demostró ser un desperdicio en las pruebas), en cambio, como parte de la actualización por tick, cada personaje tiene un booleano 'disparado', que si es verdadero, el cliente crea un local objeto de bala que funciona casi exactamente como los servidores, la única diferencia es que tiene un sprite.

Esto significa que, aunque la viñeta que ve no es una representación totalmente precisa de la misma en el servidor, cualquier diferencia sería apenas perceptible en absoluto para un jugador, y los beneficios de la red superan cualquier inconsistencia.

Nota sobre diferentes métodos

Algunos juegos, incluido el mío, mueven las viñetas cada tic como si fueran objetos físicos, mientras que otros simplemente crean un vector en la dirección del disparo, o calculan el camino completo de la viñeta en el tic que se crea, por ejemplo en Counter- Juegos de huelga. Hay algunos pequeños trucos del lado del cliente para disfrazarlo, como una animación del disparo de bala, pero para todos los efectos, cada bala es solo un láser .

Con los modelos 3D que pueden tener hitboxes complejos, es estándar probar las colisiones contra un cuadro delimitador simple PRIMERO, y si eso tiene éxito, pasar a una detección de colisión más 'detallada'.

TheCatOfWar
fuente
0

Se llama detección de colisión. Las computadoras de 8 bits hicieron esto usando gráficos de misiles de jugador en hardware. Los motores de juegos modernos usan motores de física y álgebra lineal. La dirección actual de un arma se representa como un vector 3D. Eso proporciona una línea infinita en la dirección del fuego. Cada objeto en movimiento tiene una o más esferas de delimitación, ya que es el objeto más simple para detectar una colisión con una línea. Si los dos se cruzan, es un golpe, si no, no hay golpe. Pero el escenario puede estar en el camino, por lo que también debe verificarse (utilizando volúmenes de límite jerárquico). El objeto más cercano que tiene una intersección es el que ha sido golpeado.

Mikael
fuente