Ho un sacco di oggetti di varie dimensioni e velocità che gravitano l'uno verso l'altro. Ad ogni aggiornamento, devo esaminare ogni oggetto e sommare le forze dovute alla gravità di ogni altro oggetto. Non si adatta molto bene, è uno dei due grandi colli di bottiglia che ho trovato nel mio gioco e non sono sicuro di cosa fare per migliorare le prestazioni.
Si sente come dovrei essere in grado di migliorare le prestazioni. In qualsiasi momento, probabilmente il 99% degli oggetti nel sistema avrà solo un'influenza trascurabile su un oggetto. Ovviamente non posso ordinare gli oggetti in base alla massa e prendere in considerazione solo i primi 10 oggetti più grandi o qualcosa del genere, perché la forza varia con la distanza più che con la massa (l'equazione è lungo le linee di force = mass1 * mass2 / distance^2
). Penso che una buona approssimazione sarebbe quella di considerare gli oggetti più grandi e gli oggetti più vicini, ignorando le centinaia di minuscoli frammenti di roccia dall'altra parte del mondo che non possono influenzare nulla, ma per scoprire quali oggetti sono più vicino devo iterare su tutti gli oggetti e le loro posizioni cambiano costantemente, quindi non è che posso farlo una volta sola.
Attualmente sto facendo qualcosa del genere:
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);
}
(si noti che ho rimosso molto codice, ad esempio i test di collisione, non riesco a scorrere gli oggetti una seconda volta per rilevare le collisioni).
Quindi non sto sempre ripetendo l'intera lista - lo faccio solo per il primo oggetto, e ogni volta che l'oggetto trova la forza che prova verso un altro oggetto, quell'altro oggetto sente la stessa forza, quindi aggiorna entrambi loro - e quindi quel primo oggetto non deve essere considerato di nuovo per il resto dell'aggiornamento.
Le funzioni Gravity.ForceUnderGravity(...)
e Motion.Velocity(...)
, ecc. Usano solo un po 'di matematica vettoriale integrata di XNA.
Quando due oggetti si scontrano, creano detriti senza massa. È tenuto in un elenco separato e gli oggetti massicci non iterano sui detriti come parte del loro calcolo della velocità, ma ogni pezzo di detriti deve iterare sulle particelle massicce.
Questo non deve scalare a limiti incredibili. Il mondo non è illimitato, contiene un bordo che distrugge gli oggetti che lo attraversano - mi piacerebbe essere in grado di gestire forse un migliaio di oggetti, attualmente il gioco inizia a soffocare intorno ai 200.
Qualche idea su come potrei migliorare questo? Qualche euristica che posso usare per radere la lunghezza del loop da centinaia a pochi? Qualche codice che posso eseguire meno spesso di ogni aggiornamento? Devo solo multithread fino a quando non è abbastanza veloce da consentire un mondo di dimensioni decenti? Dovrei provare a scaricare i calcoli di velocità sulla GPU? Se è così, come lo progetterei? Posso conservare dati statici e condivisi sulla GPU? Posso creare funzioni HLSL sulla GPU e chiamarle arbitrariamente (usando XNA) o devono far parte del processo di disegno?
G * m1 * m2 / r^2
dove G è solo per modificare il comportamento. (anche se non posso semplicemente farli seguire un percorso, perché l'utente può disturbare il sistema)