Start new game. Exit game. Resume game.
Expected result: Game resumes where player left off.
Actual result: Camera appears stuck inside sun OR game crashes
After Rare Earth 0.1 was released, it wasn’t long before the first bug reports started rolling in. This issue bubbled to the top quickly. However, I was just about to leave on a trip to Europe and only had time for one quick update to the game, so I instead addressed the bug that was getting the most complaints and added instructions.
Maybe being immersed in foreign cultures gave me the idea, but as soon as I got back, the first thing I tried was to change the region format on my phone. Sure enough, I could suddenly repro the bug. Why should the region format matter? Math is the same in any language, and all the game UI treats numbers as invariant culture, specifically to avoid issues like this.
Ah, but I overlooked another point of vulnerability – the game save. Each object in the game is serialized as a collection of XML attributes: mass, radius, etc. For example, a sphere looks like this:
var element = new XElement("Sphere"); element.Add(new XAttribute("Radius", body.Radius)); element.Add(Serialize((Body)body));
…where Body includes universal physics properties, like position and mass. Loading is a bit more nuanced, but ultimately ends up with something like this:
var radius = float.Parse(xAttribute.Value);
This would work fine, if creating an XML attribute and parsing a float defaulted to the same culture format (either both current or both invariant). But if they did, this post wouldn’t exist. My saved attributes always had floats formatted the U.S. English way (e.g. 0.45), but float.Parse apparently defaults to the current culture. In a region format like Italian, 0.45 reads like 0,45 would in English – 45 with some junk in front. So a star that was 0.9 units wide at save time is 90 units wide at load time, which is bigger than the entire game world, which means you’re stuck inside the sun forever. Best case scenario. Worst case, some numbers can’t even be parsed, the load operation fails, and now the game crashes every time. Fortunately, there is a pretty simple fix:
I could alternately serialize my value types to the current culture by hand, but that could have other consequences, so I stuck with the simple fix.
Ultimately, it’s a pretty embarrassing mistake, but I learned a valuable lesson about testing my game for an international audience.