- Alpha: At this stage the project is playable as a game and gives a good indication of how the final game will play. There may be large gaps, and indeed every individual system may need significant work, but you should be able to test the entire flow of the game at this stage and have a viable experience despite the gaps.
- Beta: All the gaps are filled in now. Improvements are still possible or needed throughout the game, and some chunks of content may still need finishing, but all the systems should be present and approaching complete. Testing at this stage can hopefully focus on finding out what parts of the game need more improvements than you thought.
At the end of last month’s update I talked about being very close to being able to go from editor to gameplay smoothly. That’s working now, which has led to some fun iteration. Before I’d done months of testing and development on a single level, and now I can whip a new one up in minutes. There’s a lot of bumps and bugs in it that I still have to work out, though. Working so long with a hard-coded level in a fixed unity scene led to code that is not structured well for loading and especially reloading.
BETTER LINE SMOOTHING
One change a long time coming that happened this month is line drawing. Previously, an object’s trajectory was drawn by simply getting a start and end time(ideally equal to one orbit in the simplest cases), then picking N points of time evenly spaced through it and getting the object’s position at those times. The problem is that an object only spends equal times at different parts of an orbit if it’s orbit is perfectly circular, if it’s highly elliptical you could run into cases where one end becomes visibly jagged even with high numbers of overall points.
An example of the old system breaking down.
The new system starts out making a set of points the same way as before, then refines the line further. At each initial point it takes the object’s velocity at that moment and extrapolates out an amount of time matching the time of the next point in the set. If the extrapolated and actual points are far enough apart it asks the simulation for another point at the time-midpoint halfway between the existing ones. I tried a few other smoothing methods, but right now this seems like the most reliable way to handle the particularly curly paths that can arise if you’re looking at the system relative to a moon.
This month I did get to put the game in front of a couple friends and watched them come to grips with it. The state it’s in is rough enough that I wouldn’t want to throw it out to anyone that I can’t be there to guide, and that plus the fact that it was being tested by very good friends does mean that the opinions I got are far from perfectly objective, but I still learned a lot! I’ll list a few of the things I learned.
- More parts of the racing interface are currently broken than I realized
- People want the information those parts would provide without much prompting, so I really need to fix them!
- The better line smoothing is still imperfect and needs more refinement.
- If you don’t stop them, people will break the speed of light.
- People really want to orbit the moon even if you don’t ask them to.
- Orbiting the moon requires you to be a LOT closer than people expect.
It definitely gave me some useful information and ideas on what I should focus on next.
Something I’ve finally gotten around to testing this month is Lagrange points. If you’re unfamiliar, I’ll provide a quick overview of the idea. If you have one object of notable mass, like a planet, orbiting a larger object, like the sun, then in most cases a third object added to the system will not be able to keep a constant distance to both of them. It’s distance to either one or the other, if not both, will be constantly changing. However, at least if the orbit of the planet is relatively close to circular, there are five points where the forces can balance out such that an object can be placed there and keep a constant distance to both bodies. These are the Lagrange points. The first three are on a line with the two main objects, L1 between the two, L2 just outside the lesser object, and L3 on the far side of the larger object from the lesser. L4 and 5 sit on the same orbit as the lesser object, 60 degrees ahead and behind it.
Lagrange points are of some note because they’re commonly thought to be useful to a space-faring civilization, and they’re a feature that does not arise in some popular approximations such as the patched conics used by Kerbal Space Program. They should be something that naturally comes up in the simulation, and it hasn’t been urgent that I verify their presence, but with the editor working it’s become pretty trivial to check L3, 4, and 5, so I did.
In this example I made a Jupiter mass object at 1 AU in a circular orbit of a 1 solar mass object and then threw down a bunch of zero mass objects in the same orbit but offset a number of degrees, and named them according to the amount of offset. The A60 point thus corresponds with the L4 point and can be expected to be stable. The other objects don’t match any Lagrange points, so they shouldn’t be stable.
Then I wound the simulation forward 9 years. It might be a bit hard to see in the jumble, but relative to J the A60 is still in its place, while all the other ones have wandered around significantly. That’s what they’re supposed to do, so huge success!
Those are the high points for the last month. For the coming month, I need to focus on polish, and on organizing my internal documentation. The next post consequently might be a bit thin, but we’ll see! There tends to be more to talk about in these than I expect, so maybe I’ll be surprised.
I was feeling down on my most recent progress until I pulled back up News#5 realized just how much I’ve really done in the last month. Last time, I’d just got saving and loading working. A few things have happened since then.
I’ve gotten most of the way to making objects sharing a mutual center of mass, like binary stars or binary planets, work in the editor. There are a few hiccups in saving out final state vectors for simulation, but the path to solving them seems a clear one to me. I’m not going to go down it until some other things are cleared out, because binary systems aren’t a requirement to hit the next milestone I’m aiming for, but it’s definitely on the to do list later.
Here’s a snapshot of a binary system in the editor. It may not be immediately evident at a glance, but the central ‘Nirgal System’ marker does not represent a physical object, but rather the shared center of mass of the stars Anlil and Naanlil. A different icon for this kind of object might be a good idea. The outline of a dot perhaps, rather than a filled one? The main object dot is probably going to get replaced at some point too, so that can wait till then.
An interesting wrinkle that I didn’t realize would crop up before really getting into the math here is that if you’re designing a binary system, the mass and separation are interconnected such that you can only set one of them independently. What I mean by that is that you can have each star be of a specific mass, then set their total separation from each other, or you can set their individual orbital radii and set the total mass of the system. Then the editor will have to distribute appropriate portions of the set total value based on the independent values. At the moment I’ve only implemented one of these two options, the one with setting the masses of the objects independently.
Getting system loading by the simulation working led surprisingly easily into a very rewarding other feature, one button testing. The editor now has a button that will immediately fire off a simulation of the currently loaded system. Seen below, a quick snippet of simulation of the famous[link]Trappist-1 star system
I’ve not yet got it to where I can immediately jump into a test of race gameplay from the editor, but I’m closing in on that, and it’s my next major target.
The current thing on my plate is getting mission goals from the editor into the simulation. While working on that I decided it’d likely help people if the objectives were drawn visibly. Even with all my playing with my test scene I’ve never been entirely certain how close I need to get, I picked the distance for the objectives relatively at random and simply know from experience that it’s not that hard to hit, but neither is it that easy.
So I did that. Here’s an Io intercept, both from normal reference and from Io reference. No other objectives are completed, so you can see the difference in how they’re drawn.
In the future there may be a few different kinds of objectives. So far four major ones have come to mind.
- Get within distance X at least once
- Get beyond distance X at least once
- Never get within distance X
- Never get beyond distance X
Maybe there are more, but those are the obvious ones so far. None of them should be that hard to represent in code, either.
That’s most of the month’s progress. Maybe next time I’ll have hit that milestone of going from editor to full playable level. Getting very close!
Last month I said I’d be working towards having multiple levels. That’s led me down a bit of a rabbit hole. To have multiple levels I need a better level file format. That format is going to need to contain various kinds of objects, whereas the existing format only knows how to handle one type of thing. That leads to the fact that the level editor itself needs to be more flexible, and while I’m at it there’s real star system arrangements that the editor can’t support that I want it to. Also it’d make setting up systems based on real data much easier if I could enter in values like distance or mass in various units instead of a single option, so if I’m hacking on the editor I might as well try to do that at the same time.
Some might call this scope creep, and they might be right, but this is all stuff I need to do eventually and they go well together.
Working back up from the bottom of the rabbit hole to the top, the new editor has option selections for each appropriate line to let you choose what unit you want to view/edit them in.
The option list is flexible, I could add or remove some without much effort(though it’d cause some layout headaches, those are solvable). A few familiar units like miles or tonnes might be useful to put in.
I streamlined text entry while I was at it. In the old system you’d put in values, then have to hit a button before they were pushed into the actual data and you could see the results. Now they update as you type. There are surprisingly many moving parts involved in that! The old system does have some small benefits in being able to back out of bad ideas without commiting them, but I’ve some ideas on how I could potentially implement undo, and I think the faster feedback will be a worthwhile trade-off.
The other big change from the existing system editor interface is less visible, which is that the values to edit are not hard coded into the GUI, instead the object being edited tells the GUI manager what values it has to be edited and what kind of values they are, meaning that as I add new object types they can slot in there with minimal extra work and hopefully no special cases. This is a big win for me that’ll hopefully save some time going forward, and bits of it my be re-usable elsewhere.
Back up the rabbit hole another step is saving. Up until now all my saving and loading has been through creating or parsing comma separated value files, essentially a spreadsheet of values with a line for each object and an expected format. Strictly speaking I could extend this to plenty of new objects, but doing so is labor intensive and failure prone, and the resulting files are somewhat opaque when I’d like them to be at least theoretically hand editable. That brings me to serialization, which is pretty much the automated process for doing just that. C#, which is the language all this is being done in, has some standard serialization features. This is a thing I’d tried to make use of long ago, but I was too novice at certain aspects of programming to make it work. This time around it went relatively smoothly, I did have to more or less turn a portion of my data structure inside out to make it work, but that only took about an hour and seems like it’ll have some good side effects down the road. I can now save and load the editor files to an XML format with minimal amounts of special work to prepare for saving or cleanup after loading. Similar to the GUI stuff this happens without me needing to specifically write (much) code to handle new objects types, which will save time in the future. XML is less than ideal for my goals of human readability/editing, so I may look into alternate options, but it does do the job.
I’m still a few steps away from having a full featured level editor. Most pressingly the new logic can not yet convert from the simplified orbital representation in the editor to the actual raw positions and velocities needed for the levels. That shouldn’t be too hard to cannibalize from the old logic, but it hasn’t been done yet. Editing mission objectives promises to be a bit tricky, and definitely needed. Editing mission meta-data(name, description, and so forth) is similarly needed but probably easier. Also on the immediate to do list is support for binary stars or planets, which now that I’ve done everything else I’ve talked about here should be pretty simple. There are a few other loose ends in the editor GUI, and if I’m really smart I’ll build an automated way to convert the old CSV levels I’ve made to the new format.
That’s all for this month! Plenty of words this time, next time I’ll strive to have more pretty pictures too.
It’s time for another installment of Atomic Space News! Since I only just thought of that heading for these posts, but will be editing it retroactively into the titles of the existing posts, this is both the first and fourth installment, and since I’m a programmer, it’s post #3.
Last time I talked about some things that I was doing or wanted to do that I’ve since done! Invigorating progress.
First off, the change in how headings are displayed is done. Inputs now mimic a compass, though you’re free to do things like enter in -090 degrees instead of 270. Forcing everything to be displayed in the 0-360 range is on my to-do list, but not a high priority item despite the fact that it should be dead easy to do.
Secondly, I’ve added a second smaller marker to every burn to show when the burn ends. Last post I included a shot of an Io encounter. Here’s what it looks like now! All images are clickable links to full size screenshots.
In addition to that I’ve implemented relative display, meaning you can now view the simulation as if a particular object were the center of the universe. This can lead to quite interesting patterns, and is also helpful at in refining a near encounter into a very close encounter. Here’s that same course shown from the perspective of Ganymede. This view is not especially useful, but it is interesting to look at! Note the circular path of Jupiter, mirroring Ganymede’s own orbit around it, just from a different frame of reference.
In fact using the Io relative display I was able to refine this course down from a three burn, 24.5 km/s delta-V course that takes 22 days, I refined it to a two burn, 17 km/s delta-V course that takes just shy of 5 days.
And again from Io’s perspective, which makes it much easier to see that they definitely encounter each other.
In fact this encounter is so close that in real life your spacecraft would be directly encountering not only Io’s atmosphere, but also it’s surface. Not bad for about a minute’s refinement of the course.
Right now my next goal is more PR related than code, but code is happening as a direct result of it. I’d like to make a narrated video of assembling a winning course with the current tools, like the video linked on the front page of this site but with explanations of what’s going on. Without those it’s quite opaque to people that don’t already know what the game is about, and can get rather dry. I’ve taken a few stabs at this already, but each time I do I find some aspect of the interface is slowing me down and drawing the run time out unacceptably long. Some editing down is possible, but I want to keep it to a minimum in large part due to my lack of familiarity with video editing software.
That’s where the coding comes in, as each time I do this I come up with ideas on how to modify the GUI to speed up the process, and then get to work on implementing those. So next time I should have either a lot of small GUI refinements to talk about, or have a nifty video to share. Until then!
The past month of development has been consumed by refamiliarizing myself with a somewhat stale codebase, trying to refresh it a bit without breaking anything, and doing some expansion of the interface for playability. Today lets talk about the technical side. This is going to get a bit down into the nitty gritty of how the engine works, but what else is a dev diary for?
In order to let you plan out your future course, the game stores the current *and future* positions of all objects for the duration of the gameplay period. The game takes the initial state of the system as defined in the level file, does some math to approximate the proper positions of all the objects a certain distance in the future, and saves both the initial and ending positions, velocities, and accelerations. And then does it again using the new end points as the new end states.
Now, exact equations for the motion due to gravity of more than two objects doesn’t exist, save for some limited cases for three objects. As a result what I’m using is an approximation. A pretty crude one in fact, as I am neither a physicist nor a mathematician. The upshot is that there is unavoidably a degree of error in the calculations. You can improve accuracy by making sure the change between each state is as small as possible, which means less time between each step. There is thus a natural tradeoff between the speed and accuracy of the simulation.
For these reasons the simulation has an adaptive timestep. Objects in more extreme conditions, such as Jupiter’s moon Io, require more accuracy or they’ll be shot off into space quickly. Io in fact requires a simulation step a bit faster than once every ten seconds to keep acceptably accurate. Other objects, like Jupiter itself, might demand a step every few weeks or months. Because storing states for a long play duration has noticeable memory considerations the entire simulation doesn’t run at the rate of the most demanding object in it, instead each object is updated on it’s own clock as needed. This results in thousands of Io states being stored for every Jupiter state that is calculated.
What’s changed this month is how the required timestep for each object is determined. Before, each object would remember what the last step size it used was and start with that, then if the amount its velocity changed in a step was too big or small it would discard that step, adjust the step size, and try again.
This worked well until player-planned maneuvers came into the picture. When a player inserts or removes an order from a ship’s timeline the ship has to discard all it’s states following that order and recalculate them. If these orders are high thrust they can also affect the required size of the timestep for that object. On top of that, objects do not store what the timestep they were attempting to use was at a given past state, they only know what the last timestep they calculated was. That bit of ill-advised state combined with loose bounds on what defined an acceptable step meant that if you added an order to a ship and then removed it, the states following the time of the order after it’s removal might not exactly match the states that existed before it was placed at all. This has the potential for unintended and hard to resolve inconsistencies, and could have become a real problem.
Now instead of having the old recursive search for an acceptable timestep with all it’s extra state I have a targeted change in velocity per step, and the timestep duration is determined exactly based on that. The presence of orders can necessarily impact the step size. If there are orders for an object, it will first check and see if there are any within it’s desired timestep after the initial calculation. If not, it proceeds as normal. If there are, and the desired step size is larger than the minimum step that is allowed, it’ll cut the step short so that the next step starts at the exact time of the next order. If the order is inside the window defined by the minimum step size then the acceleration from the burn is added to the acceleration from gravity to determine the desired timestep size. If the burn is longer than the desired step size it will be divided up into multiple segments so and the process repeats until the burn is complete. When an order is removed from the list that object’s states past the step preceding the order are removed from it and everything is recalculated as if it was never there, avoiding any inexplicable footprint on the simulation.
Thanks for reading. Next time will likely go into the current state of the GUI and how the race prototype currently plays, and where it’s going from there. For now, I’ll leave you with a screenshot of an Io encounter being arranged. Click to see the full size version.