I optimized away all object creation in my code. I then did a small performance test on Semi-explicit Euler, Verlet and RK4. Results:
100.000 objects, 1 attractor
Semi-implicit Euler: 4.5 ms
Verlet: 4.6 ms
RK4: 10.2 ms
100.000 objects, 10 attractors
Semi-implicit Euler: 17.0 ms
Verlet: 17.0 ms
RK4: 68.5 ms
Verlet is insignificantly slower than Semi-implicit Euler, but the precision difference is probably less than significant too. xD When the number of attractors increases RK4 falls behind. The actual operations for each integrator is not very costly, even for RK4. The bottleneck seems to be my gravitational force calculations. RK4 gets really slow because it does 4 of these each step.
This would’ve been fine if RK4 had 4 times the precision of Verlet, but unfortunately RK4’s precision gets better with smaller time steps. For small time steps it is awesome, almost perfect, but for larger time steps it is equally or even more inaccurate than Verlet.
They also behave very differently. If your time step is too large and you have an elliptical orbit, the whole orbit will spin around the attractor, making a cute flower pattern. RK4 however just loses energy eventually crashing into the attractor and then exploding. It seems like that would be an important point when deciding on what integrator to use for a game. A space game could use RK4 and justify the energy loss with atmospheric friction (the reason satellites eventually enter the atmosphere), while a puzzle game or a particle engine using magnets or “black holes” or something would be better of with Verlet, as it performs better and the spinning might not be noticeable if objects do not orbit. Space games also usually have a lot larger time steps, you probably don’t want to simulate an entire orbital period in 1/60 second time steps. xD
Precision loss occurs when objects get close to attractors, so the shape of the orbit definitely plays a part in the decision. If you just have circular orbits, RK4 is waaay overkill.
Does anybody have any ideas on how to improve my gravitational acceleration function?
/*Acceleration is just an object containing the x- and y-accelerations (ax, ay). Supplied by the integrator to avoid having to create a new one each run. (Verlet and Euler keeps 1 instance, RK4 has 4.)*/
public void getAcceleration(Acceleration a, double x, double y) {
a.ax = 0;
a.ay = 0;
for(int i = 0; i < gravityBodies.size(); i++){
GravityBody p = gravityBodies.get(i);
double dx = p.x - x, dy = p.y - y;
double distSqrd = dx*dx + dy*dy;
double scale = GRAVITATIONAL_CONSTANT * p.mass / (distSqrd * Math.sqrt(distSqrd)); //Calculates the acceleration (G*M/r^2) and divides it by the distance.
a.ax += dx*scale;
a.ay += dy*scale;
}
}
And yes, GravityBody is a really bad name. xD