Showing posts with label ShipBasher. Show all posts
Showing posts with label ShipBasher. Show all posts

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.

12 July 2019

ShipBasher Development Log 1: So Far

Way back when I was in game development school I had an assignment for which I built a clunky prototype of a game I, for lack of a better name, dubbed "StarBlast!" (no affiliation with the later and much more successful starblast.io). The basic concept was a sandbox in which players could open up a "Ship Builder" minigame, construct a space warship out of a variety of predefined sections, and then switch to either a "Scenario Editor" or the game's Campaign or Sandbox modes to spawn a bunch of warships either of their own design or pre-built and pit them in battle against each other. I even made a cute little trailer for it even though I never made a proper public release, let alone tried to sell the thing:
One of my pet projects, as I've given a cursory mention previously, on which I've worked off and on in the years since then is to rebuild this game with entirely my own assets, more robust code, a more polished general design, and lots of user customizability and mod support.
Here's what I have so far:

It's rather barebones at this point, but it counts as progress nonetheless. I've also mocked up a ship editor complete with the ability to spawn some cubes and move them around (whoa!) and with some pretty buttons to click that don't do anything useful.
There isn't much else to say at this point aside from expounding on the details of the design of the game, but nobody wants to hear about my Totally Original Idea™ with no implementation in sight, so instead I mean to reveal the workings of the game piecemeal over the course of its development logs.
My current hurdle is figuring out what sort of system to use to save and load ships, and eventually other data - XML? Unity's built-in JsonUtility? A third-party JSON library? My own homebrewed lexer and parser? Hmmmmmmm... Expect the next update to involve my answer to this puzzle.

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,...