Ich habe eine Reihe von Objekten unterschiedlicher Größe und Geschwindigkeit, die sich gegenseitig anziehen. Bei jedem Update muss ich jedes Objekt überprüfen und die Kräfte aufgrund der Schwerkraft jedes anderen Objekts addieren. Es lässt sich nicht sehr gut skalieren, ist einer der beiden großen Engpässe, die ich in meinem Spiel festgestellt habe, und ich bin nicht sicher, was ich tun soll, um die Leistung zu verbessern.
Es fühlt sich an, als könnte ich die Leistung verbessern. Zu jedem Zeitpunkt haben wahrscheinlich 99% der Objekte im System nur einen vernachlässigbaren Einfluss auf ein Objekt. Ich kann die Objekte natürlich nicht nach Masse sortieren und berücksichtige nur die 10 größten Objekte oder ähnliches, da die Kraft mit der Entfernung mehr variiert als mit der Masse (die Gleichung ist in der Richtung von force = mass1 * mass2 / distance^2
). Ich denke, eine gute Annäherung wäre, die größten Objekte und die nächsten Objekte zu betrachten und dabei die Hunderte winziger Gesteinsbrocken auf der anderen Seite der Welt zu ignorieren, die möglicherweise nichts bewirken können - aber um herauszufinden, welche Objekte es sind Am nächsten muss ich alle Objekte durchlaufen , und ihre Positionen ändern sich ständigEs ist also nicht so, dass ich es nur einmal machen kann.
Momentan mache ich so etwas:
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);
}
(Beachten Sie, dass ich viel Code entfernt habe - zum Beispiel bei den Kollisionstests. Ich durchlaufe die Objekte nicht ein zweites Mal, um Kollisionen zu erkennen.)
Ich iteriere also nicht immer über die gesamte Liste - ich mache das nur für das erste Objekt, und jedes Mal, wenn das Objekt die Kraft findet, die es auf ein anderes Objekt ausübt, fühlt dieses andere Objekt die gleiche Kraft, so dass es nur beide aktualisiert sie - und dann muss das erste Objekt für den Rest des Updates nicht erneut berücksichtigt werden.
Für die Funktionen Gravity.ForceUnderGravity(...)
und Motion.Velocity(...)
usw. wird nur ein Teil der in der Vektormathematik integrierten XNA verwendet.
Wenn zwei Objekte kollidieren, entstehen massenlose Trümmer. Es wird in einer separaten Liste aufbewahrt und die massiven Objekte durchlaufen die Trümmer nicht als Teil ihrer Geschwindigkeitsberechnung, sondern jedes Trümmerstück muss die massiven Partikel durchlaufen.
Dies muss nicht an unglaubliche Grenzen stoßen. Die Welt ist nicht unbegrenzt, sie enthält eine Grenze, die Objekte zerstört, die sie überqueren. Ich möchte in der Lage sein, mit etwa tausend Objekten umzugehen. Derzeit fängt das Spiel an, an 200 zu ersticken.
Irgendwelche Gedanken, wie ich das verbessern könnte? Eine Heuristik, mit der ich die Schleifenlänge von Hunderten auf wenige reduzieren kann? Code, den ich seltener als jedes Update ausführen kann? Sollte ich es einfach multithreaden, bis es schnell genug ist, um eine Welt von annehmbarer Größe zu ermöglichen? Sollte ich versuchen, die Geschwindigkeitsberechnungen auf die GPU zu übertragen? Wenn ja, wie würde ich das errichten? Kann ich statische, gemeinsam genutzte Daten auf der GPU behalten? Kann ich HLSL-Funktionen auf der GPU erstellen und sie willkürlich aufrufen (mit XNA) oder müssen sie Teil des Zeichenprozesses sein?
G * m1 * m2 / r^2
, wo G nur das Verhalten optimieren soll. (obwohl ich sie nicht einfach einem Pfad folgen lassen kann, weil der Benutzer das System stören kann)