After building last weekโs Solar System renderer, I was excited to move on to the fun stuff and see some planets moving around. I rushed on into implementing motion, which on the face of it is pretty simple. If Earth is moving along at 30,000m/s, for example, then you can just add its velocity to its current position once per second and get its new location.
The code for this is an easy read. I threw together a little Motion class. So if Earth has a vector of 30000 horizontally and 0 vertically, then each tick of this method counts as one second of motion in my imaginary 2D universe, and it moves the earth along by 30km each time.
class Motion {
affect (universe) {
universe.planets.map((planet) => {
planet.position.add(planet.vector);
});
}
}
This worked on my first try, which as usual, meant Iโd barely broken the surface of the problem. To give you a sense of perspective, hereโs ten seconds of motion in plain old 1:1 time. Itโs not exactly riveting. Earthโs 12,000km diameter makes 30km/s look slow. Honestly, I kind of thought itโd be a bit quicker, even at 1:1 scale. Remember this next time somebodyโs talking about how weโre โhurtling through space on a giant rockโ. It barely moves at all in this clip.
No way Iโm waiting an entire real-life year to see my simulated Earth make its way around the Sun once. What I needed was a way to speed up time.
Something I quickly realised was that this was more or less the same problem as the viewport scaling, just dressed up a little differently. The symmetry carries over into the solution, too, because just like in the graphical layer, I ended up creating a scale property to represent the conversion rate. This time, it was for converting between real life seconds and imaginary space seconds. So a scale of 1 gives you one second per real second, and a scale of 60 gives you a minute per real second, and so on.
The timing code is too involved (๐ค) to show a decent summarised version here. The gist of it is that for each tick, it multiplies the real life tick interval length by the scale factor, and passes that to any physics calculations so that they can multiply their results by that. For motion, that just means multiplying the planetโs vector by the number of seconds elapsed before adding it to the planetโs position.
class Motion {
affect (universe, seconds) {
universe.planets.map((planet) => {
planet.position.add(
planet.vector.scale(seconds)
);
});
}
}
Now hereโs the Earth again, this time with scale pumped up to 600. Thatโs 10 imaginary space minutes for every real world second. If youโre good at maths you might notice that this video is 18 seconds long, and the timer doesnโt make it to 3 hours like it should at this scale. But then if you were paying that kind of attention youโd have noticed that only 10 seconds pass in the 12 second video earlier! Still got some minor issues to iron out there ๐
This is going great so far. It isnโt perfect, but Iโm reasonably pleased with it. Getting the foundational stuff like graphics and timing right makes it so much more enjoyable to build the complicated stuff on top.