September 2023 Tech Update! Developer Console/Input, UI Improvements, and Inventory


Wow, it's been a hot minute since my last update! A lot has changed with the game since the last update, so let's hop right into things.

The Developer Console

Previously, when I wanted to test out a new feature, I'd have to throw some UI buttons on the screen and tie them to functions to test things out. This worked, but it was really annoying. 

I love how Source engine games have a developer console that looks like this:

So I built my own! Well, kind of. There are 3 pieces to it.

The first is a console log where I can see all the logs that have been printed during the current game session. That looks like this:

You can see different severities of logs (info and warning, in this case) get color coded appropriately. This helps to be able to review logs when the game is running outside of the Unity editor (like, on a phone).

The next part is popup log messages! When something gets logged, a pop up appears on the screen to show something happened. That looks like this:

See that little popup at the bottom? Ain't it adorable? This is, again, helpful when running on a mobile device.

Finally, the last component solves the issue I mentioned at the beginning. This is the developer input panel, and it looks like this:


As you can see, all I needed to do to get a dialogue scene to run was type "dialogue tutorial_intro" into the console popup, and away it went!

The way the developer input panel works is extremely flexible. For each console command, I create a class that inherits from a "DeveloperInputCommand" abstract class. That class looks like this:

public abstract class DeveloperInputCommand {     
    public abstract IEnumerator Execute();     
    public List<string> parameters;     
    public string helpText; 
}

Execute is a method that gets called as a coroutine when the command is submitted. Parameters are any words that come after the command (so, in the case of "dialogue tutorial_intro" - "dialogue" is the command, and "tutorial_intro" is the parameter at index 0)

Each class name is what defines the command. These get parsed using the most magical concept in programming ever called "Reflection". I look for a class name that is defined under a developer input command namespace, then create an instance of that class and call its execute method.

So to add a new command, I just create a new class! It's that easy.

You may have noticed the "helpText" string. If I type "help dialogue" into the command window, it will show a popup with directions on how to use that particular command. Super nifty when I completely forget how I wrote my commands!

These 3 pieces help me debug much easier, and have been a huge help as I've continued my development journey.

Now, let's talk about some UI.

Dialogue UI Changes

As you might have noticed from my dialogue demo - things have a new look for the dialogue popup. Let's take a look:


I've worked to create a new design language for my UI that features rounded corners and subtle animations. It looks much better than the old placeholder UI I had previously!

The system behind it is the same as I talked about in my last update, it just looks a lot nicer now :)

It also features some new character artwork by the very talented @KoltRiv!

Inventory

I've spent the last few days working on building out a new inventory system. Let's take a look at it:

The inventory system follows the new design language and looks awesome, in my opinion.

Behind the scenes it's pretty straightforward. I have a list of characters that are on the player's team, and their data gets loaded from a JSON file that looks like this:

{
    "characters": [
        {
            "identifier": "player",
            "name": "{PLAYER}",
            "characterClass": "Swordsman",
            "experience": {
                "level": 1,
                "experiencePoints": 0,
                "maxUnpromotedLevel": 20,
                "maxPromotedLevel": 30
            },
            "health": {
                "name": "health",
                "current": 4,
                "currentMax": 12,
                "maxUnpromoted": 22,
                "maxPromoted": 56
            },
            "mana": {
                "name": "mana",
                "current": 7,
                "currentMax": 7,
                "maxUnpromoted": 41,
                "maxPromoted": 81
            },
            "spells": [
            ],
            "stats": [
                {
                    "name": "attack",
                    "current": 4,
                    "maxUnpromoted": 11,
                    "maxPromoted": 21
                },
                {
                    "name": "defense",
                    "current": 4,
                    "maxUnpromoted": 15,
                    "maxPromoted": 41
                },
                {
                    "name": "speed",
                    "current": 18,
                    "maxUnpromoted": 18,
                    "maxPromoted": 6
                },
                {
                    "name": "movement",
                    "current": 13,
                    "maxUnpromoted": 8,
                    "maxPromoted": 8
                }
            ],
            "items": [
                {
                    "identifier": "wooden_stick",
                    "equipped": true
                },
                {
                    "identifier": "health_potion"
                },
                {
                    "identifier": "health_potion"
                },
                {
                    "identifier": "mana_potion"
                }
            ]
        }
    ]
}

This file drives the character's initial items and stats, and will eventually only be used for new games, with saved games just reading a save file. Regardless, you can see how we define items that are stored in the character's inventory and whether or not they are equipped.

These item details are then read out of another JSON file, with an item looking like this:

{
            "identifier": "health_potion",
            "name": "Health Potion",
            "description": "It's a flask of red goo! Drink it and your health somehow increases by 20 HP.",
            "shopDescription": "I have no idea how they make these things, but they're pretty tasty!",
            "cursed": false,
            "canDrop": true,
            "value": 7,
            "cost": 15,
            "useInteraction": "healthpotion_use",
            "equippableClasses": [],
            "useableClasses": [],
            "stats": [
                {
                    "name": "health",
                    "add": 20
                }
            ]
}

This is how I store the details for each item, with things like their description and what interaction is used when the "use" button is hit (which its details are stored in yet another JSON file!)

The reason things are split up like this is so things have a clear separation of duties. The first time I coded out Bravewater, I made everything very tightly coupled, and in some cases duplicated their functionality. This resulted in a mess, and I'm trying very hard to avoid that this time.

The Battle System

At some point in the last year I worked on completing the battle system. I think I broke it by doing some other changes, so I'll have to go back and get it working again, which shouldn't be too hard. Here's a video I made showing off how it was coming along at the time:

I re-watched this and was very proud of it. I look forward to sharing more about this once it's back working again!

What's next?

I've been working on this game for far longer than I would like to admit. I really want to get a playable demo out by the end of next year, but to do that is going to require a lot of hard work. Here's what's left on my list of building out to get to that end goal:

  • The shopkeeper functionality to buy/sell items
  • The quest system to keep the player informed of what their objective is
  • Finishing the battle system
  • Game saves, and a menu to access them
  • Completing the dialogue JSON for the first chapter
  • Artwork for the world
  • Artwork for the characters
  • Music for the first chapter

Phew. That's a lot. But I am determined to make it happen!

I'll see you in the next blog post, hopefully soon  :)

-J

Leave a comment

Log in with itch.io to leave a comment.