Advertisement

Handling per instance logic in inventory system - unreal engine 4

Started by July 17, 2022 02:09 AM
8 comments, last by aljav 2 years, 4 months ago

Hello, first time posting here.

I am prototyping an inventory system using unreal engine 4.

The goal:
Player can pick up items in the game world. Some items have individual states or conditions that needs to be remembered.

The problem:
It probably is not feasible to have an actor spawned for every single item, but we still need to run logic on a per instance basis.

I have a PickupItems base class that holds data that covers most items. It also handles basic functions for stuff like OnPickup, etc.

Most items are defined only by static data that I can reference from a data table. I.E. if I grab a piece of wood board, it has some weight values and dimensions values that never change. So the only thing I need to pass around and store is an ID.

But if there is a subclass of PickupItems called FoodPickupItems, and this class has some logic for incrementing a “spoilage” integer - the problem is that this logic lives in the class. Then, in order to consistently update that spoilage integer, I'd have to keep the entire actor alive all the time?

Is this a case for using a manager class? It might track all instances of the FoodPickupItems class in the world and handle incrementing their spoilage counters. And it might also search the players inventory, look for inventory item slots containing a spoilage flag, and incrementing a variable held there as well?

Is there any programming pattern which solves problems like this I could study?

Thanks for any advice.

I`m not sure about the new approaches to build inventories but if we talk about the classical approach the easiest way to imagine RPG inventory operations is to think of it as a snap to grid system rather than free floating. I mean when your mouse is on top of the GUI inventory cell grid, the inventory item you`re holding with your mouse 'jumps' from a group of cells to the group of cells nearby, like there is no free floating, no item movement “smoothing” in between ‘presets’. Once you understand it in these terms you can add free floating too as ‘visual sugar’ that has no practical function other than helping you visually estimate things.

My project`s facebook page is “DreamLand Page”

Advertisement

I would approach this by having 3 different things: actor class for the actual visible item, data asset for all the static data and class for the state of an item.

The item state could be just derived from UObject and all the data that need to persist would be in it. That allows you to destroy the item actor from the world while keeping all the variables in this state object in the inventory. When you need to spawn the item from inventory, you would give it a reference to its state.

So each time you instantiate a new item, you would also create a default state object for it, which can then be stored into the inventory. When the item is picked up, you store the state object into the inventory and destroy the pickup. When you want to drop item from inventory, you would spawn an item actor (actor which represents the item in the world) and assign the state for it.

@Fullstag

brilliant!

I had a vague notion after time to think on this more about using uobjects but you've described it succinctly and in an easy-to-understand way. Now I have a good plan to go from, rather than thrutching through with confusing trial and error ?.
Thanks!

One follow up thought - suppose I want to have the ability to easily swap behaviors between different pickup_uobjects.
Like I have some “spoilage” behavior and “emits odor” behavior. Just through play testing iteration I decide that some behavior shouldn't be tied down to a class.

I can't put components onto a uobject, so I suppose that I could have a manager actor class that holds all of the uobjects in an array, also holds all available behavior components, each uobject can hold a flag for which components it should be subjected to.

In that case, when the player character either picks up an item to move into their inventory or drops an item which would instantiate it's actor class in the level, in both cases we only need an ID which would point back to the actual uobject held in the manager class?

Perhaps that is unnecessarily complicated… it's hard to know how much behavior swapping I might need to do and if setting up this compositional approach would be more/less work than just copy/paste some functions among classes.

You can solve almost all problems like this by adding a layer.

Fullstag and others have a right approach, add a layer and decouple the pieces.

You have a blob of information about that specific item, and the blob of data is pretty small. It's got the percentage used, bonuses, or whatever modifiers you need attached to it. You need an accessor that references the blob of data, either by returning the blob of data or by querying the attached blob of data and returning the results.

You can compose them in many different ways, with collections being quite common. This is how some games develop their buff/debuff system. The general type of object has some type of reference or pointer or other link to the collection of modifiers. When you put it in your inventory you store the link to the collection. When you have it in use, you store the link to the collection.

When it comes time to calculate damage or other effect, you pass in a collection of the object, the collection of the object user, and a collection of the target and let the system run the numbers. You may have a weapon with 45 points physical damage,19 points cold damage, 15 points poison damage, and 3 points magic damage, all of them as a collection of modifiers. You may have a weapon user who has 120 strength points, an amulet that adds 20 points plus 20% to cold damage, and a ring that adds 30 points plus 20% to magic damage. You may have a creature who is immune to cold and magic, and takes physical and poison damage at normal rates. The system has a function that takes all three collections (the wielder's collection, the object's collection, and the target's collection) and computes the actual damage that results. Someone can add a rune to the weapon and it adds another 5% cold damage, now instead of 4 entries in the collection it has 5 entries in the collection. The system will still be able to tally them up and compute the result.

Collections let you nest and group as much as designers can tolerate. A player has a sword with stats, the sword has been augmented with two embedded rings, the first ring has its own stats plus itself augmented with four runes… etc. The composite effect is not complex to compute, and usually only need be tallied when object attributes change.

Advertisement

@frob

thanks! i think i understand what you mean for the most part, though I am new to programming (actually i am only using unreal blueprints, so i'm not a “true” programmer.)

At this point I think i need to test a lot of this out so i can make sure i understand. I'll report back once i'm able to get some basic functionality going.

Blueprints do some things behind your back so you will definitely need to be careful here.

I wrote above that you need to pass around some type of reference, pointer, or similar link to the container. Blueprints have some rules that can be confusing to beginners about how they pass data around. Depending on details, the system makes fresh copies for many situations to avoid certain issues, especially USTRUCT objects. In this situation when you start modifying values in blueprints the changes in one copy it won't affect the other copy, they are each their own copy rather than a reference to a single shared instance. With a system like this you'll need to be careful that you're passing around references to the object that owns the container, since that reference remains shared. There are also plenty of operations in blueprints that will copy data out of containers, and your modifications will only affect your script's copy rather than the original shared source.

It still works as described, it's just a potential “gotcha” moment since Unreal has some details that you might not be expecting.

@frob

i have of a notion about the differences between copying/mirroring a reference and getting the actual reference in blueprints, but so far with the work I've done I never needed to get that low level. I'll make sure to test my save/load system and working with built projects early and often to make sure i don't have a big complex system with some fatal flaw like that somewhere in it. Thanks for the heads up!

This topic is closed to new replies.

Advertisement