State of the Game, #2

In this week's State of the Game, we go over Bus Game's freshly overhauled inventory system.

State of the Game, #2
An early, irrelevant screenshot from Bus Game, prototype 9
📅
Week 26, 2024

Introduction

Much of the work this week was focused on overhauling the MKG Inventory System, which is a framework we built to simplify the addition of inventory and item handling in our games. The pre-overhaul solution was largely built on code that was originally written 2+ years ago, and was severely lacking in functionality and had many usability issues from a game design perspective.

Along with the framework overhaul, the actual implementation of inventory and item handling in Bus Game was updated to not be dependent on a single, giant, 700 line of code script that handled literally everything from tracking picked up items, to handling the pose animations of held items, to controlling the player's hand-item grip inverse kinematics, and more.

The Inventory Framework Overhaul

Most of the work done on Bus Game's inventory system this week is largely invisible to the actual player. The system functions in much the same way it did before, but is now better organized at the data level, and will be easier to expand upon and integrate with in the future.

Data Structures (boring nerd stuff)

Our overhauled inventory framework is based around a generic Unity ScriptableObject class, with three generic parameters representing:

i) the in-scene instances of items being stored,

ii) the data of items being stored, and

iii) the inventory slots that items are stored in

Each of these generic parameters require the implementation of a corresponding interface that the system uses to function correctly. Item instances must implement IRecordable<IRecord>, which makes it possible to create records (which must implement IRecord) that the inventory uses to store data about items being stored.

Finally, item records are inserted into inventory slots (which implement ISlot), which are used to organize collections of items in the inventory. It's up to the implementer of the system to decide on how they want to organize items - in the case of Bus Game, items are organized into 'stacks,' where each inventory slot holds a certain amount of identical items before spilling over into a new slot. This allows for easier inventory navigation, even when the player has picked up a lot of items, so long as the items are not all unique.

Each item instance in a scene that has been added to an inventory will have a reference to a record in the inventory, but records in the inventory won't have direct references to item instances. This should make it easier to serialize entire player inventories when I eventually get around to building a proper save/load system (that'll be an interesting post), without worrying about how to serialize 'physical,' in-scene objects.

With this overhaul complete, the next step for the inventory system is to implement a server-authoritative transactions system, where each item pick-up or drop is verified with the server to ensure players aren't cheating.

Part of that inventory transaction system will be the ability to sync inventory data across the multiplayer session to any selection of clients, which opens up some interesting new functionality. For example, being able to spawn (cheat) in items as an admin, or - and this isn't a planned feature at this time - the ability to "take over" the character of a player that disconnected from the session.

Changing to a ScriptableObject as the container for inventory data will also allow for having preset default inventories for Epochs or Levels, where players can spawn with a set of items that'd be useful for whatever experience they're starting. That particular feature will be implemented at a later date, when the Bus Game SDK is in a better position to support it.

Item Posing & IK Grips

In addition to fixing up the core of the inventory system, I've temporarily removed the code responsible for driving the player's pose based on item state, and for the inverse kinematics used to make the player character "grip" held items. Those two features were quickly bodged into the pre-overhaul inventory system as a sort of tech demo, and realistically, they need some reworking before they're truly ready.

There's two main pain points I'd like to address in a rework of those features - item-driven player animations, and setting up of item grips.

  • While doing basic "animations" - like aiming down sights of a held weapon - is technically possible with the old solution, they'd look rigid and unnatural due to the player's character not changing its animations. In the rework, I'd like for some item poses to be able to apply "additive" animations that would alter the stance of the player, for example.
  • Setting up item grips in the editor was meant to be easy - just make a new transform, position it where the player's hand should grip the item, and ensure the rotation fits. Unfortunately, due to how IK targets work in Bus Game's IK system, the player's hand would rarely line up accurately with the item grip's position and rotation. We'll need a better solution for that.

Item posing and IK grips are not exactly priority features in my eyes at the moment, so I may put them off until the game is closer to a public demo release. We'll see.

Server-Authoritative Inventory Networking

Figuring out a way to ensure some amount of inventory 'security' took a lot of thinking and iteration, but I think I've settled on a solution that should work pretty well for the foreseeable future. Rather than giving clients full authority over their inventories and not even bothering to sync inventory states across the network (as was the case with the previous iteration of the system), inventory states and transactions are now mainly held and processed on the server and require approval to be synced to individual clients.

So, when the game starts by loading an Epoch, an InventoryManager script will create as many inventories as required - for all players and containers in the scene - on the server, and only sync them to clients when requested. When a client wants to add or remove items to or from their inventory, they need to send a transaction to the server, which will process it and either approve and apply the change, or reject it.

This approach does sound a little heavy-handed at times, but it makes item and inventory cheating significantly harder. And while it does add some milliseconds of latency to a client's inventory actions, that can easily be masked in such a way that the delay would only be noticed if the client is attempting an invalid action.

Conclusion

That was a lot of words; hopefully they were at least somewhat interesting. I'm very much still trying to figure out the voice and writing style I want to use for these posts, so any feedback would be greatly appreciated.

In this upcoming week, the plan is to complete the inventory overhaul and to start integrating it with other systems - mainly the Modular Vehicle Part System (MVPS). After that, there's some more work to be done on MVPS to make it fully network compatible.

Thanks for reading,

-RB