Ancient Star

Development log


Project maintained by iktools Hosted on GitHub Pages — Theme by mattgraham
1 July 2025

Loading circles

by Ivan Kravarščan

Load game screen

Game selection / loading screen has been done for a month or so. It looks almost identical to the original except on the desktop there is enough room for both word and icon on buttons. I made icons myself because I’m unsure what terms and conditions are for clip art used in the previous version.

Load game screen

They were stock images from Android Studio where terms were not listed. Looking at other clip art on the internet I figured it would be easier to draw three circles and a few lines myself than to manage attribution (keep a private list of what came from where just in case, see if and how the game has to list attributions and in what format). Last time I learned a few tricks in Inkscape to make my canvas the right size for drawing and exporting at expected resolution so it was a 15 min job for both share and delete icons.

It’s nice to have icons in SVG (vector) format in case I decide to render them with SDF but for now the game is using exported raster format. And it shows as they are slightly darker then they should be. They are supposed to be purely white, not light gray. That’s an artifact of image shrinking where an average of opaque white and transparent pixels ends up being semitransparent gray instead of semitransparent white.

Functionality of the screen is also recreated except the share button. At the moment it does nothing. I’ll make Android specific functions later and I’ll have to figure out what that button should do on the desktop.

Serialization rewrite

The reason why it took this long to properly finish the loading screen is that a supposedly quick rewrite blew up into a full side project. In simple terms serialization handles converting running game data to save files and back. Normally you’d either have to shape game data as a database (which is not natural, as strange as it sounds), or manually write conversion logic for every single tiny bit of data. I took the option #2 and the first time around it did cost me about a month of work and 3 months of nerves. I hated every other second of it. I did it with a code generator, where you put an annotation above relevant data containers and it generates serialization logic every time the code changes. You’d think I’d actually write those lines of code? No no no, that’s a much worse option. I tried that a decade ago in the Stareater and while it was easier at first, it gave a false sense of security. Unless you remember to rigorously test everything every time you might miss that you changed the data model without updating serialization logic. At first everything would work fine until a bizarre bug torpedoes the entire save and sends you on a 20 years long Odyssey to find the root cause. With a generated serialization code you just have to test the newly developed code like you normally do.

The tool for writing the code generator was KAPT (Kotlin annotation processing tool) which is based on Java’s APT. APT is rather archaic and rather poorly documented. It operates on a notch lower abstraction level than you’d expect for source code generation and it’s either super hard to google or too rarely used to have nice examples and proper explanation. KAPT as a wrapper over APT that additionally suffers from poorer compiler integration and translation issues between Java and Kotlin. When I was done with the first serialization code generator I tried to avoid touching it as much as possible. Unfortunately there were some funny things in the game code about how Ancient Guardians and Replicator Probes “players” are stored so every now and then I had to make a patch. The code grew more and more unwieldy, giving me ideas how to improve it. But in the meantime KAPT got deprecated in favour of KSP (Kotlin symbol processor).

While I was working on the loading screen I noticed how the KAPT phase was the slowest part of compilation time. It would probably be instant without or even skipped with an incremental compilation trick. So I did a test project to see how KSP works, and how hard it is to learn. It started out as KAPT, weak documentation, scarce examples but ChatGPT was surprisingly good at covering that part so I was satisfied enough to do the rewrite. Benefit of hindsight helped at laying down much better architecture and test cases from the previous version could be copied almost verbatim, giving me a decent roadmap on what has to be supported. And frankly, the reason why the rewrite took a whole month was that I didn’t have enough tests. Whenever I tried to integrate it with the existing game code it’d discover a new class of use cases that were not covered.

I’m probably going to be the sole user of this code generator but I did make it a separate project that is published on Maven Central. It’s easier to manage it that way and maybe I can win an opportunity with those bragging rights.

Map screen

This is a long post already and I’ve been working on the game in the meantime so I have a few more updates. I’ve started to seriously work on the main part of the game: galaxy map screen. Previous version of the game had so many little features on the galaxy screen I had to go through the code and list them. It’s amazing how the smallest things make the greatest digressions.

Load game screen

First thing on the chopping block was the outline of the sensor range. Drawing thin lines is not something OpenGL can do out of the box. I tried all sorts of things with a normal star image, that is just a circle, but its resolution is not high enough to make a large and thin outline, let alone smooth looking one. So I tried with a custom shader that colors pixels in a band a certain distance away from the center of the square. LibGDX (the framework used in the game rewrite) makes it really convenient to draw sprites, rectangles holding an image (texture) from either a file or cut out of an atlas (one image tile with multiple smaller images). In a shader you do get information about which coordinate of the image you are working with for each particular pixel and you can use that information to figure out where you are on the rectangle. But if you don’t actually use the image color for the final pixel color then the GPU driver can optimize all the shader code that is reading the image. That would not be a problem if it would leave uniform variables alone. Uniforms are sort of levers a shader exposes and standard LibGDX rendering pipeline expects certain one to find. Specialized shader for drawing a circle just needs coordinates, not texture colors so that’s how I learned about the existence of this particular rabbit hole. I did find a few workarounds but each had some drawbacks I didn’t particularly like. So I raised the question on the LibGDX Discord server and the discussion was interesting. I actually got some to answer but the person was not too keen on helping me. They gave a few generic responses and defended how the framework code works but eventually they did accidentally slip a gold nugget. They proposed a workaround I did not think of, is not too much work, and doesn’t have glaring drawbacks. If you are just a customer you’d be very dissatisfied with the service and give up at the start but I’ve been on both sides, gave them a benefit of a doubt and it was worth it. Believe me, this is the best outcome one could hope for!

Second thing on the chopping block was selecting objects on the map. It sounds like a simple thing, just find a thing under the mouse and open it. Except there can be multiple objects at the same location, on the phone you have to account for “fat fingers”, and there is a non-trivial state machine for what exactly happens considering what you had selected before and clicked on now. All in all nothing that hard, just a lot of shoveling. For now everything except selecting a star most of will be under //TODO.

tags: