Wednesday, November 22, 2023

Inventory System Port to Godot 4 C# Complete!

Greetings to all and a Happy Thanksgiving to those of us in America.  As my wonderful wife is hard at work preparing for Thanksgiving tomorrow, I am finishing what has been a very huge re-write of my game's inventory system from Godot 3 and GDScript to Godot 4 and C#.  It took longer than I expected, but then I did chase many rabbits which resulted in many great new features.

I want to talk a little bit about what the process was like and compare the two implementations.  Permit me to dive right in.

Godot 3: (March 2023)

Godot 3 Alpha

The Godot 3 version stored static inventory item definitions in Resource files.  This worked well and made updating the properties of each inventory item easy to do directly from the Godot user interface.  All instanced data was stored in Godot dictionaries.  The system was comprised of a Singleton for much of the inventory system logic, three UI nodes for the slots (small, medium, large) and a UI node for the base inventory window (shown below).  The inventory window dynamically builds itself based on the configuration of the properties stored in the Resource file for the respective container object.  The system had two types of objects, being containers and items.  Liquids were considered items.  It offered basic drag and drop, stack splitting and animated opening and closing.  Finally, it supported weight values shown in standard and metric systems.

I enjoyed learning about the Resource system in Godot and I only have one critique about it.  Sometimes my data structures were complex and when my resource contained an array, it was very difficult to add something to the array without having to re-key the data into the array in order to pick up a change.  GD Script was also fun to learn and I was able to quickly put together a basic system.

Godot 4: (November 2023)

Godot 4 Alpha

The Godot 4 version of the inventory system was essentially a complete re-write from GDScript to C#.  I switched from Godot Resources to JSON files and from Godot dictionaries to C# dictionaries.  I also switched from using Godot Signals to using C# Event Handlers.  Hum... what else...  I structured my Visual Studio solution to be close to a Clean Architecture style where I have my scripts broken down into an Application folder for the UI scripts, a Domain folder for my inventory classes, and an infrastructure folder for injections like my JSON implementation., etc.  It's not completely Clean Architecture simply because everything is on one DLL.  But it helps me organize my code.

There are so many new features as I found myself chasing rabbits. Even so, it was a rewarding experience to put this system together (again).  Let me share some feature highlights with you.

Inventory Window Base Scene

  • Inventory container window scenes build dynamically from JSON definitions where each container can have up to three slot sizes in any combination of slot counts and slot columns.
  • Container windows can be dragged anywhere in the game and locked in a set position.
  • Containers that have child containers can be opened via double-click or clicking the bottom right icon to open/close all child containers at once.
  • Weights can be configured to show in standard or metric measures.
  • Static JSON-based inventory definition files for image locations and data for items, containers, and liquids.  The goal is to allow players to be able to introduce their own items into the game or tweak any item to customize the game experience.
  • Instance game data will also be saved as JSON files.
  • Sound effects for valid and invalid actions, including liquids pouring from one container to another with the sound effect selected based on how much liquid is actually being moved around.
  • Extensive drag and drop scenarios (item to slot, item to container, item to item, container to container, etc.
  • Shift-drag-and-drop to split stacks in half.
  • Ctrl-drag-and-drop to bring up a quantity dialog for specific amounts.
  • Auto-merge and auto-fill when dragging items onto slots and containers while respecting volume constraints.
  • Containers have max volume constraints and separate liquid unit measurements for those containers that can hold liquid.
  • Dragging an item from a slot will double the size of the item visually
  • Custom tooltips which display summary information for containers, items, and liquids.

These are some of the features currently coded for the inventory system.  Some features I want to add in the future include selecting multiple slots at one time for drag-and-drop between containers, the ability to sort items inside containers based on name, type, etc., and the ability to pick up all items and deposit them into the player's inventory from a loot drop.

What's next on my list?  I am now going to move on to finishing my flora system along with all remaining features of the Godot 3 version that have not yet been ported over.  I hope you have enjoyed reading about my game.  Feel free to comment or suggest here in my blog, or on Twitter.

Until next time,

I'm Bound2bCoding

Wednesday, November 8, 2023

November Update

I want to mention briefly that I have changed my YouTube and Twitter handles.  Eyesgood is a name I have long used in gaming and I have really wanted to identify with a different handle in the context of my game development life.  Yesterday, I had a moment of inspiration and came up with:  Bound2bCoding.  Look for that name going forward as I sunset the use of Eyesgood in my dev life.

October was an amazing month of development work.  While working on converting the inventory engine to C#, I expanded the capabilities of the inventory system to handle scenarios that were not even considered in Godot 3.  I am still in the midst of this translating and refactoring process which I expect to run out to the end of the year.

C# Tip:  Unsubscribe C# Events Before Removing Scenes

One thing I discovered while testing my C# version may be of interest to any developers reading this.  I learned that C# events that are subscribed to are NOT unsubscribed just because the node is removed from the scene tree in Godot.  For example, I have a singleton named InventoryEngine which handles all the drag and drop transactions among other things.  I also have an InventoryWindow scene that subscribes to an event in the InventoryEngine.  The window node is instantiated each time a container is opened.  When the inventory window is closed, it is removed from the scene tree along with all its children.  

InventoryEngine (singleton)  

    public event EventHandler<int> RefreshContainerWindow;

InventoryWindow (node)    

    _inventoryEngine.RefreshContainerWindow += On_InventoryEngine_RefreshContainerWindow;   

InventoryWindow (node)

        _inventoryEngine.RefreshContainerWindow += On_InventoryEngine_RefreshContainerWindow;

I was having weird issues with my inventory windows not refreshing when dragging and dropping items.  It took me a long while to realize that events were firing from nodes that were no longer in the scene.  This was because I was not using Godot Signals, but rather ordinary C# event handlers.  According to the documentation, Godot handles the Godot Signals for disposed objects, but it does NOT handle any C# event listeners.  To fix this, I had to unsubscribe those subscribers in the InventoryWindow code behind before deleting the window node.  This is done in the _ExitTree() method.

public override void _ExitTree()
{
    _inventoryEngine.RefreshContainerWindow -= On_InventoryEngine_RefreshContainerWindow;          
    base._ExitTree();
}

I hope this helps someone else who may be pulling their hair out trying to track down this kind of issue.

Visual Studio Tip:  Don't forget to point your debug to the new DLL when upgrading Godot.

Here is another gotcha for Visual Studio.  When you upgrade your Godot version, Visual Studio => {project name}Debug Properties => Path to the executable, and click Browse to locate the new Godot executable.  This will allow debugging in the version you have upgraded to.

That's all for now.  I hope you have learned something useful.

Until next time...

I'm Bound2bCoding