J'ai un tas d'objets de taille et de vitesse variables qui gravitent l'un vers l'autre. À chaque mise à jour, je dois parcourir chaque objet et additionner les forces dues à la gravité de tous les autres objets. Il ne évolue pas très bien, est l'un des deux gros goulots d'étranglement que j'ai trouvés dans mon jeu, et je ne sais pas quoi faire pour améliorer les performances.
Il se sent comme je devrais être en mesure d'améliorer les performances. À tout moment, 99% des objets du système n'auront probablement qu'une influence négligeable sur un objet. Bien sûr, je ne peux pas trier les objets par masse et ne considérer que les 10 plus gros objets ou quelque chose du genre, car la force varie plus avec la distance qu'avec la masse (l'équation est dans le sens de force = mass1 * mass2 / distance^2
). Je pense qu'une bonne approximation serait de considérer les plus gros objets et les objets les plus proches, en ignorant les centaines de minuscules fragments de roche de l'autre côté du monde qui ne peuvent pas affecter quoi que ce soit - mais afin de savoir quels objets sont le plus proche, je dois parcourir tous les objets, et leurs positions changent constamment, donc ce n'est pas comme si je pouvais le faire une seule fois.
Actuellement, je fais quelque chose comme ça:
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);
}
(notez que j'ai supprimé beaucoup de code - par exemple les tests de collision, je ne répète pas les objets une deuxième fois pour détecter les collisions).
Je ne suis donc pas toujours en train d'itérer sur toute la liste - je ne fais cela que pour le premier objet, et chaque fois que l'objet trouve la force qu'il ressent vers un autre objet, cet autre objet ressent la même force, donc il ne fait que mettre à jour les deux eux - et puis ce premier objet ne doit pas être considéré à nouveau pour le reste de la mise à jour.
Les fonctions Gravity.ForceUnderGravity(...)
et Motion.Velocity(...)
, etc. utilisent simplement un peu de mathématiques vectorielles intégrées de XNA.
Lorsque deux objets entrent en collision, ils créent des débris sans masse. Il est conservé dans une liste séparée et les objets massifs ne parcourent pas les débris dans le cadre de leur calcul de vitesse, mais chaque morceau de débris doit itérer sur les particules massives.
Cela n'a pas à atteindre des limites incroyables. Le monde n'est pas illimité, il contient une frontière qui détruit les objets qui le traversent - j'aimerais pouvoir gérer peut-être un millier d'objets, actuellement le jeu commence à s'étouffer autour de 200.
Avez-vous des idées sur la façon dont je pourrais améliorer cela? Une heuristique que je peux utiliser pour raser la longueur de la boucle de centaines à quelques-unes? Quel code puis-je exécuter moins souvent que chaque mise à jour? Dois-je simplement le multithread jusqu'à ce qu'il soit assez rapide pour permettre un monde de taille décente? Dois-je essayer de décharger les calculs de vitesse sur le GPU? Si oui, comment pourrais-je concevoir cela? Puis-je conserver des données statiques et partagées sur le GPU? Puis-je créer des fonctions HLSL sur le GPU et les appeler arbitrairement (en utilisant XNA) ou doivent-elles faire partie du processus de dessin?
G * m1 * m2 / r^2
, où G est juste pour modifier le comportement. (bien que je ne puisse pas simplement les faire suivre un chemin, car l'utilisateur peut perturber le système)