24 January 2020

ShipBasher Development Log 4: The Symmetrizer

Long ago I remember catching an episode of a children's cartoon, whose name I didn't remember but I was able to rediscover was "Cyberchase," in which a group of children had to stop a villain wielding a device that could give and take away symmetry, either making asymmetrical objects symmetrical or vice versa. Why is this relevant? Because I have objects I need made symmetrical, and I imagine soon you will too!
(Thanks to space.com artist Adrian Mann for the image.)
Look at all that symmetry! There appears to be four-fold symmetry in the thrust plate pylons, six-fold symmetry in the darker tanks, perhaps eight-fold (it's hard to tell for sure) symmetry in the lighter tanks, and two-fold symmetry in the front section.
Now if I were constructing a replica of this thing in ShipBasher, currently I would have to add all of those duplicated pieces individually, which is very tedious and maximizes opportunity for errors (anything from a few being slightly out of alignment to accidentally parenting them each to the next one instead of the core, making a big floppy chain). Naturally ShipBasher needs a way to automate this process.
As with many editor features, I looked to my perennial favorites Kerbal Space Program and Space Engine for reference.
Space Engine's ship editor handles symmetry in a relatively basic fashion: modules are duplicated around the ship's central axis, and edits are made in a similarly duplicated fashion. For example you could activate 6-fold symmetry and add a group of fuel tanks, then switch to two-fold symmetry and delete two of them, leaving a group of four. This is surprisingly effective despite its simplicity:
Its limitations make themselves obvious very quickly, however. For example, there isn't a way to create proper four-fold symmetry (or any degree aside from the four options specified in the menu: none, two, three, and six), and the system has no ability to apply to symmetry around something other than the ship's central axis - for example, a cluster of engines attached to a nacelle. Kerbal Space Program manages to handle this much better:
To be fully honest, this is a screenshot of me adding a symmetrical component onto a ship manually by docking it in orbit - but you can do this in the craft editor too, and it symmetrically duplicates groups of objects that themselves contain symmetrical duplicates very reliably. How do they do it?
Well I could decompile Kerbal Space Program and browse the codebase myself, or I could dig around to see if anyone else has done this or if any documentation ever got made on how it's implemented, but so far I haven't and instead have been exploring various strategies independently. I want to deeply understand what the nature of a symmetry system is and why it would need to be built one way or another, so that as I build mine I can make the best decisions possible for my needs. I started by writing up some pseudocode that I thought was fairly sound and rigging it up in Unity:
I wanted to see if I could support getting "as close as possible" to perfect symmetry and thereby allow a bit more creative freedom. I'd previously noticed that Kerbal Space Program had a bit of trouble when one attempted to add modules in one symmetry mode as children of modules in a different symmetry mode and wanted to know if I could build a system to be immune to that issue. At this point it seemed to be going very well - here I have a group of five "thrusters" (small cylinders) attached as best as will fit to a group of eleven "fuel tanks" (medium cylinders) arranged symmetrically around the core (large cylinder). The sliders adjust the numbers of fuel tanks and thrusters respectively, and the algorithm is able to space everything out as evenly as it can be spaced while maintaining alignment between parents and children. Children even get assigned to the most suitable parents out of those available so as to optimize symmetry. I was rather proud of myself.
...But what of those clusters and nacelles I mentioned earlier that Space Engine couldn't handle? So far I hadn't escaped Space Engine's limitation of only allowing symmetry around the central axis. Cue my next thought experiment:
I'm not completely clueless about how Kerbal Space Program handles symmetry. I've read through the ship files (which, happily, are human-readable text) and found that modules ("parts," in this case) contain references to other modules with which they form a symmetrical group. A drawback of this is redundant data: all parts comprising a ship are saved in the file completely, and every one contains a reference to all of the others. Not only is the file much larger and more repetitive because of this, but opportunity for error is maximized. A user modifying this file directly (perhaps trying to fix a notorious docking port bug) might mess up a reference somewhere and, well, summon the Kraken.
I'm thus exploring an alternate strategy for now, as illustrated above, which I'm dubbing "symmetry groups." Modules themselves won't contain any information about their partners in crime symmetry; rather, ships in files will now contain two types of data block, module entries and symmetry group entries. As of the creation of the above image, the plan was for symmetry groups to have two properties: Degree and Members. The degree would be an integer representing how many members would attach to each individual parent module, and the members would be an array of indices pointing to the modules in question. The number of indices listed as members would determine the overall degree of symmetry. Note how, in the first and third examples from top left, the only difference is the number of members in the array. All members would be distributed among the parent modules as evenly as possible while still obeying the degree value: a degree of 0 means to behave as in the previous image, simply spacing modules out radially; a degree of two or more means to space out groups of that number of module, even if the members array runs out after only filling one parent module; a degree of one is understood to mean two-fold symmetry, but bilaterally instead of radially. I imagine this is a bit hard to digest typed out, which is why I made the image to begin with. Hopefully it helps all of this logic make sense.
Unfortunately even this has limitations I'd rather not have in my (or my eventual users') way. As the considerations get more outlandish, they get exponentially harder to explain, but suffice it to say that this system still breaks down if modules need to have symmetry around something other than their immediate parent or grandparent. What if I want a group of nacelles, each of which has a group of thrusters, some of which have radiating fins and some of which don't, but in a symmetrical fashion? It seems like an obscure edge case, and granted, I expect the vast majority of times the symmetry system will be invoked will be for much simpler tasks, but it didn't seem quite obscure enough to ignore. I could quite easily imagine a ship with this kind of structure and thus imagine a player attempting to build one. If that happens and the symmetry system can't take it, it means a lot of tedium and a high risk of frustration and disappointment.
(Actually it just occurred to me that this is pretty close to a description of the Falcon Heavy. I wouldn't want to prevent players from replicating that!)
I'm slowly incrementing the power of my designs, but it remains uncertain whether I'll achieve a "perfect" system or have to stop at some "good enough" point, and if so where that ends up being.

17 January 2020

ShipBasher Development Log 3B: Hat Simulator Development Log 2

Earlier I waxed pedantic about a barebones character creator I'd ended up building as a sandbox for developing the editable data system for use in ShipBasher (and, the way it's working out so far, several other projects with any luck). This is a small update on the work I've done over the last few days.
https://imgur.com/EQpXZKc
Here's how it looks at present.
I'm pleased to report that, at least as far as I've verified in my own testing, the system is now capable of completing a full edit-save-load cycle. What that means is the following:

At lower left is a large text box. This acts as a surrogate for a text file (the system can handle text files or in-game text boxes interchangeably) and contains serialized data that can be typed in directly by the user or generated based on a selected object.

When a character or a hat is selected, as I detailed before, the UI fields set it as their target, depending on whether it is the right type of object: hat fields only target hats, etc. When these are changed and then either the selection is changed or the "Serialize and Display" button is pressed, their modified information is stored in an "Editable" component on the target object.

When "Serialize and Display" is pressed, once all the information is gathered in the selected object's Editable component, the component itself is serialized, with each field being converted into a string. "Normally" this constitutes a key-value pair; each Editable in the scene references a "Field Definitions" object (something new I have developed since last time) that contains a list of all of the keys and the rules for how that Editable should be serialized. A flag can be set so that instead of forming a key-value pair, the first field can simply be the value, in this case the character's name; if so, that value will override the usual behavior and be saved even if it is unchanged from the default value. This mostly helps with human readability.
If the object being saved has any children which are also Editables, they are serialized and added to the saved text. Field Definitions objects also specify which characters to use as brackets to begin and end the data block for each Editable, in this case square brackets, between which all of the hat information is saved. Note that the Field Definitions information for hats does not make use of the required first field feature; in this setup, hats do not have names, even though they can have descriptions.
When the serialized text is complete, the selected character can still be edited as normal, along with all of the other objects. Changes made in the UI will be applied to whatever is selected, independently of the contents of the text box.

When the "Load" button is pressed, however, whatever is selected will have its data superseded by whatever is specified in the text box. Any existing hat will be removed; if a hat is described in the text, a new hat will be spawned and given the appropriate properties. Behold:
Here I have selected the green character, and pressing "Load" has caused it to take on all of the serialized values, i.e. the name and the presence of a hat with "Regular Hat" as its description.

This was all very verbose, but in short this system behaves as one would expect. In short the user can edit stuff, save it as text, maybe edit the text, and then load the text and have it turn back into useful stuff. That complete cycle is what I consider the primary milestone indicating that my editable data system is actually complete and ready to be put to work for real. With any luck I can use it in ShipBasher without it needing any further modifications.
AND LOOK HERE YOU CAN PLAY WITH IT YOURSELF WOOHOO

09 January 2020

ShipBasher Development Log 2B: Dat Booty, er I mean Parsing Text, yes. No silly referential jokes here.

In Part 2 I rambled about this "lexer" thing I had contrived for reading ship files. It is able to read characters from a file, assemble them into "tokens," and pass these tokens, along with some state flags, to a second system called a "parser," which is the main topic for today.
Last time I talked a bit about finite state machines, and the parser is itself a finite state machine that reuses some of the same state flags from the lexer while adding a few variables of its own. Its routine roughly goes like this:
  • The current token should come with a boolean flag indicating whether it is a key rather than a value. If it is a key, store it as the "currentKey" and set the "currentValueIndex" to 0.
  • If the given token is not a key, it is presumably a value corresponding to the current key. This forms a key-value pair that applies to one of three known things: a module, a ship (or ship-like object such as a station or asteroid), or a level. State flags shared with the lexer will indicate which of these is the subject in question.
  • If a module is being loaded: spawn a fresh module if needed, based on the current key, which should be a name for the new module and thus indicate which module template to use; once a module exists, use the current key to determine which value to edit and then set that value on the module, for example its position in the ship. At the moment, all possible keys are hard-coded in a switch statement, but this is likely to change as my new Editable Data System gets incorporated.
  • If a ship is being loaded: as with modules, spawn a fresh ship with no modules if needed, based on the given name; once a ship exists, use the key to fill the correct value, for example its description.
  • If neither a ship not a module is being loaded, then any keys found must apply to the level itself, carrying information such as the level's author.
This is all relatively straightforward compared to the lexer's work, which makes sense since it doesn't have to deal with building strings, streaming data from a file, or interpreting what esoteric symbols in the file constitute words or state machine triggers. It does, however, involve a lot of hard-coded information, causing it to constitute an uncomfortably long file nearly 500 lines long with only a few basic keys included in it. That number isn't such a big deal now, but with a few dozen possible keys for modules and possibly a comparable amount for ships and for levels, it begs re-examination. At least for the moment it runs pretty smoothly:
https://i.imgur.com/N3TMzVX
Click the image or here for a bit more information.
As seen here (assuming the gif loads properly), a file based on the upper ship exists, it is being read by the lexer, the lexer is generating tokens and sending them to the parser, the parser is adding and adjusting modules based on the tokens and which keys and values they happen to be, and finally the lower ship attaches all of its modules together based on which module each of these modules references as its parent, resulting in the new ship becoming a faithful copy of the original. I've successfully put these mechanisms to work in my ship editor prototype, allowing me to build, save, and load a few test ships:
Building using those textured cubes from before I had any proper module models. Since there was no module properties UI at this stage, modules had no properties aside from their positions and default names.
Building using my first generation of module models, albeit still without any sophisticated texturing. Note how the selected module's name and position are represented in the right panel of the UI.
Still to be done is loading ships in some form of Play Mode or level editor so I can get them to move around and pew pew at each other. Look forward to hearing about my steps toward that goal in a future installment.

ShipBasher Development Log 2: Parsimonious Lexicography

This post is out of order because I wrote it and then wasn't entirely satisfied with what I had written, so I left it as a draft for months... Anyway, I left off Part 1 in the midst of a quandary as to what strategy to employ to accomplish my goal of a basic saving and loading system for ships and, eventually, other game files. To, amusingly, paraphrase the infamous Dennis Prager, it's a simple problem to explain but a very complicated one to solve. I needed to teach my computer to do the following:
  • Convert a ship into serialized data in the form of text
  • write the text in a file
  • read text from a file and build a ship based on it. If this all completes properly, that ship will be identical to the ship with which I started.
My choices were to use XML as I had before, use Unity's built-in "JsonUtility" class, import and use some third-party implementation of JSON or some other format, or to homebrew my own system from scratch. Naturally, being an overeducated amateur programmer, I opted for the most difficult of these solutions. Every seasoned programming professional and h4x0r knows that the proper thing to do here is nab something from GitHub or at the very least consult StackOverflow, but nooo, I'm an "overachiever," so without further ado here's my story.
I did a bunch of reading online about what was involved in the science of saving and loading, during which I learned of erudite terms such as "lexer," "parser," "lexerless parser," etc. At first the complexity scared me off and I dabbled in wrangling Unity's JsonUtility, but shortly thereafter I gave up due to the lack of control I had over how it worked - issues such as wanting to only serialize some fields in an object but not others, how to serialize compound types such as vectors, etc. It turned out that what I wanted to make was actually two things, corresponding to two bullet points rather than one:
  • Read text from a file and organize it into usable pieces (often called tokens)
  • understand what the tokens mean and assemble a ship based on them.
These two tasks correspond respectively to the duties of a lexer and a parser. I thus proceeded to get my first lexer up and running:
As seen here, what I had the system do was grab one letter from the file at a time and treat it differently based on a few flags, effectively creating a Finite State Machine. Look at me, using big professional words! Obviously what comes next is over-simplified refresher #45890149021 on what a Finite State Machine is:
A Finite State Machine is a thing that can have any one of a limited (finite) number of statuses (the states) and shift between them based on things that happen to it, including its own actions. Oft-cited examples include vending machines, whose states include "idle," from which inserting a coin shifts it into "waiting for selection," from which pushing a button shifts it into "dispensing," from which it shifts itself back to "idle" by moving some of its mechanical bits around in such a way as to (theoretically) give the user one of its items (even better, the item the user wanted).
My lexer has like ten binary flags now, corresponding to a huge number of possible states depending on how granularly one wants to define a state, so I won't be enumerating them all here, but here's an oversimplified interpretation of what it does:
  • Be in one of several states based on how the developer or user set it up or based on what has happened already. These are determined by which flags are set or unset, as detailed below.
  • Grab a character (a letter, a number, a symbol such as "{", or a whitespace character such as a tab or a line ending) from the file.
  • Change one or more flags if the character is special in some way. For example, if the character is a whitespace character, and the "inQuotes" flag is false, then the current token (word, phrase, numeric value, whatever) is complete. If the token is a "key" such as "entityName," then the next token is expected to be a "value" and not a key, and if it is a value such as "The Friend Ship," then the next token is expected to be a key and not a value, so in either case "isKey" should be toggled... unless a flag such as "inData" is true, indicating that multiple values exist to follow the most recent key, e.g. "0.5", "1", and "9.5" all follow and correspond to "position."
  • If the character is not special, i.e. it's just a letter, number, or punctuation or symbol that isn't one of the symbols I consider "special" for these purposes (for example a period holds no special significance to the lexer), add it to a string (technically I used a StringBuilder, but that's all, well, very technical) representing the current token. As detailed above, the current token will build up until a whitespace character is encountered, unless "inQuotes" is true, etc. etc.
  • When a token is completed, i.e. a whitespace character shows up as shown above (wow such non-linearity much complexity doge) and the current token isn't nothing (or empty), and there isn't a flag set that allows treating of empty tokens as actual tokens under certain conditions (yes there actually is one), the token is sent to the parser to be processed along with a modicum of data about the current state machine flags.
Despite the clumsiness with which I bumbled about explaining all that, I did make all of the code work in a very controlled and predictable manner, which is the important thing in situations like this. As can be seen above, the lexer is able to construct tokens for keys and for values and output these tokens for processing - in the above image, "processing" is simply displaying them on the screen with some formatting for clarity.

Since this has ended up somewhat longer than I'd imagined it would, the explanation of what the parser does shall be in another installment.

ShipBasher Development Log 3: Hat Simulator 2̶0̶1̶9̶ 2020

Months-long intervals between posts and then suddenly two posts in one day?!? No. Maybe. As it stands right this minute, I'm uncertain whether this post will actually be finished and I'll deem it ready to publish before another day has gone by, but I do know that I've started typing it within one day (mere minutes, in fact) of the last time I was typing one. Assuming reality is real and all, that is.
Right, the actual post content...
ShipBasher is supposed to include an editor for ships - you know, so you have something to bash. Actually the reason I'm calling it "ShipBasher" is as a portmanteau based the term "KitBash," which describes taking a bunch of existing parts (provided to you prefabricated or previously created by you) and combining them together into a new thing. This is more or less the point of Legos, though I've mostly heard the term in reference to computer-generated artwork, e.g. a 3D animator modeling a bunch of little greebles and then instantiating and intersecting them with each other to make a big awesome mech or, well, spaceship. This is, in fact, how the spaceships in the original Star Wars trilogy came to be.
In ShipBasher, ships (or stations, or drones, or whatever else) are assembled from a number of "modules" selected from a menu, instantiated, and positioned, not unlike the ship editors for Battleships Forever, Kerbal Space Program, Space Engine, and Spore. I don't intend to support resizing or reshaping of the modules as some of these do, at least not at this time, but I do intend to allow editing of the module instances, something that was not supported in "StarBlast!," the prototype version of ShipBasher (again, no affiliation with starblast.io).
Here's how this editor thingy looks so far:
Whoa! Look at that fancy transform manipulator! Maybe I'll write about that another time.
Some features are obviously still in the works, e.g. module previews and better modules that aren't just textured cubes or boring gray thrusters I made in Blender and have yet to bother texturing, but the important features I planned to include are all at least indicated here. At the top is a UI for the ship to be named and given a faction (and in the future, other properties) and buttons to save the ship to a file, load a ship file, clear the scene for building a new ship, and exit. At left is the menu to add modules, with buttons to switch between module categories (e.g. thrusters, weapons, armor plates). At right is a UI related to the selected module, with buttons to change its parent module, duplicate it, delete it, and delete it along with any siblings assigned to it, and with an extensive list of fields for editing the module's properties, from its name to its position and rotation and even how much heat it generates when it turns on. This UI and these fields within it have been my main concern for the past few days.
There are a few dozen fields already and possibly more in the future, and every single one of these has to correspond to a specific property of the module and perform a similar set of tasks when a module is selected:
- if a different module was selected before, check whether the value in the field has been changed, and if so, compare it to the current value and the default value for the corresponding property of that module
- depending on what the new, old, and default values are, update the assigned property on the module
- examine the new selection and whether the assigned property has a value set
- depending on what the existing value is, display either that value, the default value, or a blank or zero value (depending on the field type) in the field
This is a lot of repetitive functionality that I had barely begun to implement in a rudimentary and very clunky manner so far, so about four days ago I set about building a clean new system for handling them. I started an empty scene in an empty folder in my general experimentation project, and in it I put a few simple primitives and a simple UI with a few fields of a few different types. In the end what I ended up making was basically a character creator, so I stuck with the idea and this is how it looks now:
You can read a bit more about my progress on this here: one two three four five
You can click on any of the three characters to select it, edit the character's properties, determine if it has a hat, and select any hat to edit the hat's properties.
Each character and hat has an "editable" component that stores a number of properties as instances of a "datum" class (this is the singular form of the word "data" btw if you want to sound smart). Each datum contains a value and an index. The index refers to an index in an array of names for the data, e.g.:
{ "name", "HP", "armor", "hasHat", "hiddenExtraPropertyNotShownInTheUI" }
The presence of the name array effectively turns each datum into a key-value pair, which is programmer talk for the sort of information you'd find on a web form: username = "problemecium", password = "••••••••", parkingSkillLevel = 100, isBeingPedantic = true for example. I can make any number of name arrays and have them correspond to different sorts of things, e.g. characters, hats, modules, or ships. Each editable is assigned to a certain name array and to a "default" version of itself that, naturally, contains a default value for every datum named in that array. Having this default allows instantiated modules to not have to store copies of every value, but rather only those that have been modified.
Each field also has an index, which determines which datum it is meant to display and edit. Fields are able to intelligently scan for a datum with the correct index, find the default value if no such datum exists, examine themselves for what form to have that value take (string, floating-point number, etc.), and display that value in the UI. Fields can be linked to other fields, e.g. the armor field shown above, so that players have multiple ways of reading and editing the information.
Each field is also able to independently target any editable in the scene. Thus as seen above, character fields can target a character when one is selected and still correspond to that character even when the selected item is a hat, or vice versa. This actually enables me to do something awesome that I hadn't planned at first: select and edit modules on a ship inside a level that contains multiple ships. I could even pause a level that's being played and then edit values in the modules on one of my ships, for instance refilling a fuel tank or setting a damaged module's HP back to 100%! Naturally I want players to have to enable the developer cheats to do this unrestricted, but a limited version of this functionality could enable them to create, say, a level in which you have a damaged freighter you have to protect because all of its turrets have run out of bullets and the engines can only operate at half thrust.
(Battleships Forever, a major early influence on ShipBasher, includes a mission where you can interact with damaged ships from a previous off-screen battle, as seen here.)
I'm considering releasing this system on the Unity Asset Store if there is enough interest in it, as I have designed it to not be specific to ShipBasher and, in theory, work with any project that involves selecting things and editing their properties. Please do let me know if you think this would be useful in a project of yours or if you see any issues or think there is a feature it sorely needs.
As it stands now, I think this is feature-complete, and with it having passed all the robustness tests I've thought to toss at it, I intend to start incorporating it into ShipBasher. I'm not entirely sure whether to maintain the existing interface design or try something new such as a single editor that handles levels, ships, and modules. With any luck, the next devlog will reveal some exciting progress on that front.

RNGesus is an unruly troublemaker who'll cause you all kinds of grief if you let him off his leash.

"Find Your Car" includes, unsurprisingly, a lot of procedural generation and in turn a lot of pseudorandom number generation. As with my prior article about pseudorandom numbers, I felt like taking a few minutes here to share my experiences wrangling the laws of mathematics to achieve my goals.
A major purpose of this game is acting as a demo for my procedural universe chunk system, particularly its versatility "out of the box." I accordingly designed it with as much modularity as possible and used preexisting utility scripts I'd already written wherever I could. I ended up needing to upgrade a few of these, but in so doing I was careful to keep them generic and thus reusable.
A notable example is my "Swap With Random Prefab" script, which when attached to an instance of a prefab will, once it is spawned, choose from a user-specified list of prefabs, instantiate it in place of itself, and then despawn itself (or, in the upgraded version, an optional "target" object). This has been useful to me in the past for, for example, spawning a placeholder tree and then swapping it with a random other tree to add variety, or having a zombie drop a random item when it is killed. In "Find Your Car," it is used for walls and floors to randomly replace them with a variety of walls or floors, e.g. a floor with a ramp or a wall with a door, when a chunk is refreshed. This way I could construct one template parking garage chunk and then have it adopt a large variety of configurations as it was instantiated throughout the garage.
Some readers may, assuming I'm explaining this well, see the problem already. With this level of encapsulation, the wall and floor instances have no idea what chunk they occupy and thus no connection with its random seed, but they do call upon the built-in random number generator - meaning that the random swaps they perform will be truly unpredictable and not deterministic. The user-facing side of this problem is that a player could leave an area, walk a decent distance away so that the corresponding chunks are unloaded, head back so that they get reloaded, and find that they have regenerated completely differently! There might be walls where there weren't any before, cavernous voids where there had been floor, and the car that had been left comfortably parked in an open space now lodged halfway inside a wall!
Ways to fix this didn't come easily. The most obvious solutions were to refactor all of my utility scripts to use interfaces such as "seedable random" or to move all of the random generation that needed to be deterministic into the chunk refresh function, sacrificing modularity in favor of a complex monolithic algorithm. As may be inferrable from my tone I wasn't excited about either of these. I did find a solution, but it involved sacrificing performance instead (and what I imagine is less than professional-grade code) by creating a helper script that would scan chunks for specific scripts and call non-randomized versions of their swap methods, which it would randomize itself based on the chunks' random seeds and positions - a bit of a midway point between the other two options that I considered a workable compromise.
The reason I bring this up isn't to brag about my hacky workarounds to my own buggy code though. The reason I bring this up is because I learned a valuable lesson and I want to pass it on to everyone else who tries to do something like this: be very careful when dealing with random number generators if you want deterministic results. If you don't make sure that any and all objects using them are strictly controlled so that your random seeds (or states, etc.) are properly enforced, the generators will bite you in the butt with no hesitation and happily run off generating all manner of decidedly non-deterministic, unpredictable values, the end result of which is an unstable game world. Unless that's what you want, keep them under control!

Sorry this isn't a real post :C

I've entered graduate school and, as I expected, suddenly become far busier than I had been before, leaving no time to maintain my blog,...