Como as balas funcionam nos videogames?

64

Me deparei com essa pergunta quando estava projetando um videogame em c #.

Se considerarmos jogos como Battlefield ou Call of Duty , centenas ou até milhares de balas estão voando ao mesmo tempo. Os eventos são acionados constantemente e, pelo que sei, isso consome muito poder de processamento ... ou é? Quero saber como vários desenvolvedores de jogos gerenciam marcadores (2D e 3D) e qual é o método mais eficiente para cada um.

Eu li a pergunta Como as balas são simuladas nos videogames? mas não toca em como os marcadores funcionam da perspectiva do design do programa.

Eu tive algumas idéias, mas cada uma tem suas desvantagens:


O método mais eficiente que eu consegui pensar (para jogos 2D):

Digamos que eu criei uma classe chamada Bullet e, por quanto tempo o usuário mantenha pressionado um botão, a cada 0,01 segundo, um objeto Bullet será criado. Este marcador possui:

  • 1 velocidade

  • 2 Posição inicial de onde está sendo filmado

  • 3 textura de Sprite

  • 4 Um efeito ao acertar

Como a bala seria de sua própria classe, poderia gerenciar os ouvintes de desenho, movimento e ação.

Não seria difícil para o processador processar milhares desses objetos sendo instanciados e depois destruídos (quando o efeito de ativação é acionado)? Espaço RAM?


Método eficiente para jogos em 3D - Outro pensamento que tive foi:

Digamos que eu crie uma classe de armas. Essa arma possui vários recursos, alguns dos quais:

  • 1 Detecte onde a arma está apontando e determine se está olhando para um alvo

  • 2 Acione uma animação do disparo da arma

  • 3 Possui um método doDamage () que indica algo para subtrair a saúde de qualquer coisa que a arma apontar

  • 4 Notifica uma classe de animação de marcadores quando o botão é pressionado

Eu poderia criar uma classe estática, por exemplo, BulletAnimation, que poderia receber notificações de onde está a arma que a acionou, para onde está apontada (para o destino da bala) e informações sobre um sprite e velocidade apropriados para usar na bala. . Essa classe então desenha sprites (talvez em um novo segmento, idk) com base nas posições e no sprite desejado, para simular uma bala sendo disparada de uma arma.


O último parece muito mais difícil de codificar, e não seria necessário muito poder de processamento para chamar constantemente a estática para fazer isso por milhares de marcadores por vez? Obter atualizações constantes nas posições inicial e final também seria difícil.

Minha pergunta é: qual é a maneira mais eficiente de os criadores de jogos fazerem isso? Esse método muda de jogos 2D para 3D?

Eric
fonte
16
Observe que ainda hoje, a maioria dos jogos não simula o vôo de balas. Para qualquer coisa considerada "rápida o suficiente", um simples hitcan é executado - basicamente, é assumido que o impacto ocorre ao mesmo tempo em que você pressiona o gatilho. De qualquer forma, "centenas ou milhares de balas" não são realmente uma quantia grande - isso é algo que os jogos tinham desde os primeiros consoles (vários jogos de balas-inferno), milhares de vezes menos poderosos do que as máquinas de hoje. Você só precisa ter certeza de fazer apenas como pouco trabalho por bala quanto possível :)
Luaan
42
Balas geralmente trabalham através de pew-pew-pewtecnologia :)
MonkeyZeus
5
Nunca existem centenas ou milhares de balas voando ao mesmo tempo. Não há arma que os atire tão rápido. Até o poderoso Phalanx atinge 75 balas por segundo. Com base no "Campo de tiro efetivo" listado na Wikipedia, as balas voam por cerca de 3 segundos, no máximo, para que um Phalanx possa colocar 225 balas no ar ao mesmo tempo. Um M16 atinge a volta de cerca de 12 disparos / s e não pode sustentar essa taxa (o máximo para fogo prolongado é de 0,25 jardas / s). Simplesmente não existem muitas armas disparando ao mesmo tempo!
Cort Ammon
3
Apenas para salientar isso, nunca é bom criar objetos em classes individuais quando são tão simples. É muito melhor ter uma instância bulletField para cada tipo de marcador. A pequena sobrecarga no comprimento do código e outros enfeites economizarão uma palavra extra de 4 bytes por marcador (se o tipo for um número inteiro). Além disso, um objeto pode facilmente digitalizar uma lista.
The Great Duck
4
@Cort - isso é verdade, supondo que haja apenas uma arma de fogo no espaço do jogo. O OP mencionou jogos como Battlefield e CoD, onde dezenas de jogadores poderiam estar disparando armas automáticas simultaneamente. Não é irracional haver um número ridículo se cada rodada for realmente considerada fisicamente no espaço.
Jesse Williams

Respostas:

78

Eu posso certamente entender por que você pensaria que seria difícil simulá-las, mas existem restrições suficientes nas balas (todos os projéteis, na verdade) para torná-las mais fáceis.

  1. Eles geralmente são simulados como um único ponto, em vez de algo com volume. Isso facilita significativamente a detecção de colisões, pois agora eu só preciso fazer colisões em superfícies muito simples, como uma linha contra um círculo.

  2. Sabemos como eles se moverão, portanto não há muita informação que precisamos armazenar ou calcular para eles. Sua lista era razoavelmente precisa; geralmente, temos mais algumas coisas associadas a ela, como quem disparou a bala e qual é o seu tipo.

  3. Como todos os projéteis serão muito semelhantes, podemos pré-alocá-los, para evitar toda a sobrecarga de criá-los dinamicamente. Eu posso alocar uma matriz de 1000 projéteis, e agora eles podem ser acessados ​​com apenas um índice, e todos são seqüenciais na memória, para processá-los rapidamente.

  4. Eles têm uma vida útil / intervalo fixos, para que eu possa expirar marcadores antigos e reciclar a memória em novos marcadores rapidamente.

  5. Uma vez que eles atingem algo, também posso expirá-los, para que tenham uma vida útil finita.

  6. Como sabemos quando eles foram criados, se precisamos de novos e não temos nenhum livre em nossa lista pré-alocada, eu posso simplesmente pegar os mais antigos e reciclá-los, e as pessoas não notarão se os marcadores expiram um pouco mais cedo .

  7. Eles são renderizados como sprites (geralmente) ou modelos de baixo poli e ocupam muito pouco espaço na tela, por isso são rápidos em renderizar.

Levando tudo isso em consideração, as balas tendem a ser relativamente baratas. Se nosso orçamento for consumido por balas e renderizado, geralmente o redesenharemos para limitar o número de tiros que você pode disparar por vez (você verá isso em muitos jogos antigos de arcade), use armas de raios que se movem instantaneamente , ou diminua a taxa de disparo para garantir que permaneçamos dentro do orçamento.

Tom K
fonte
12
Devo discordar de 5), o que na verdade apenas torna tudo complicado nos jogos modernos. Nos atiradores anteriores, isso era aceitável, atualmente até o COD permite que os jogadores atirem através de paredes de madeira. 6) seria inaceitável para qualquer sistema competitivo, embora fosse um problema raro.
SBoss
17
O @SBoss reformulou: "Depois que eles atingem algo que não podem penetrar, também posso expirá-los, para que tenham uma vida útil finita". E para 6, você pode obter o pior caso limitando a taxa máxima de disparos por personagem e mantendo uma variedade de comprimentosnum_characters * max_bullets_per_character
catraca
14
@SBoss acho que # 6 é mais para, por exemplo. jogos espaciais de cima para baixo, onde uma bala lenta pode percorrer uma grande distância fora da tela antes de atingir algo / desaparecer. Obviamente, isso não é um problema nos jogos do tipo CoD, onde as balas se movem rapidamente e atingem rapidamente os limites do mundo.
BlueRaja - Danny Pflughoeft 01/07
11
A grande maioria dos jogos NÃO modela balas (balística externa). A maioria dos jogos emprega uma técnica chamada "hit-scan".
Aron4
45

Provavelmente, uma das maneiras mais eficientes de implementar marcadores é usar o que é conhecido como hitscan . É bastante simples em sua implementação - quando você dispara, você verifica o que a arma está mirando (possivelmente usando um raio para encontrar a entidade / objeto / malha mais próxima) e, em seguida, você 'bate' nela, causando dano. Se você quiser fazer com que pareça mais uma bala invisível real e rápida, foi disparada, você pode falsificá-la adicionando um pequeno atraso, dependendo da distância, antes de causar danos.

Essa abordagem pressupõe essencialmente que o projétil disparado tem velocidade infinita e é tipicamente usada para tipos de armas como lasers e feixes / canhões de partículas e talvez algumas formas de rifles de precisão .

A próxima abordagem seria modelar a bala disparada como um projétil, modelado como sua própria entidade / objeto sujeito a colisão e, possivelmente, a gravidade e / ou resistência do ar que altera sua direção e velocidade. É mais complexo do que a abordagem hitscan devido às equações extras da física e consome mais recursos devido à existência de um objeto de marcador real, mas pode fornecer marcadores mais realistas.

Quanto ao gerenciamento de colisões entre balas baseadas em projéteis e outros objetos no jogo, a detecção de colisões pode ser bastante simplificada, classificando seus objetos em quad ou octrees . Octrees são usados ​​principalmente em jogos 3D, enquanto quadtrees podem ser usados ​​em jogos 2D ou 3D. As vantagens de usar uma dessas árvores é que você pode reduzir bastante o número de possíveis verificações de colisão. Por exemplo, se você tiver 20 objetos ativos no nível, sem usar uma dessas árvores, precisará verificar todos os 20 quanto a uma colisão com o marcador. Dividindo os 20 objetos entre as folhas (nós finais) da árvore, é possível reduzir o número de verificações para o número de entidades presentes na mesma folha que o marcador.

Quanto a essas abordagens - hitscan e projétil, ambas podem ser usadas livremente em jogos 2D ou 3D. Depende mais do que a arma é e de como o criador decidiu que a arma deve funcionar.

Seta
fonte
As informações sobre o padrão de design, Hitscan e quad / octrees realmente ajudam. Além disso, obrigado pela informação!
Eric
8
Se você não processa um hitscan, mas simula os projéteis, eles podem distorcer objetos finos porque se movem muito rápido. Nesse caso, lembre-se de que tudo em jogos é falso. Mesmo que uma bala tenha apenas alguns centímetros de comprimento, você pode fazer a detecção de colisão como se a bala tivesse um metro de comprimento. Dessa forma, você ainda pode fazer boas simulações de queda de bala e tempo de voo sem ter que se preocupar muito com as balas deformando os objetos sem atingi-los :).
Roy T.
2
Existem jogos em que a física das balas (em oposição a, por exemplo, projéteis de canhão) respeita coisas como gravidade (queda de bala), resistência do ar? (Ou seja, jogos além de jogos especiais, nos quais o foco é o tiro com precisão ou algo assim: FPS, etc.) Não sou um jogador, mas estou surpreso que esse nível de fidelidade seja (às vezes) necessário.
Davidbak #
3
@davidbak: depende fortemente do encontro típico no jogo e do realismo esperado do gênero do jogo. Se você está lutando principalmente (apenas?) De perto, então esse nível de fidelidade não é necessário. Mas se existe a opção de combate a longo alcance (por exemplo, atiradores de elite ou arqueiros em um cenário mais parecido com um RPG), a gravidade que afeta os mísseis é hoje o tipo de expectativa. Se você apontar o lançador de foguetes para cima, ainda esperaria que o foguete aterrisse e explodisse em algum lugar, não? Ainda assim, as trajetórias não são sempre calculados a partir da física real, apenas uma aproximação (por motivos de desempenho)
hoffmale
11
@davidbak Battlefield desde Bad Company 2 teve queda de bala. Tanto para rifles, pistolas, cartuchos, foguetes, tudo. Battlefield 3 é gratuito no Origin, você pode conferir (IIRC). O Battlefield 4, é claro, também possui esse 'recurso'. Outro jogo onde você pode ver isso é "Sniper Elite". 2 ou 3 são os títulos mais recentes. A física desempenha um papel importante nesse jogo.
Apache
7

Eu não sou um especialista, mas para responder sua pergunta, sim, você precisaria de muitas dessas coisas que menciona.

Para o seu exemplo 2D, você pode ter uma posição e velocidade para uma bala. (Você também pode precisar de uma distância máxima ou vitalícia, dependendo de como implementar seus marcadores.) Isso normalmente envolveria 2 (x, y) valores. Se fossem carros alegóricos, são 16 bytes. Se você tiver 100 marcadores, isso significa apenas 1600 bytes ou cerca de 1,5 mil. Hoje não há nada em uma máquina.

A seguir, você menciona os sprites. Você precisaria apenas de um único sprite para representar cada marcador. Seu tamanho dependeria da profundidade de bits que você está desenhando e do tamanho que deve aparecer na tela. Mesmo descompactado em, digamos, 256x256 em full float de 32 bits por canal, isso representa 1 MB para o sprite. (E isso seria muito grande!) Você desenharia o mesmo sprite em cada local do marcador, mas não é necessário memória adicional para cada cópia do sprite. Seria semelhante para um efeito de acerto.

Você menciona disparar a cada 0,01 segundos. Isso seria 100 balas por segundo da sua arma. Mesmo para uma arma futurista, é bastante! De acordo com este artigo da Wikipedia :

Quando o gatilho é acionado, a taxa na qual os disparos são disparados é a taxa cíclica. As taxas cíclicas típicas de fogo são de 600 a 900 RPM para rifles de assalto, 1.000 a 1.100 RPM em alguns casos, 900 a 1.200 RPM para metralhadoras e pistolas-metralhadoras e 600-1.200 RPM para metralhadoras. As miniguns M134 montadas em helicópteros de ataque e outros veículos de combate podem atingir taxas de tiro de mais de 100 disparos por segundo (6.000 RPM).

Então essa seria a taxa de um helicóptero de ataque!

Para um mundo grande como você mencionou em Battlefield / Call of Duty / etc., Eles podem calcular todas essas posições de bala, mas não desenhar todas se a ação estiver longe. Ou eles não podem simulá-los até você chegar perto. (Tenho que admitir que estou adivinhando um pouco dessa parte, pois não trabalhei em nada tão grande.)

user1118321
fonte
6

Não seria difícil para o processador processar milhares desses objetos sendo instanciados e depois destruídos (quando o efeito de ativação é acionado)? Espaço RAM?

Acho que você está subestimando a rapidez com que os computadores são. Às vezes, isso era um problema nos sistemas dos anos 80 e 90. É em parte porque os Invasores do Espaço originais não permitem disparar outra bala até que a atual seja atingida. Alguns jogos sofriam "desaceleração" se houvesse muitos sprites na tela.

Hoje em dia, porém? Você tem poder de processamento suficiente para milhares de operações por pixel necessárias para texturizar e iluminar. Não há problema com milhares de objetos em movimento; isso permite que você faça terrenos destrutíveis (por exemplo, Red Faction), onde cada fragmento processa colisão com outros fragmentos e segue uma curva balística.

Você precisa ter um pouco de cuidado algoritmicamente - não pode fazer a abordagem ingênua de verificar todos os objetos em relação a qualquer outro objeto quando tiver milhares de objetos. Os marcadores geralmente não verificam colisões com outros marcadores.

Uma pequena anedota: a primeira versão do Doom em rede (original dos anos 90) enviou um pacote pela rede para cada bala disparada. Quando um ou mais jogadores pegam a metralhadora, isso pode facilmente sobrecarregar a rede. Os anos 90 estavam cheios de pessoas jogando ilegalmente o Doom na universidade ou nas redes de trabalho, enfrentando problemas com os administradores da rede quando a rede se tornou inutilizável.

pjc50
fonte
Eu me pergunto como a serra trabalhou neste contexto
reas0n
11
IIRC, o verdadeiro problema com a primeira desgraça da rede é que ela evitou a necessidade de enviar cada pacote separadamente para cada jogador oponente usando pacotes de transmissão. Isso reduziu o número de pacotes enviados, mas infelizmente representou uma carga considerável de CPU em todas as máquinas da rede, incluindo aquelas que não estavam jogando.
Supercat
1

Estou longe de ser um especialista, mas tenho trabalhado em um jogo de tiro 2D multiplayer no meu tempo livre.

Meu método

Existem várias classes de marcadores entre o cliente e o servidor (mesmo quando jogando offline, uma instância do servidor é iniciada em um processo separado e conectada ao jogo 'principal').

A cada tick (60 por segundo), o cliente calcula a relação entre o ponteiro do mouse do jogador e o centro da tela (onde está o personagem) e faz parte das informações enviadas ao servidor. Se o jogador também estiver atirando naquele momento (supondo que a arma esteja carregada e pronta), é criada uma instância de bala do lado do servidor, com apenas algumas coordenadas e um dano base (que deriva das estatísticas da arma que disparou isto). A instância do marcador usa algumas funções matemáticas para calcular a velocidade X e Y do rolamento que coletamos do cliente.

Para cada escala subsequente, a bala se move por essas coordenadas e reduz o dano base em uma quantidade predefinida. Se esse valor for inferior a 1 ou se atingir um objeto sólido no mundo, a instância de bala é excluída e, como as colisões de pontos de teste são incrivelmente baratas em 2D, até mesmo armas de disparo rápido têm um impacto insignificante no desempenho.

Quanto ao cliente, as informações do marcador não são realmente recebidas pela rede (provou ser um desperdício nos testes); em vez disso, como parte da atualização por tick, cada personagem tem um booleano 'acionado', que, se verdadeiro, o cliente cria um local objeto de marcador que funciona quase exatamente como o servidor, a única diferença é que ele tem um sprite.

Isso significa que, embora o marcador que você vê não seja uma representação totalmente precisa dele no servidor, qualquer diferença seria quase imperceptível se fosse para um jogador, e os benefícios da rede superam quaisquer inconsistências.

Nota sobre diferentes métodos

Alguns jogos, incluindo o meu, movem as balas a cada marca como se fossem objetos físicos, enquanto outros apenas criam um vetor na direção do tiro ou calculam todo o caminho da bala na marca que é criada, por exemplo, em Jogos de greve. Existem alguns truques do lado do cliente para disfarçá-lo, como uma animação do disparo de balas, mas para todos os efeitos, cada bala é apenas um laser .

Nos modelos 3D que podem ter hitboxes complexos, é padrão testar colisões com uma simples caixa delimitadora PRIMEIRO e, se for bem-sucedido, prossiga com a detecção de colisões mais 'detalhada'.

TheCatOfWar
fonte
0

É chamado de detecção de colisão. Computadores de 8 bits fizeram isso usando gráficos de jogador-míssil no hardware. Os motores de jogos modernos usam motores de física e álgebra linear. A direção atual de uma arma é representada como um vetor 3D. Isso fornece uma linha infinita na direção do fogo. Todo objeto em movimento possui uma ou mais esferas delimitadoras, pois esse é o objeto mais simples para detectar uma colisão com uma linha. Se os dois se cruzam, isso é um sucesso, se não, não há sucesso. Mas o cenário pode estar no caminho, de modo que também deve ser verificado (usando volumes limitados hierárquicos). O objeto mais próximo que possui uma interseção é aquele que foi atingido.

Mikael
fonte