Ich mache ein Spiel vom Typ Weltraumforschung, es wird viele Planeten und andere Objekte haben, die alle eine realistische Schwerkraft haben werden. Ich habe derzeit ein System, das funktioniert, aber wenn die Anzahl der Planeten über 70 steigt, verringert sich die FPS um praktisch exponentielle Raten. Ich mache es in C # und XNA.
Ich vermute, dass ich in der Lage sein sollte, Schwerkraftberechnungen zwischen 100 Objekten ohne diese Art von Belastung durchzuführen, daher ist meine Methode eindeutig nicht so effizient, wie sie sein sollte.
Ich habe zwei Dateien, Gravity.cs und EntityEngine.cs. Gravity verwaltet NUR die Gravitationsberechnungen. EntityEngine erstellt eine Instanz von Gravity und führt sie zusammen mit anderen entitätsbezogenen Methoden aus.
EntityEngine.cs
public void Update()
{
foreach (KeyValuePair<string, Entity> e in Entities)
{
e.Value.Update();
}
gravity.Update();
}
(Nur relevanter Code von EntityEngine, selbsterklärend. Wenn eine Instanz der Schwerkraft in entityEngine erstellt wird, übergibt sie sich selbst (diese), damit die Schwerkraft auf entityEngine.Entities (ein Wörterbuch aller Planetenobjekte) zugreifen kann.)
Gravity.cs
namespace ExplorationEngine
{
public class Gravity
{
private EntityEngine entityEngine;
private Vector2 Force;
private Vector2 VecForce;
private float distance;
private float mult;
public Gravity(EntityEngine e)
{
entityEngine = e;
}
public void Update()
{
//First loop
foreach (KeyValuePair<string, Entity> e in entityEngine.Entities)
{
//Reset the force vector
Force = new Vector2();
//Second loop
foreach (KeyValuePair<string, Entity> e2 in entityEngine.Entities)
{
//Make sure the second value is not the current value from the first loop
if (e2.Value != e.Value )
{
//Find the distance between the two objects. Because Fg = G * ((M1 * M2) / r^2), using Vector2.Distance() and then squaring it
//is pointless and inefficient because distance uses a sqrt, squaring the result simple cancels that sqrt.
distance = Vector2.DistanceSquared(e2.Value.Position, e.Value.Position);
//This makes sure that two planets do not attract eachother if they are touching, completely unnecessary when I add collision,
//For now it just makes it so that the planets are not glitchy, performance is not significantly improved by removing this IF
if (Math.Sqrt(distance) > (e.Value.Texture.Width / 2 + e2.Value.Texture.Width / 2))
{
//Calculate the magnitude of Fg (I'm using my own gravitational constant (G) for the sake of time (I know it's 1 at the moment, but I've been changing it)
mult = 1.0f * ((e.Value.Mass * e2.Value.Mass) / distance);
//Calculate the direction of the force, simply subtracting the positions and normalizing works, this fixes diagonal vectors
//from having a larger value, and basically makes VecForce a direction.
VecForce = e2.Value.Position - e.Value.Position;
VecForce.Normalize();
//Add the vector for each planet in the second loop to a force var.
Force = Vector2.Add(Force, VecForce * mult);
//I have tried Force += VecForce * mult, and have not noticed much of an increase in speed.
}
}
}
//Add that force to the first loop's planet's position (later on I'll instead add to acceleration, to account for inertia)
e.Value.Position += Force;
}
}
}
}
Ich habe verschiedene Tipps (zur Schwerkraftoptimierung, nicht zum Einfädeln) aus DIESER Frage (die ich gestern gemacht habe) verwendet. Ich habe diese Schwerkraftmethode (Gravity.Update) so effizient gemacht, wie ich es machen kann. Dieser O (N ^ 2) -Algorithmus scheint jedoch immer noch meine gesamte CPU-Leistung zu verbrauchen.
Hier ist ein Link (Google Drive, gehe zu Datei> Herunterladen, behalte .Exe mit dem Inhaltsordner, du brauchst XNA Framework 4.0 Redist. Wenn du es noch nicht hast) zur aktuellen Version meines Spiels. Ein Linksklick macht einen Planeten, ein Rechtsklick entfernt den letzten Planeten. Die Maus bewegt die Kamera, das Scrollrad zoomt hinein und heraus. Sehen Sie sich FPS und Planet Count an, um zu sehen, was ich mit Leistungsproblemen nach 70 Planeten meine. (ALLE 70 Planeten müssen sich bewegen, ich hatte 100 stationäre Planeten und nur 5 oder so bewegte Planeten, während ich noch 300 fps habe. Das Problem tritt auf, wenn sich 70+ bewegen.)
Nach 70 Planeten werden Leistungstanks exponentiell hergestellt. Mit <70 Planeten bekomme ich 330 fps (ich habe es auf 300 begrenzt). Bei 90 Planeten beträgt die FPS ungefähr 2, mehr als das, und sie bleibt bei 0 FPS. Seltsamerweise steigt die FPS, wenn alle Planeten stationär sind, wieder auf etwa 300 an, aber sobald sich etwas bewegt, geht es wieder auf das zurück, was es war. Ich habe keine Systeme, um dies zu ermöglichen.
Ich habe über Multithreading nachgedacht, aber diese vorherige Frage hat mir ein oder zwei Dinge beigebracht, und ich sehe jetzt, dass dies keine praktikable Option ist.
Ich habe auch gedacht, ich könnte stattdessen die Berechnungen auf meiner GPU durchführen, obwohl ich nicht denke, dass dies notwendig sein sollte. Ich weiß auch nicht, wie ich das machen soll, es ist kein einfaches Konzept und ich möchte es vermeiden, es sei denn, jemand kennt eine wirklich noobfreundliche einfache Methode, die für eine n-Körper-Schwerkraftberechnung funktioniert. (Ich habe eine NVidia GTX 660)
Zuletzt habe ich überlegt, ein Quadtree-System zu verwenden. (Barnes Hut-Simulation) Mir wurde (in der vorherigen Frage) gesagt, dass dies eine gute Methode ist, die häufig verwendet wird, und sie scheint logisch und unkompliziert zu sein. Die Implementierung ist jedoch weit über meinem Kopf und ich habe keine gute gefunden Tutorial für C #, das es jedoch auf eine Weise erklärt, die ich verstehen kann, oder Code verwendet, den ich eventuell herausfinden kann.
Meine Frage lautet also: Wie kann ich meine Schwerkraftmethode effizienter gestalten, indem ich mehr als 100 Objekte verwenden kann (ich kann 1000 Planeten mit konstanten 300 FPS ohne Schwerkraftberechnungen rendern) und ob ich nicht viel tun kann, um mich zu verbessern Kann ich meine GPU für die Berechnungen verwenden (einschließlich einer Art Quadtree-System)?
List
und Dictionary
(und seine Keys
und Values
Sammlungen). Sie sind ziemlich sicher zu bedienen foreach
. ( foreach
in C # nicht verwendet IEnumerator
, verwendet es Ententypisierung, so dass Enumeratoren struct
wie erwartet sein und arbeiten können).
IDisposable