Eu tenho vários objetos de tamanho e velocidade variados que gravitam um para o outro. Em toda atualização, eu tenho que passar por cima de todos os objetos e somar as forças devido à gravidade de todos os outros objetos. Ele não escala muito bem, é um dos dois grandes gargalos que encontrei no meu jogo e não tenho certeza do que fazer para melhorar o desempenho.
Ele se sente como eu deveria ser capaz de melhorar o desempenho. A qualquer momento, provavelmente 99% dos objetos no sistema terão apenas uma influência desprezível em um objeto. É claro que não posso classificar os objetos por massa e considerar apenas os 10 maiores objetos ou algo assim, porque a força varia mais com a distância do que com a massa (a equação é ao longo das linhas de force = mass1 * mass2 / distance^2
). Eu acho que uma boa aproximação seria considerar os objetos maiores e os objetos mais próximos, ignorando as centenas de pequenos fragmentos de rocha do outro lado do mundo que não podem afetar nada - mas para descobrir quais objetos são o mais próximo que eu tiver que percorrer todos os objetos, e suas posições estão mudando constantemente, então não é como se eu pudesse fazer isso apenas uma vez.
Atualmente estou fazendo algo parecido com isto:
private void UpdateBodies(List<GravitatingObject> bodies, GameTime gameTime)
{
for (int i = 0; i < bodies.Count; i++)
{
bodies[i].Update(i);
}
}
//...
public virtual void Update(int systemIndex)
{
for (int i = systemIndex + 1; i < system.MassiveBodies.Count; i++)
{
GravitatingObject body = system.MassiveBodies[i];
Vector2 force = Gravity.ForceUnderGravity(body, this);
ForceOfGravity += force;
body.ForceOfGravity += -force;
}
Vector2 acceleration = Motion.Acceleration(ForceOfGravity, Mass);
ForceOfGravity = Vector2.Zero;
Velocity += Motion.Velocity(acceleration, elapsedTime);
Position += Motion.Position(Velocity, elapsedTime);
}
(observe que removi muito código - por exemplo, os testes de colisão, não repetimos os objetos pela segunda vez para detectar colisões).
Portanto, nem sempre estou repetindo a lista inteira - apenas faço isso para o primeiro objeto, e toda vez que o objeto encontra a força que sente em relação a outro objeto, esse outro objeto sente a mesma força, então apenas atualiza os dois. eles - e, em seguida, esse primeiro objeto não precisará ser considerado novamente pelo restante da atualização.
As funções Gravity.ForceUnderGravity(...)
e Motion.Velocity(...)
etc. apenas usam um pouco do XNA construído em matemática vetorial.
Quando dois objetos colidem, eles criam detritos sem massa. Ele é mantido em uma lista separada e os objetos maciços não iteram sobre os detritos como parte de seu cálculo de velocidade, mas cada pedaço de detrito deve iterar sobre as partículas maciças.
Isso não precisa ser escalado para limites incríveis. O mundo não é ilimitado, ele contém uma borda que destrói objetos que o atravessam - eu gostaria de poder lidar com talvez cerca de mil objetos, atualmente o jogo começa a sufocar cerca de 200.
Alguma idéia de como eu poderia melhorar isso? Alguma heurística que eu possa usar para raspar o comprimento do loop de centenas para poucos? Algum código que eu posso executar com menos frequência que todas as atualizações? Devo apenas multithread até que seja rápido o suficiente para permitir um mundo de tamanho decente? Devo tentar descarregar os cálculos de velocidade na GPU? Se sim, como eu arquitetaria isso? Posso manter dados estáticos e compartilhados na GPU? Posso criar funções HLSL na GPU e chamá-las arbitrariamente (usando XNA) ou elas precisam fazer parte do processo de desenho?
G * m1 * m2 / r^2
onde G é apenas para ajustar o comportamento. (embora eu não posso simplesmente tê-los seguindo um caminho, porque o usuário pode perturbar o sistema)