Monday, May 17, 2021

Once again, but with Templates

I've been wrestling with the system behind visible terrain for the past week. It's been in this awkward state of "I almost like using this" for too long now. I'd like to use the system for real... so let's make a real game.

How about we combine the idea of a tower defense game with a card-system that ties structures to rooms? The player can create defensive areas and manage resources to build new rooms. I'm a big fan of platformers; that should give this game a lot of verticality and make falling traps - both creatures falling and spikes from above - fun to design.

Anything built here can be re-used elsewhere. Alisia Deena Rain can stave off every enemy from her game, use every one of her powers, and have a failure state based on lives. mDiyo (the character) can build swords and armor, 'find' soldiers to give the swords to, and fend off a whole bunch of slimes. As long as the game is fun, anything goes!

Basic Game Stats

Resolution: 640x360 px
Tile size: 16x16
Template size: 8x8 tiles or 128x128 pixels
Room size: Minimum of 1 Template. Maximum of... a lot?
 
Style: 2D sidescroller
Genres: Platformer, Tower Defense
Aesthetics of Play: Challenge, Fantasy, Discovery, Expression

References: Boss Monster, Super Metroid, Starcraft

Resolution
 
640x360 (360p) is the ideal resolution for retro-styled games. Most monitors use a 16x9 resolution with 1920x1080 (1080p) as their base. The scaling works near perfectly:

360p: 1x
720p: 2x
1080p: 3x
1440p: 4x
4K: 6x
5K: 8x

There are a few resolutions where this doesn't scale up as nicely such as 1366x768. In these cases, we can give the player a bit more screen space in whichever direction doesn't match. 

Tile Size
 
Tile sizes were picked by feel, system limitation, and tradition. Unity lets tiles be any resolution at all; you can even mix and match them. Traditional tile sizes are 8x (Sega Genesis), 16x (very common), or 32x (RPG Maker), with a few crazy systems going for larger systems and a few super-constrained systems going for less. 

At 16x, the screen can display tiles 40 wide and 22.5 down. This gives us a nice area to work with that isn't so small we end up fiddling with all the tiny little details, but isn't so large that a good artist needs to make multiple variations on tiles to get them right. This size groups up well in sets of 2, 3, and 4.

Video cards store textures and process information in powers of 2. Unity automatically adds a buffer to the edge of graphics that aren't a power of 2. A lot of weird problems are mostly mitigated, but there are still a number of features that don't work correctly with odd shaped textures due to under-the-hood shenanigans that the engine performs to prevent your computer from exploding.

Templates and Rooms
 
 

This is a full-size mockup of the game. Each light blue square and its border is a set of tiles that we can call a template. Groups of these templates make up a room. Rooms can be as small as 1 template or larger than the entire screen. Templates can be arbitrary shapes, but rectangles should be easiest to work with. 

Making it Work


Mockups aren't too hard to make in-game. The base project has tiles, colliders, characters, health, tools, and anything else needed already set up. Almost all of it is in a state of "this works, but it's ugly/hardcoded/barely useful", also known as "good enough". Let's just grab some basic colors for tiles and...

Perfect. I can work with this. 

Getting the template system working was no easy feat. I had duplicated a lot of code into six separate places... templates were loading differently from the editor's inspector, the game itself, and I wanted to make them load by clicking on the asset. Editing one thing would leave the rest intact; refactoring this mess down was mandatory. The entire workflow needed to be adjusted so that I could spend more than a moment in the editor without short-circuiting my brain with code questions.
 
The code's flavor changed from 300 lines of spaghetti into this:
public void LoadTemplate(Template template)
{
    //Find tilemaps
    Tilemap[] maps = TemplateHelper.FindTilemaps();

    //Let the Template Builder know that we're adjusting it from the outside
    TemplateBuilder builder = GameObject.Find("Template Builder").GetComponent<TemplateBuilder>();
    builder.assetName = template.name;

    //Load the room
    Vector3Int templateCorner = Vector3Int.zero;
    TemplateEditorHelper.LoadRoom(maps, template, builder, templateCorner);
}
A lot of effort was put into making a template system that could save and load rooms that have a background, terrain, and foreground layer. The template needs to keep track of objects like trees, spikes, or special creatures. It also needed a new set of paint.
 
This looks good. Suspiciously good... has it ever looked this good? I don't think so. Why does this feel right and why didn't I spend the time before to make this actually work in a reasonable manner that a designer could understand? It feels like I've spent so much time floundering around in my own head that actually showing this off is a feat in and of itself.
 
Let's grab a few sprites from my unsorted design archives and the Open Pixel Project, a few sounds from royalty free sites, and string everything together in the most basic version of 'reasonable'. Write just a little bit of code to get this whole thing working... 
 
public override void GenerateLevel()
{
    //Terrain
    Tilemap[] maps = RoomTemplateHelper.FindTilemaps();
    Vector3Int roomSize = groundLayer[0].GetBaseSize();
    Vector3Int mapSize = new Vector3Int(roomSize.x * groundSize.x, roomSize.y * groundSize.y, 1);
    TileBase[] tiles = new TileBase[mapSize.x * mapSize.y];
    Debug.Log("Making " + tiles.Length + " tiles betterererer");

    //Wipe the map
    maps[0].SetTilesBlock(new BoundsInt(Vector3Int.zero, mapSize), tiles);
    maps[1].SetTilesBlock(new BoundsInt(Vector3Int.zero, mapSize), tiles);
    maps[2].SetTilesBlock(new BoundsInt(Vector3Int.zero, mapSize), tiles);

    //Generate ground
    int baseCount = groundLayer.Length;
    for (int y = 0; y < groundSize.y - 1; y++)
    {
        for (int x = 0; x < groundSize.x; x++)
        {
            RoomTemplateBase template = groundLayer[rand.Next(0, baseCount)];
            RoomTemplateHelper.LoadRoom(maps, prefabContainer, template, new Vector3Int(x * roomSize.x, y * roomSize.y, 0));
        }
    }

    //Generate top layer
    baseCount = grassLayer.Length;
    int treeCount = grassTrees.Length;
    for (int x = 0; x < groundSize.x; x++)
    {
        if (x % 4 == 2)
            RoomTemplateHelper.LoadRoom(maps, prefabContainer, grassTrees[rand.Next(0, treeCount)], new Vector3Int(x * roomSize.x, (groundSize.y - 1) * roomSize.y, 0));
        else
            RoomTemplateHelper.LoadRoom(maps, prefabContainer, grassLayer[rand.Next(0, baseCount)], new Vector3Int(x * roomSize.x, (groundSize.y - 1) * roomSize.y, 0));
    }
}

The overall result?

We have ground, grass, trees, a ghost template, the character, a multi-part hitpoint and status effect system from another source, and a tool UI from that same other source. The ground is built from rooms and everything works as intended.
 
It just works. Huh.  
 
IT FINALLY WORKS!!

I spent a week getting all of this to work. Most of it was in place already; it was ugly, sabotagetastic, and weird. Now it's something I can be proud of and have enough progress to think about sharing with the world. 
 
I'll work on the other systems in due time as the card system gets fleshed out and the skeleton turns into a fully-fleshed out game. For the first time in the history of the Base Project, a game has been built out of its pieces. This has been a long, long time coming and I'm glad it's finally coming together.

Content Versioning

Version numbers are like opinions: everybody has one, they tend towards the majority, or you're a madman who thrives in utter chaos. The programmer side of life tends towards regimented, structural numbers, and the marketing side sees a version number as a way to show progress or change in their project.
 
Small video games or projects that constantly change in early development tend to have version numbers that don't make a lot of sense. It may not be feasible to keep track of a version in a way that makes sense to a machine. Projects can get pretty wild before they've been scoped out and defined even with the best idea behind them.

This gets even more complicated when you throw in mods. Do mods need to include the game version they're built against in the version itself? If the mod works on a range of acceptable game versions, does it need to include that? What about addons for tabletop games? Hacks? Total conversions? Grafting an entire game onto another game?

I developed this versioning format to try and sort out some of the weirdness and incompatibility that comes with rapidly creating content on top of systems and then gutting the entire thing because the core of your design is good, but the implementation is horrid.

This format works best for alpha or beta projects before their full release. Projects that are content heavy, like video games, benefit the most. Marketing may like the nice feeling of a 1.0 public release or a continual push towards the future, but this format has an advantage that no others do: the version history itself can tell a story of development, of progress, and of change.

Content Versioning

Format: MAJOR[DEVELOPMENT_PHASE].SYSTEMS.CONTENT.PATCH/TEST

Example: A.6.2
Example 2: 3A.4.17.340
Example 3:
(1.16).4.1

Major Version: The intended version of the project you're working on. Generally, this will be your first project. The first version can either be 1 or 0 until there's a full release of the game.

The major version number is optional before a full release. It's most useful when the project you're working on is a sequel to another project. A or B at the beginning of the version is painfully obvious that this is not a completed project.

Major version is also a good place to put a game's version. Minecraft tends to change itself every 6 months or so and Windows 10 breaks drivers like clockwork.

Development Phase: These are usually referred to as "alpha", "beta", or "release", with the occasional extra step such as release candidates. This can also be arbitary; replace the letter as the project moves from phase F to G in its nine-phase development outline.

Systems: The most useful number for programmers. The number represents how many working modules of code, gameplay dynamics, or miscellaneous systems are currently working as intended. 

A simple platformer may have a character, level, and UI system. Complicated games will have systems built on top of systems and the layers themselves will interact in multiple ways. This number will naturally go up quickly at the beginning of a project and level off towards the end.

Content: This number relates to the amount of content in the game. Divide up the game into bite-sized pieces and increment the counter when one of them gets made. 

Made a new enemy for a level? Bump up the content number. Your custom crafting system had a significant amount of new content put in that is going well? Bump it up some more. Finish off the final boss in the game? Bump it twice for good measure.

For good measure, keep track of how much content each bite actually takes up. Refining a level or a content system can make the number go up, down, sideways, down some more, and shoot way up on a day of inspiration. Try not to make this number go down after release or you will have people asking a lot of questions; version numbers only go up, after all.

Patch/Test:  

Before release: You released a new test version or wanted to make a build just for the sake of it? Number goes up. No thought required.

After release: Something broke, you just wanted to make a couple small changes, or forgot a readme file. These changes don't change the project substantially, so the number goes up. This number resets any time the systems or content increments.

The content, systems, and test numbers work independently from each other. Content and systems are often related no matter what type of project you're using, but sometimes you'll develop the entire system before touching one drop of content and other times two groups will work on the entire project in parallel.

Detailed Example: Tinkers Construct 1

Tinkers' Construct 1 was the main source of frustration with versions. Let's apply this system to the mod retroactively and see where we end up. From the top:

The initial public release had three systems: Tool Core, Modification, and Crafting. Content would be broken down into rough chunks of swords, other tools, tables, patterns, and a couple more for modifiers. The initial release was on Minecraft v1.4.7... so the mod would have a version of 
 
Initial version: 1.3.6
 
The first release was botched pretty badly; I ended up hotfixing it 8 times before adding more content. 
 
Hotfix version: 1.3.6.8
 
There was a bit more development to flesh out modification and the tools with a proper release every two weeks. Add in another system to make auto-smelting, fortune, or silk touch on tools and carry on.

Midpoint version: 1.4.14.2

The second major release was a continuation of the project. There's a new idea in town: The Smeltery. This took a very long time to build and involved multiple new systems: structure creation, alloying, casting, fluids, networking, and broad tools. Content amount goes up in spades and the mod obtains an inordinate amount of content compared to before.

Smeltery release: 1.9.30

That's a huge jump in systems and a rather large jump in content. It took 6 months to sort out all the bugs, finish up content, and get everything moving smoothly.

Smeltery endpoint: 1.9.38

At this time I had switched to a content-driven development format. The version was bumped up to "1.3.0" from its "1.2.18" point, but there was no real reason for that. I just wanted to make a whole lot of things after spending so much time on systems. There was also the odd problem with armor being wanted, and I tried adding a system for that... but it was not exactly great. I had to add things and rip them out multiple times.

Content start: 1.10.40

The overall quality of the mod went up over this time. Things were refined and polished in a way that hadn't been done before. Most of the systems were already made; it was time to add the rest of the things on top of that.

Final version: 1.10.70
 
This version history tells a much different story than the butchered semantic versioning that I was using before. There wasn't a real rhyme or reason behind anything. The number was detached from the source, and while it was still useful to compare and see whether things were out of date, it wasn't useful beyond that.

Let's compare against the two most common versioning systems:

Semantic Versioning

Semantic versioning is great for keeping track of a particular version in a project. Its best use is for libraries or similar files that code or documentation depends on. You know immediately if your code is out of date, or if the library you're using is going to work at all.

SemVer has a very mechanistic view on how its versions should be described. The format is simple: MAJOR.MINOR.PATCH.BUILD
  1. MAJOR version when you make incompatible API changes,
  2. MINOR version when you add functionality in a backwards compatible manner, and
  3. PATCH version when you make backwards compatible bug fixes.
  4. BUILD version when a program attempts to compile a nightly build
Projects with automatic build systems need more information. BUILD is an extension of this format, but is usually left out on releases.
 
jashkenas has a good writeup on why semantic versioning is not semantically sound. In short, the version format compresses too much information into a single three-number phrase and doesn't take into account the human factor. It's not good for dynamic systems that are constantly changing.

Calendar Versioning

Some software is updated regularly. Some companies have taken this idea and incorporated it directly into their version numbers. If a piece of software has a major change once per year, then it has a larger number to match. EA games such as FIFA or Madden, Adobe products, and Ubuntu releases follow this mold.

CalVer takes a temporal view on how its versions are described. Let's take a look at Unity3D's format: 2018.4.35f1
  • Major - Calendar Year. Unity used 1-5 for its calendar version up until 2017, which switched the version number itself to the year.
  • Minor - Changes. Unity uses this for new systems that tend to change or break things.
  • Micro - Patch. Small but significant changes like bugfixes.
  • Modifier - An optional text tag, such as f1 for "final/hotfix on version 35". Unity has one of these on every build (usually f1, but people make mistakes)
The date may end up directly in the version number somewhere, usually the major number. Other times the number will be incremented automatically each time the date switches over.
 
This format is great for long-term projects with a regular release cycle and a marketing team behind it. The larger and longer-lived the project is, the more benefit they will find in the consistency of time.
 

Final Thoughts

Content Versioning is best used on projects that have an erratic development process or lean heavy into content like video games. The version itself holds more meaning than an increment in the development process and can be used to gauge how the project is going. It is best used where other version formats are inappropriate or are a poor fit. 

I use this format for game development. It's a far cry from the mechanical incrementation of SemVer, and who in their right mind would use a calendar format on a project that isn't even released yet? The format is a tool, and like all tools they have a time and a place where they are best used.

I may carry this format into full release later. I'm not sure if I'm going to have an external version of 1.0 alongside an internal version of 1.40.3249 on any future projects; we'll see when we get there.

Tuesday, May 11, 2021

Room Template UI Improvement

 

Poor programmers blame their tools for their mistakes. Great programmers build their own.

 

I've found myself picking up game development, putting it down, starting on a new project, picking up game development again, putting it down again, and on and on the cycle goes. This cycle has been going on for some time and I haven't really been sure why. It's almost like something was short-circuiting my flow state and making the entire experience dissatisfying.

Today, I came to the realization that all of the tools I have made are getting in my way.

Unity is a game engine in two parts: the designer interface and the scripting backend. The design side of making a game has enough information and enough niceties that a skilled designer can take art, scripts, and sounds, combine them all into a series of experiences, and end up with a polished product at the end.

Programmers, on the other hand, bury themselves in the code. The behavior of everything from enemies to GUI is their domain. Each and every thing

The problem when you mix these two workflows together is that they can sabotage each other. A programming task that wants feedback will have the developer jumping into the unity editor - design side - and immediately testing out what they have. This is great when everything is working smoothly, not so much when you have to figure out what you're doing every time. 

 Say you have a box like this:

This is the ugly cake room. I'd like to save this room for later; let's put it into a data structure called a Template so we can take it down and pull it up on a whim. How do we do that? By building an asset file out of it.

This is the inspector UI of a room template builder. Things are organized roughly in order, but it's hard to tell what's what at a glance, there's a bunch of useless information that the builder needs but the designer doesn't care about, and who can tell what template 2 exits to at all? I certainly couldn't.

This is actually the improved UI. The old one was worse... we'll just pretend that doesn't exist.

The template itself saves just fine. The UI on this asset is also kind of ugly, and it has no texture. Only the Unity default image.

Normally when I pull up an old project I'll get excited about some idea that I would like to try making and make it as fast as possible. I'll muddle through any weirdness in here, re-learning anything as I go along, getting used to the thing that I had made, and eventually just leaving with a feeling that I made what I wanted but something about the whole experience was off. 

Any time I would hit the UI I would already be in the mindset of "design" and not the mindset of "programmer". Everything I have built like this is built with knowledge of how it works in mind. There's no room for mis-remembering and if I showed a random person what this did, who knows? Looks like a bunch of text, some numbers, unsorted checkboxes, and buttons. Nothing really draws your eye to any part and the best you can do is "muddle through".

Switching from Visual Studio to Unity is like flipping a switch. One side is code, the other side is design. The designer in me is cringing at the programmer who just wants to get things done, and the programmer in me is getting frustrated at the ease of flow between prototyping, testing, and bugfixing. It's so bad that I'm repeating myself. 

If it's that bad, then I can certainly do something about it. What about this...


That's a LOT better. It's so much better that I'm surprised I didn't try this before. There's a large button to draw your attention, multiple buttons below it that are easier to navigate, the checkboxes for room exits are sort of in the same spot as the room connections, and there's a readout on the (now somewhat in order) location of the rooms in this template group.

The code for this is at the bottom of the post. It was tedious to write and was repetitive and fiddily. A lot of the code looks like this:

int spacer = 40;
for (int i = builder.foldoutConnections.Length - 1; i >= 0; i--)
{
    int y = i % builder.templates.y;
    int x = i / builder.templates.y;
    builder.foldoutConnections[i] = EditorGUILayout.Foldout(builder.foldoutConnections[i], "Template " + (i + 1) + " (" + x + "," + y + ") connections");
    if (builder.foldoutConnections[i])
    {
        GUILayout.BeginVertical();

        GUILayout.BeginHorizontal();
        GUILayout.Space(spacer);
        builder.connections[i * 4 + 0] = EditorGUILayout.ToggleLeft("Up", builder.connections[i * 4 + 0]);
        GUILayout.EndHorizontal();

        GUILayout.BeginHorizontal();
        builder.connections[i * 4 + 3] = EditorGUILayout.ToggleLeft("Left", builder.connections[i * 4 + 3], GUILayout.Width(60));
        builder.connections[i * 4 + 1] = EditorGUILayout.ToggleLeft("Right", builder.connections[i * 4 + 1], GUILayout.Width(60));
        GUILayout.EndHorizontal();

        GUILayout.BeginHorizontal();
        GUILayout.Space(spacer);
        builder.connections[i * 4 + 2] = EditorGUILayout.ToggleLeft("Down", builder.connections[i * 4 + 2]);
        GUILayout.EndHorizontal();

        GUILayout.EndVertical();
    }
}

Not bad for a bit of time in Unity's documentation on EditorGUI. I'm sure this can be improved; template connections would look nice in the same layout as a 2x2 grid with buttons instead of checkboxes... but that would take more than the 3 minutes I spent figuring out how to arrange them properly. I'll chuck it on the back burner for now.

What about the cake room asset?

Scriptable Objects automatically change their icon when their script is changed. This works well enough for differentiating generic objects. The templates don't particularly need an icon associated with each one... that will come later.

The main thing that's bothered me for a long time is the flow of loading up templates. You have to go to the template builder, type in the name of the template, and press load. This is more-or-less fine... is what I keep telling myself. I swear, on the blood of monsters and all that is worthy of derision, this problem is just like the other problems fixed today: it's tedious, irritating, and why did I put up with it this long?!

Let's just load the asset directly from its display.


It would also be nice to preview this template without needing to load up a scene dedicated to editing them. That's... going to come later. The button will eventually bother me enough that the feature will get built, and the button can just exist for now.

Here's a lesson in being lazy: The GUI elements are in the correct spot and I'm too tired to get this done in one day. Things are arranged in quite a bit more readable fashion, so it's good enough for now. Plus anyone who looks at this can ask why there's a bunch of TODOs staring them in the face.

There's been quite a few changes in all of this. I just need to test everything out to make sure that nothing is broken and...


I forgot to save the rotation of the tiles in this template. There wasn't any code to save the rotation when I started on this trip. I just wanted to make some nice rooms so I could take this idea of building a dungeon of cards and protecting the mDiyo workshop from a whole ton of slimes crawling on every wall they can touch.

This entire process is a lesson in Yak Shaving that turned into the primary goal. I'm not sure exactly how I feel about that, but it feels good enough for one day.


Anyone who wants to peruse my code can take a look at pastebin. TemplateBuilder needs to be attached to a GameObject; everything else should work as-is.

TemplateBase
Template
TemplateMaster
TemplateEditor

Template Builder
Template Builder Editor

Self Reflection, Avatar Reflection

It started as a joke. One day I decided that my game development was going poorly because I was too attached to my characters. If I messed a...