In the previous post, I covered the implementation of the back-end Java server work for a basic inventory system.
Therefore in this chapter, we will be looking at the steps required to integrate it in Unreal Engine.
Here’s a video going through the high level implementation as well as a demo of the results.
Also I uploaded the example project used here.
Unlike some of the other posts, I tried to make everything in Blueprints this time round, incase this is easier for people. It does however rely on free plugin called VaRest.
This video is not focusing on the aesthetics, but rather the data flow between systems and storage. Doing so, the approach is generic and can be tweaked by people as required.
Overview
Let’s begin with what components do we need to work on and why
- Inventory Widget
- Inventory Item Widget
- Game Mode changes
- Player character changes
- Utilities and helpers
The inventory widget should be self explanatory. We want to create a widget component which will contain a dynamic number of inventory slots
which can be populated with items or remain blank.
The Inventory Item widget is a small component which represents the slot for an item in an inventory. You’re able to give it functionality in future, such as right click equip, drop etc. In this example we will just drop
the item on click event.
Game mode changes will have us store some world information here – i.e. dropped items on ground floor. We can store it on player controller blueprint, but I think it may be better inside game mode as it may allow easier interactions if other NPCs or mobs were to interact with those world items.
Player character changes – this will refer to us opening inventory
which is tiny piece and also capability to pick up
nearby items.
Utilities and helpers are blueprints to help with passing data between blueprints and serialize data.
Let’s cover each topic one by one.
Inventory Slot Widget
As you can see, I made this as a tiny 60x60
widget component. It contains a SizeBox, Overlay, Iamge Component, Text component and Button
.
At this time, mainly just the image component and the button is utilised.
Let’s have a look at the construct for this component, to see how we populate the image for the item.
I added one extra bit to the response of item, which is the Item Config
. It contains two pieces of information, only one of which I am utilizing right now – icon
. Essentially when I create a ‘item’ component in my server, I specify the location of the image I want to use.
I can evaluate this in other ways, for instance using a Map in a helper file and getting the key using Item ID
– we will look at this when we spawn actor.
So now if we have file path for the icon, we can simply import it using the file name and apply the image to the image component.
Dropping the item
The next piece of code that we look at is ‘on button clicked’ – we want to drop the item when the user clicks this button.
First, let’s recall the Postman request that we made in the previous post.
This gives us the Json structure that we want to create.
The most complicated part of that is getting the character location, which is not too hard – we can GetPlayerCharacter
and with that, GetActorLocation
. This gives us the vector location which we can break and insert into the location structure. The Map
field is not used currently, so we can keep the consistent map1
that we were testing with.
See the blueprint implementation below, which uses VaRest plugin for handling the API calls.
Inventory Widget
The next thing that we’ll look at is the inventory widget.
This is a very simple component which has a canvas panel just around the right corner for an inventory.
It has a image field – it allows you to put whatever design you like for the background of this inventory system. I just tinted the color slightly for it to be visible.
The next piece is a Uniform Grid Panel
which is an important component.
My initial inventory design was to have a simple 10×10 inventory (extendable), so I was looking for square design. Consider making dynamic as you like (e.g. scrollable etc.).
The inventory item components are added dynamically, hence you don’t see them here in this design. This allows it to be extensible.
Inventory Blueprints
The blueprints are relaively large so I will split them into smaller sections and describe their intentions.
In this part, we create a timer to fetch user inventory. I create an event which will not be this fast in production. The 0.5 seconds is only for demonstration purposes. i.e. it can be set to something like 10 seconds for re-consolidation purposes (incase it goes wrong somewhere else).
In reality, you should have this event Get Inventory Event
called whenever there was an inventory action, e.g. item picked up, consumed, etc. This way you can greatly reduce load to server.
Now looking at the actual request, here’s the postman request that we wanted to mimick:
The request itself is straightforward and easy – no url parameter, just adding the characterName
to header.
On the other hand, the response is complicated, but we will abstract this into a helper method to keep our blueprints more clean. We will cover this part in chapter below.
The next connecting part of this blueprint is:
Here, we get the array field using VaRest function – this is in format of VaRest Json Object
which we cannot directly work with. Therefore we want to iterate over this and convert it to CharacterItem
. This function will be implemented in one of our helper methods.
You will also notice that when i am collecting, I am adding this to a CharacterItemsMapTemp
. This is a local variable containing the CharacterItems mapped by their location
as the key. This is quite unusual, but it will boost our performance.
Notice that the variable type is set to Location2D Structure
and Character Item Structure
. We will generate those soon.
The next part of blueprint is complicated – to be honest it wouldn’t look so complicated in code, but there’s a lot of small functions required from blueprints which make it appear inflated.
First let’s describe the intention behind this, then we’ll look at the code.
The intention is to receive a list of items and only update the delta of those items. i.e. you don’t want to destroy all items in inventory and reconstruct. You want to evaluate what’s the difference between what you’ve received and what you have and action only the diff.
For example from last state, your inventory items had:
- Item A
- Item B
in the new response you got
- Item A
- Item C
Therefore you know that:
- don’t touch Item A
- delete Item B
- add Item C
Let’s talk about what its doing in pseudocode.
- we just populated
CharacterItemsMapTemp
so we iterate over each key in this map - for each key, check if the
CharacterItemsMap
contains this entry - if it exists in
CharacterItemsMap
– remove it from there (don’t touch it in UI basically) - if it doesn’t exist in that list, create inventory slot widget for it
- populate the
inventory slot widget
with data required by constructor - add this widget reference to a
widget list
variable, mapped by the key from 2nd step - add this widget to uniform grid, extract the
row
andcolumn
info from item server data - When you complete 2nd step loop, iterate over the remainder
Character Items Map
- This list should have only items which were not present in response – they need to be deleted
- For each one, find the item in the
widget list
added in step 6, and remove from parent.
That’s it for the inventory widget, let’s look at the models and helpers that we needed to use in the previous steps.
Models and Helpers
In this part we will cover the models and helpers required by our blueprints. Generally its to share data between the blueprints and extract them from the VaRest (or other API) plugins.
These models are simple structures, so to create one, right click -> Blueprints -> Structure
.
These structures are just reflections of the models we used in our Java server code – so just have them as the same as your requirements if you’ve got different data.
The structure in UE can contain basic values, or other structures (nested).
So for example, we can see tags
are another user defined structure, which is array. This mimics the image above List<Tag>
field in Java.
And the Tag
structure in UE is just:
As you can see its simple the name/value String pair.
So just create all the models that you expect to use. For each one, we’ll have to create serializes
or translators
essentially from JSON -> Struct and vice versa in some cases.
That’s covered by our Helpers
:
In order to create one of these right click -> Blueprints -> Blueprint Function Library
.
Here’s the example for VaRestToStruct
looking at probably the most complex one, Convert to Item
function.
As you can see, you have to define an Input
and Outputs
for the function. All of the inputs for these function will be Va Rest Json Object
fields. The outputs will be whatever you’re translating, so in this case Item Structure
.
With the Va Rest Json Object
you can do things like Get String Field
, Get Array Field
, Get Number Field
etc. This is basically like any other Json parser. Generally speaking you will use JSON almost always with networking so if you’re not familiar with them, you should check some tutorials on them, they will be super useful in future.
Inside this function you’re also able to nest other calls, for example you can see we have Get Array Field
with Field name: "tags"
. We translate this using Convert to tag
which has the blueprints of:
Struct to VA Rest
This function library will help us take IN the structure and convert it to URL string param, or Va Rest (JSON) body for the API calls.
For example, in order to call Get Nearby Items
we have a GET request with url encoded location
parameter. Therefore we want this as a string in form of ?map=<map>&x=<x>&y=<y>&z=<z>
.
That’s what this function achieves – it takes in Location
structure as input and provides a encoded String output.
The second function is a bit more complex, we’re constructing the Drop Request
object.
For reference here’s what we’re trying to create in postman:
Here’s the blueprints of how we achieve this:
And above is how we can convert the structures into the JSON objects. Simply going field by field and populating the desired values.
Item Actor Resolver
In order to drop an item and see it, we need to evaluate which class to draw on the map. That’s what the Item Actor Resolver
is going to be used as. Before that though, we should create some actors for our objects.
In order to keep all assets generic and free, I used Craft Resources Icons pack and the Infinity Blade Weapons pack.
Obviously the icon will NOT match the blade pack, but you can use your own ones as desired – we’re focusing on the approach rather than aesthetics here.
For the mesh, I decided to go with SK_Blunt_Cinderblock
mesh – feel free to use whichever mesh you like.
Now you can right click in a desired folder (I chose Inventory/ItemActors
) click to create Blueprint Class
and select Actor
.
With the new class add a new component for skeletal mesh
and add the mesh for the chosen item, highlighted in the image. Then just rotate and position the item as you like.
Of course you can make additional modifications here as you like, for instance making it a spinning component. But we will keep things basic for now.
With this actor item created, you can generate the item actor resolver. This class will determine what actor to spawn on map depending on the item ID (or another field of choice, but should be unique).
As you can see, this function is very simple. It takes in an Item ID and checks the Map To Actor
Map. You need to populate the default values here, so for each ID of item that you have, select the Actor reference.
Note the variable type here, its a Map where the key is String and the value is Actor class reference.
Game Mode changes – populate dropped items on map
As described in initial summary, we will populate the dropped items on map using the Game Mode
.
It doesn’t have to be here, it can live in the Character Controller
for example, so feel free to move as you like.
Here we see that we have a timer, every 0.5 seconds we check what items are near to our character. We do this by creating a GET
request, getting the players Actor location
and generating a Location Structure
using this data. We then use the Location to String
helper function created in our Helper methods.
We use a similar technique to our inventory widget of keeping a Map of nearby items. We have a temp
map to keep those values for processing. After we populate the temp map
, we call our Draw Dropped Items On Map
event.
In this blueprint, we iterate over each of the temp items, check t o see whether we need to add or remove this item from screen.
If we need to add it to screen, we get the location of the dropped item and use the Resolve Item Actor
method we covered in chapter above to get the class
of the object we want to spawn.
We also keep track of the spawned items using the SpawnedItemsOnMap
variable.
If we need to destroy the object, we locate it from the SpawnedItemsOnMap
and call the DrestroyActor
method.
We then set the NearbyItems
to the temp
that we received from the response.
The blueprints make it look quite complicated and to be fair it can be tricky to get your head around, but when you break down the logic its not so bad.
Again its more efficient doing it this way as you don’t want to make UE re-render all objects for each request, as that would take up a lot of processing power.
One key thing is that you should set NearbyItems
to public variable so that your character controller can access it, we will use it in next step.
Character controller – pick up items and open inventory
We’ll start off with the easy one, display the inventory:
There’s multiple ways of doing this (you could map an engine input for instance) but here’s a simple way of opening/closing inventory on button I
press.
Simply set the visibility of your widget from Visible
<-> Hidden
.
Next, we will want to create a function of finding nearby item to player
In this function (add new function using the UI on left side) we get the game mode and get the Nearby Items
that we made public in the last chapter.
We just want to return 1 item, so we just check the length of this array, if greater than 0, it means we found
the item and simply get the 0th
index of that array and return it.
For next part, I created clean new graph on the character controller for the pickup item
logic.
I made the logic such that when you press the space
key, we get the nearby item id and if an item is found, we prepare a request to pick it up.
Here’s the postman example for that request:
As you can see, the request object is simply the droppedItemId
.
We don’t actually need to do anything with the response as our inventory module is set to self refresh however, you should make a callback to inventory module here to manually refresh after getting a response from this API. It would make this more efficient.
Summary
That’s it! You’re now able to walk around the map as well as:
- open inventory with dynamic slots
- pickup items
- drop items
- see them populated on the map as actors
What optimizations can we make?
For the API to get items around the map, we call this method quite regular, 0.5 seconds each time.
This makes it appear smoother. For such a high update request, its better to setup a socket connection instead of standard HTTP request.
In future we will look at those and potentially explore equipping items
or consuming them etc.
Best of luck!