Inventory System by Opsive WIP

Duffer123

Member
@Sangemdoko - well, as you know, I am Mr Impatience, but I will do my best... ;)

In the meantime, really great news re the saving/loading. I am taking it the nested items thing is to also facilitate things like container items, socketting, attachments, affixes, that sort of a thing?

Anyway, look forward to your next update and any screenies or vids that can be spared of things in action or the demo in the coming weeks (I hope).
 

Sangemdoko

Active member
Hi @Duffer123,
Development is going smoothly. I decided to change how Items are created in code to allow more freedom for people who want to customize the item creation process.
We will now use a factory pattern. We haven't finalized the call but roughly it will be something around the lines of:

C#:
Item mySword = InventorySystemManager.Factory.CreatItem(swordItemDefinition)
This allows you to swap out the Factory by something custom in the case you would like to initialize the newly created items in some specific way.
I'm assuming most users won't need to but I think it's always good to have more control over the system.

Apart from that I am writing unit tests to make sure everything works as it should, while I am adding content to the demo scene.
There is still a lot to do in terms of the demo and the documentation.
I hope you can be little bit more patient.
 

Duffer123

Member
@Sangemdoko ,

Thanks for the update. I can certainly wait for this asset! But the updates on development help... ;)

Hoping you have bottomed-out that Item collections (as with Item containers or Items with sockets) save/load thing...
 

Sangemdoko

Active member


Hi Everyone,

We finally have something to show you and I hope you will like what you see. I know some of you have been waiting for an update for a while now and I appreciate your patience. For those who missed the last update I’ll give a quick recap.

The inventory system is a standalone asset, which means it is not part of UCC. That being said we made some changes to UCC to make a smooth integration between it and the inventory system. There will be information on how the integration works later this week.

The inventory system is a flexible and modular system which enables you to create a complex item structure in no time. You create your item definitions directly in the editor and organize them in categories. Then, thanks to the attribute system, all item definitions in the same category will have the same property types, which you can choose to inherit, override or modify from their parent ItemCategory or ItemDefinition.
In game you’ll use the ItemDefinitions to create the actual items which are usually stored in inventories. You’ll be able to spawn your items in the scene using ItemObjects which binds an item to a gameObject. You’ll also be able to bind the item attributes to any properties on a MonoBehaviour component sibling to the ItemObject.
The Inventory system also includes features such as currency, shops, crafting, saving/loading, nested items, and more.

With the recap out of the way lets get started with the showcase of the demo scene. Since there are a lot of things I want to show I will be breaking it in 5 parts, one for each day of the week. Here is what we will showcase in the coming days:

  1. The editor, the Inventory inspector and the demo main menu
  2. The shop, storage and save Menu
  3. The upgrade and crafting menu
  4. ItemObjects, Item pickups, usable ItemObjects, ItemBinder and more
  5. [EDIT] Quick look at the API (We will showcase the UCC integration later once it is presentable)

By the end of the week you will have a good understanding on how the inventory system works and you will be able to get started as soon as you get your hands on the asset.

We are still working on the demo and anything you see during this week may change before release. And if you find anything you would like to be improved on, do let us know.

Part 1 : The Editor, Inventory Inspector and Demo Main menu

In a previous post I went into extreme detail on how the editor works so this will be a brief explanation of how it works. The editor has a menu for each object type. ItemCategory, ItemDefinition, Crafting Category, Crafting Recipe, Currency. In each menu you will be able to create, edit and remove an object of that type. These objects are grouped in an object called the Inventory Database. The Inventory Database is loaded by the Inventory System Manager at the start of the game.

The attributes of an ItemCategory are inherited by all its children. The child ItemCategories inherit all attributes, while the child ItemDefinitions only inherit the ItemDefinition attributes. The Item attributes are inherited by the default Item of the ItemDefinition. A default item is the default item that will be created when an item is created from the ItemDefinition. The item can be created with different values as the default item and if it is set as mutable it can also change values at runtime.

Here is a quick tour of the Demos Database:

We took a lot of time on the editor to make the item creation process as fast as possible. Creating items is an iterative process and it is very important to go back and forth between the objects quickly. We made it so that by clicking the colored box next to the object name it will open the object page in the editor window. It is extremely useful to traverse nested objects or go to the ItemCategory which is the source of an attribute.

Once you set up the itemDefinitions in the editor there are a few ways to create their items. The easiest is to add an inventory component. An inventory component is split in multiple ItemCollections, which can all behave in different ways. The ItemCollections are used to organize the items. Lets see what it looks like for the player character:


As you saw we can add and remove items from an itemCollection. We can also change their attribute values directly in the inspector. Note that changes the values only for that unique item and not the value of the other items with the same itemDefinition.
The player character has one ItemCollection for its equipment and another for the rest of the items. As you saw the type of the ItemCollection was different for both. The Equipment ItemCollection is an ItemSlotCollection which restricts the type and amount of items that can be added to it. We’ll talk about how that works in detail when talking about the Equipper component.

And now I’m very excited to show you the demo that we have been working on for the past few months.


There is a lot to talk about here. First of all the art made by Sarah is great! You see the town portion of the demo. Each of the establishments are interactable and allows you to access menus such as shop, crafting, etc…

Then we opened the main menu to see the character inventory. We wanted an inventory UI that worked well with both controller/keyboard input and mouse. For this reason we designed our inventory UI in this way for the demo. Each game should have a unique UI and this will require you to write some custom code. That being said there is no reason you should create one scratch. Our UI system is extremely flexible and modular. You can easily remove, add or modify pieces of the UI components. We want to make UI customization as free as possible so that you can easily create your unique Inventory.

You may have noticed that the item blocks are different depending on the item Category. We actually have a prefab for each item box. And we spawn the prefab specific to the item using its category as a key.

The Item Boxes are components which are extremely easy to create. We offer a few out of the box and some custom ones were made for the demo. Here is how the WeaponItemBoxUI looks like in the inspector:

We have components to show the item icon, the item slots, change the background depending on the state of the item and one to show the attack attribute. Note that I am using an Attribute Name Binder which lets me easily bind an attribute name to a string property. It is quite useful to avoid making typos.
You will notice that the Item box does not have a Button or a selectable component. That’s because the ItemBox is not interactable. The way the grid works is that there are buttons in the grid that do not move and we replace the item boxes as children of those buttons.
 
Last edited:

Sangemdoko

Active member
When highlighting an itemBox we show the itemDescription. You may have noticed that the description shows some of the attributes under the items name. For example a Weapon shows its attack power while a consumable shows a healAmount.
Once again we map the Categories to prefabs. But this time we go one step further and map the attribute to a prefab:


When clicking on an item an item Action panel opens up. As you guessed it, we map itemCategories to Item Actions. Here is an example for a droppable item action

And here is an example for Equip Unequip Item Action.

The action type can be anything that derives the ItemAction class, so feel free to make your own.

I’m sure you noticed that the character stats were changing when equipping weapons and armor. This functionality is not part of the InventorySystem features, it is part of the demo. The Inventory System has a lot of features and we believe that the character scripts, including character stats should not be part of the Inventory system. You are free to use the demo scripts or inspire yourself from them to create your own character controller or stat system. We will talk a bit more about it when we will dive in equipping items to the character.

Lets see how the Inventory grid UI combines all of these components to create the main menu of the demo.

You can choose the grid size, a box drawer & grid event system, a ItemCategory tab control, An Item description panel. The categories item actions object we talked about above, an item action panel, a sort enum and a default Category in case no category tab is referenced.

The Item box drawer has the reference to the Categories Item boxes

while the ItemDescription has the reference to the categories attribute boxes.


The Grid event system is a component we added which lets us listen to events such as selecting, deselecting, clicking, moving in a direction, failing to move in a direction, etc…
It uses custom button scripts as the default unity buttons are not flexible enough for every use case.

I hope you enjoyed this quick peek into the inventory system demo. Tomorrow I’ll talk about the Shop, Storage and Save menu. We will see how to use the Inventory List UI which derives from the Inventory Grid UI and how the grid system can be used for other UIs such as the Save menu.

If you have any questions about the things I showed today, feel free to ask. If you have any questions on any of the features that will be showcased later this week please wait until it comes up as I may answer your questions in the post.
 

Duffer123

Member
@Sangemdoko , only just seen this and going to give it a luxurious read - sadly, made my week (and we are only on Monday..!)... ;)

[edit] it was great to see Item Actions in flow and get as sense of how they will work sitting on Item Categories.

Also, it does look like it should be relatively easy to re-purpose or re-design the inventory UIs because they are properly sumptuous in the first place.

Looking forward to seeing how you are dealing with item containers and socketing but much of a clue was in that slots attribute I suspect.
 
Last edited:

Sangemdoko

Active member
Part 2 : The Shop, Storage and Save Menu
In our inventory system we use the Inventory Component whenever we need a group of items in the scene. That means we do not only use it for the characters, we also use it for things like the shop or storage.



Let's start with storage since it is very simple.
In the example above we display two inventories, the character inventory on the left and the storgat inventory on the right. Since the storage uses an inventory we can add items to it directly in the inspector before the game starts. When selecting an item and quantity all we have to do is call:

C#:
m_CharacterInventory.MainItemCollection.GiveItem(
                    m_SelectedItemAmount.Item,
                    m_StorageInventory.MainItemCollection,
                    quantity);
This says that the character inventory should give the selected item and quantity from its default item collection to the storages default item collection.
If you were to have a different item Collection type for the storage inventory you could easily make a small character inventory with an item amount limit of X and have a big storage have a limit of Y.



The shop requires a bit more explanation since it is a bit more complex. Let’s first start with currency. As you saw in the previous post, Currency can be defined in the editor window. You can choose how much a currency is worth compared to another and the system will figure out the rest. You can also specify an overflow and fraction currency so that a Currency collection can automatically convert currency amounts for you.

In the image above we see that 1 silver is equivalent to 100 bronze and 0.01 Gold. We can also see the Overflow and Fraction currencies in the relationship window.

So how do we give a price to an item? That is fairly easy thanks to the attribute system. Simply add an attribute and select the CurrencyAmounts type.

In the demo we use an ItemCategory called shoppable which lets us know what item can be bought and sold. We have two attributes BuyPrice and SellPrice, you can have a single item price instead, it is completely up to you.

In the demo most of the items inherit the Shopable attributes since it is a parent of “Normal Item”. In the image above you can see that Shopable has quite a few Descendant categories.

Once the items have a price we can specify the name of the attribute we just created in the Shop inspector:


As you can see we give it an inventory with the items that will be sold by the shop and we can even set a buy and sell modifier to raise or lower prices.

With the items and shop set up correctly, the next step is the shop UI. To change things we will use an inventory List instead of a Grid. The only difference is that you can specify a scrollbar.

It hardly gets easier than that. Of course you’ll need a different set of item boxes to display well in a list instead of a grid. In addition, the shop item boxes will need a way to display the price. The catch is that we cannot display the item price directly, we need to display the price after taking into account the shop buy sell modifier.

That’s where the box drawer events come in. We have an event before an item box is drawn and another after. By listening to the after draw event we are able to get the item box with a reference to the item within the shop ui. Then it is a simple question of getting the Currency UI from that item box and displaying the items modified price.

To make it very clear, in the demo we show the modifier value in the top left of the shop menu. We also display the base item price in the item description. Have a look:



The shop component is very extensible. There are a few things you can do to make the shop component your own. You could make the shop remove its items when they are sold, instead of giving the player duplicates. You could add the items the player is selling to the shop inventory.
You can even have a buy and sell price specific to each character, for example you could have an equipment that reduces price in a shop when equipped. Putting these ideas in practice would require a bit of custom code but they could really make your games stand out as unique.

One of the features people have been requesting a lot is saving and loading. We made a very simple saving system that is extremely extensible. There are three components to it. The save data which you will save and load from disk, the savers component which says how some data will be serialized and deserialized and finally the save manager, which uses all the saver components in the scene to create the save data or split the save data and send them to the appropriate saver component.

So saving an inventory or a CurrencyHolder is as simple as adding a sibling component called Inventory Saver and CurrencyHolder Saver respectively.

You can add any number of save components to save all the components that need saving/loading. You can even set if it automatically saves when the application quits or load when the game starts.

You are free to make your own saver components in the case you would like to save things unrelated to the inventory system. The saver system uses the Opsive Serialization class which can serialize and deserialize most classes.

With how the demo is set up we can save and load the characters inventory with equipment and the storage. We could just as easily save the chests inventory and anything else really.

Here is the save load in action.

You may have noticed a sword which was a little different in one of the loaded save files. That was an upgraded sword. The way upgrades work in the demo is that we add “upgrade” or “rune” items to a weapon or armor. This increases its stats in some way. And as you saw the upgraded weapon was saved/loaded with nested items without issues.

That is all for today. I hope you enjoyed what you saw today. Tomorrow we will dive into crafting and item upgrades. These subjects can be quite complex and you’ll see that we give you solutions for simple cases, but the real strength comes from extending the systems we put in place to create something really unique.
 
Last edited by a moderator:

RedHawk

Member
@Sangemdoko @Justin Wow, 9 pages... you need to create a section on the Forum for only Inventory.. and one that just has your Features where no one can comment... I had to scroll to page 9 just to see the cool updates you posted on Twitter...Post 169, post 170, post 174... please for my sanity and tracking this awesome project! :)
 

Sangemdoko

Active member
Hi @RedHawk , thanks for the advice. As we get closer to the release day I will write up a full guide and the documentation. I will recompile all the features in the asset and explain how to use them in detail, using the demo as example.
For now bare with us writing the updates in the forum, it lets us get feedback asap so that we can take it into account.
 

Sangemdoko

Active member

Part 3 : Upgrades and Crafting

Item upgrades is something that was requested a lot. In some inventory solutions Item Definitions and Items are the same object, which means you may not have two similar item instances with different states and make that state permanent. With that kind of system the only way to create item upgrades is by making a new ItemDefinition. Our Inventory System is more flexible in the sense that there is a clear distinction between item and ItemDefinition. In addition an item can be defined as “mutable”, which lets you change its attribute values at runtime. This means we can change the item “state” at any time, allowing for interesting game mechanics such as breakable items with durability or item upgrades.

There are many ways to implement item upgrades. In the demo we use a custom system that computes attack or defense values using equations which take the baseValue and the upgrade item attribute values into account. You are free to explore your own solution as it is really specific to each game.
Whatever solution you choose it is important to make upgrades consistent and predictable.
Good: Attack = BaseAttack + upgradeAttack
Bad: Attack += upgradeAttack
The reason the second statement is bad is due to floating point averaging. If you were to add/remove an upgrade from an item hundreds of times, you could end up with an incredibly high or low attack value (This sort of thing happened in some retail games)
The key point to remember out of this is to always recompute the values of an item from scratch each time it is upgraded.

Let’s dive in the demo example. First of all we decided on the item attributes which made sense for us. We have two Item Categories. “Upgradable Item” & “Upgrade Item”, where the upgrade item is used to upgrade the upgradable item. The upgradable item category has two attributes. One Item Definition attribute “SlotCount” and a “Slots” Item Attribute, where slots is an array of item amounts. This means we will be able to attach items to the upgradable item.


For the upgrade Item we chose to have a few ItemDefinition attributes. “Prefix”, “Suffix”, “BoostConstant” and “BoostMultiplier”. You can see them in the image below with the green blocks.
(Quick note: The color of the first box is the category source, the second is the inherited attribute, which could be a category, a definition or a definition's default item. ‘S’ stands for source, ‘C’ for Category, ‘D’ for definition ‘I’ for default item)


There are two examples of Upgradable Items in the demo. Weapons and Armor. As mentioned at the beginning we want to make the item upgrade values predictable. So for Weapon we have two attributes. As an ItemDefinition attribute we have an “BaseAttack” and as an ItemAttribute we have “Attack”. We only want to compute the Attack value whenever it is upgraded. That means the Attack value needs to be set the same value as “BaseAttack'' in the editor. Setting base attack and attack to the same value for each default item would be a lot of repetitive, tedious work. That’s where the “Modify” attribute variant comes in.

The modify attribute variant lets you use a regex expression to define the value of an attribute. You can use attributes within the expression and it will use the attribute of that name within the scope specified. The inherit attribute variant will inherit the expression, not the value, therefore you can write the expression once and all children can use it in their own scope. You can use $[myAttribute] to use the value of the attribute in the scope of the object that has the expression. This allows us to automate a lot of item attribute value dependencies.
As you can imagine, solving a regex expression creates a lot of garbage, so we decided to add a pre-evaluate toggle which evaluates the attribute in the editor (or in game if you call Reevaluate) and saves the value in the attribute. This way you have the flexibility of using the modify variant without any of the drawbacks. Of course if one of the attributes in the expression is changed at runtime you will need to reevaluate the expression yourself as there is no way for the modify attribute to know.

Coming back to the Weapon, the “Attack” attribute is defined as such:


And to show that we no longer need to write the expression for any of the children of weapons, here is the Great Sword Item Definition, which uses the Sword(Weapon) Item Category.

We can see that the inherit value is 5 which is the Sword “Attack” attribute value, yet the actual value is 10, which is the “BaseAttack” value of Great Sword. And that’s because we inherit the modify expression, not the inherit value... if it exists of course. Pretty neat right!
For the Armor category we use “BaseDefense” and “Defense”

With the attributes set up we made a custom menu just for the demo, which lets us attach the upgrade items to the slots of an upgradable item, recomputing the attack or defense attributes.
Of course this requires custom code. Here is a code snip to recompute the attribute values and change the item name

C#:
protected virtual void ApplyUpgradeStatChanges()
        {
            m_UpgradeableItemAmount.Item.name = GetFullItemWithSlotsName(m_UpgradeableItemAmount.Item, m_Slots);
           
            int constBoost = 0;
            float multBoost = 0;
            for (int i = 0; i < m_Slots.Count; i++) {
                constBoost += m_Slots[i].Item?.GetAttribute<Attribute<int>>("BoostConstant")?.GetValue() ?? 0;
                multBoost += m_Slots[i].Item?.GetAttribute<Attribute<float>>("BoostMultiplier")?.GetValue() ?? 0;
            }
           
            var attackAttribute = m_UpgradeableItemAmount.Item.GetAttribute<Attribute<int>>("Attack");
            if (attackAttribute != null) {
                var baseAttack = m_UpgradeableItemAmount.Item.GetAttribute<Attribute<int>>("BaseAttack").GetValue();
               
                attackAttribute.SetOverrideValue(baseAttack+constBoost+(int)(baseAttack*multBoost));
            }
           
            var defenceAttribute = m_UpgradeableItemAmount.Item.GetAttribute<Attribute<int>>("Defence");
            if (defenceAttribute != null) {
                var baseDefence = m_UpgradeableItemAmount.Item.GetAttribute<Attribute<int>>("BaseDefence").GetValue();
               
                defenceAttribute.SetOverrideValue(baseDefence+constBoost+(int)(baseDefence*multBoost));
            }
        }
       
        public static string GetFullItemWithSlotsName(Item item, ItemAmounts itemSlots)
        {

            if (itemSlots == null || itemSlots.Count == 0) { return item.ItemDefinition.name; }
               
            string prefix = "";
            string suffix = "";
            var count = 0;
            for (int i = 0; i < itemSlots.Count; i++) {
                if (itemSlots[i].Item == null) { continue;}

                count++;
                var itemPrefix = itemSlots[i].Item.GetAttribute<Attribute<string>>(DemoInventoryDatabaseNames.UpgradeItem.prefix).GetValue();
                if (!string.IsNullOrWhiteSpace(itemPrefix)) {
                    prefix += itemPrefix + " ";
                }
                var itemSuffix = itemSlots[i].Item.GetAttribute<Attribute<string>>(DemoInventoryDatabaseNames.UpgradeItem.suffix).GetValue();
                if (!string.IsNullOrWhiteSpace(itemSuffix)) {
                    suffix += itemSuffix + " ";
                }
            }

            if (count == 0) { return item.ItemDefinition.name;}

            return $"{prefix} {item.ItemDefinition.name} {suffix} (+{count})";
        }
And this is what it looks like in action:

https://streamable.com/dxu0y

This approach to item upgrades requires a bit of custom code but it opens up a lot of possibilities. If you are looking for a simpler option the crafting system comes with a simple and intuitive system out of the box.
 

Sangemdoko

Active member
The crafting system is composed of Crafting Categories, Recipes and Processors. Crafting categories are used to organize your recipes and also to specify the recipe type. The recipes have a list of ingredients and a default output result. The crafting processor uses the recipe as guidelines and takes the item ingredients from an inventory (usually the characters inventory).

Crafting Processors are the brain of the crafting system and can be extended to do all kinds of things. For example, use the character level to affect the crafting quality, or give more items if you had a good score in a crafting mini game, etc.. Out of the box though, the crafting processor will simply remove the recipe ingredients of the inventory and replace them by the crafting result. That allows people to get into crafting right out of the gate without writing a line of code.

The first step is to create a Crafting category, which specifies the recipe Type. In the demo we use the two recipe types that come out of the box. CraftingRecipe and CraftingRecipeWithCurrency

With the crafting categories set up we can define our crafting recipes. We can set the ingredients and outputs within the editor. We added a nice display of ingredients and outputs to see the recipes contents in one glance.

In the screenshot above you’ll see that there is an “Others” tab. That is available so that you can extend the Crafting Recipe class and set your custom fields. It is essential to give you full customization on the crafting system.

Once the recipes are set up, In the Inspector I can set a list of Crafting categories that will tell the UI which Recipes should be available. We could split the recipes in tiers so that you can introduce the player with simple recipes at first and only show complex ones later. Or maybe you can keep track of the recipes the character has learned and use that in the UI. It is completely up to you.

Here is how the crafting menu looks in the demo

https://streamable.com/07460

The crafting processor checks whether the item can be crafted and the UI displays that information by showing the ingredient amounts in green or red. Simple but effective.

I hope you enjoyed what you saw today. These features are extremely powerful and can be used to create very interesting things. For example, I could see people creating a custom crafting recipe and crafting processor, which would let you define a slot index for each ingredient and use that to make a minecraft style crafting system. Or make a crafting mini game that affects the quality or the number of crafted items.

Tomorrow we will look into making the items appear in the scene whether it is as a pickup or an equipment piece attached to the character. To do so we use ItemObjects, Item pickups, usable ItemObjects, ItemBinder and more.
 
Top