While our game may be running without any issues in the editor or even in…
Implementing an Inventory System in C++
In this post, we’re going to implement a basic Inventory System in C++. Create a First Person C++ Project Template and make sure to have a cup of coffee nearby because this will be a lengthy tutorial!
Before we dive in to type our code, here is the end result:
Since our Inventory System includes glowing effects, inputs and user interactions we will breakdown the functionality in order to avoid confusion. This means that we will create our system using the following workflow:
- Create a minor aspect of the system
- Test its functionality
- Repeat
Eventually, by merging all the minor systems, our Inventory System will be working just like the video above.
Throughout the tutorial I’ve used some very basic Assets. You can download them from here.
Moreover, you can find all the source code of this tutorial here.
Setting up our Pickup Class and creating the Glowing Effect
The first thing we’re going to create is the Glowing Effect of our Pickups. When your First Person C++ Project Template loads up:
- Add a new C++ class based on the Actor class and name it Pickup.
- Inside the Pickup’s header file type in the following code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public: /*Enables/Disables the glow effect on the pickup*/ void SetGlowEffect(bool Status); /*Returns the Texture of our Pickup*/ FORCEINLINE UTexture2D* GetPickupTexture() { return PickupTexture; } protected: /*The Static Mesh of the pickup*/ UPROPERTY(VisibleAnywhere) UStaticMeshComponent* PickupSM; /*The Texture of the item in case we want to add it in the secrets or inventory*/ UPROPERTY(EditAnywhere, Category = "PickupProperties") UTexture2D* PickupTexture; /*The name of the item*/ UPROPERTY(EditAnywhere, Category = "PickupProperties") FString ItemName; |
1 2 3 4 |
//Initializing our properties PickupSM = CreateDefaultSubobject<UStaticMeshComponent>(FName("PickupSM")); PickupTexture = CreateDefaultSubobject<UTexture2D>(FName("ItemTexture")); |
1 2 3 4 |
void APickup::SetGlowEffect(bool Status) { PickupSM->SetRenderCustomDepth(Status); } |
The SetGlowEffect function will make sure to highlight the static mesh of our pickup. This will work when we setup our Post Process effect in the Editor.
Switch to your Editor and create some pickup classes. I’ve created 3 pickups each with a different static mesh and item texture. Everything is included in the .zip file at the start of the post.
Adding a Post Process effect inside our Level
Before we setup our Post Process we need to make sure we have the right material in order for our items to Glow. For my project, I’ve used the material named M_Highlight from 4.8 version of Content Examples of UE4. In case you don’t have this version, the material is included inside the ziped file at the start of the post.
To add the downloaded material inside your project, close your Editor and copy the file inside the Content folder of your project. Then open up your project and the material will be where you’ve copied it.
In case you’re working with 4.10.4 version of UE4 the First Person Map will already include a post process volume. If you’re working with another version you may need to place one yourself. When you have placed a Post Process volume, setup the following options:
To sum up:
- Select the Blendables option and add the material you’ve downloaded. (Make sure you select the Asset Reference option when prompted)
- Mark your Post Process volume as Unbound.
That’s it. We’re done with our Post Process. Let’s move on to our character setup in order to complete the Glowing effect!
Setting up our Character
Open up the source file of your character and add the following code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
private: /*Raycasts in front of the character to find usable items*/ void Raycast(); /*Reference to the last seen pickup item. Nullptr if none*/ APickup* LastItemSeen; public: virtual void BeginPlay() override; virtual void Tick(float DeltaSeconds) override; protected: /*The range of the raycast*/ UPROPERTY(EditAnywhere) float RaycastRange = 250.f; |
Don’t forget to add the “Pickup.h” include right before the .generated.h file.
Switch to your Character’s source file and type in the following code for the above functions:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
void AInventorySystemCharacter::Raycast() { //Calculating start and end location FVector StartLocation = FirstPersonCameraComponent->GetComponentLocation(); FVector EndLocation = StartLocation + (FirstPersonCameraComponent->GetForwardVector() * RaycastRange); FHitResult RaycastHit; //Raycast should ignore the character FCollisionQueryParams CQP; CQP.AddIgnoredActor(this); //Raycast GetWorld()->LineTraceSingleByChannel(RaycastHit, StartLocation, EndLocation, ECollisionChannel::ECC_WorldDynamic, CQP); APickup* Pickup = Cast<APickup>(RaycastHit.GetActor()); if (LastItemSeen && LastItemSeen != Pickup) { //If our character sees a different pickup then disable the glowing effect on the previous seen item LastItemSeen->SetGlowEffect(false); } if (Pickup) { //Enable the glow effect on the current item LastItemSeen = Pickup; Pickup->SetGlowEffect(true); }//Re-Initialize else LastItemSeen = nullptr; } void AInventorySystemCharacter::BeginPlay() { Super::BeginPlay(); //Initializing our reference LastItemSeen = nullptr; } void AInventorySystemCharacter::Tick(float DeltaSeconds) { Super::Tick(DeltaSeconds); //Raycast every frame Raycast(); } |
When you’re done with that, create a Blueprint based on your Pickup class and place it somewhere in your Level. Then, test if the glowing effect works as intented.
Setting up the pickup functionality
After setting up our glowing effect, it’s time to create the pickup functionality for our Character. Go to your Project Settings and add a new Action Mapping named Pickup. Then assign a keybind of your liking (I assigned the button E) like the following screenshot suggests:
Then, go to your Character’s header file again and add the following code:
1 2 3 4 5 6 7 |
/*Handles the Pickup Input*/ UFUNCTION() void PickupItem(); /*The actual Inventory*/ UPROPERTY(VisibleAnywhere) TArray<APickup*> Inventory; |
Moreover, right after your #includes add the following line:
1 |
#define MAX_INVENTORY_ITEMS 4 |
Since we will attach UMG to our Inventory later on, we need to create a somewhat hardcoded value for the total number of the items that a character can pick up. However, by defining a value and use that instead of a hardcoded value is somewhat more flexible for future use.
Then, inside your character’s begin play function, right after the initialization of the last seen item, type in:
1 2 |
//Initializing our Inventory Inventory.SetNum(MAX_INVENTORY_ITEMS); |
Moreover, go to the SetupPlayerInputComponent function and add the following mapping:
1 2 |
//Action mapping of pickup item InputComponent->BindAction("Pickup", IE_Pressed, this, &AInventorySystemCharacter::PickupItem); |
Then, implement the PickupItem function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
void AInventorySystemCharacter::PickupItem() { if (LastItemSeen) { //Find the first available slot int32 AvailableSlot = Inventory.Find(nullptr); if (AvailableSlot != INDEX_NONE) { //Add the item to the first valid slot we found Inventory[AvailableSlot] = LastItemSeen; //Destroy the item from the game LastItemSeen->Destroy(); } else GLog->Log("You can't carry any more items!"); } } |
In case the Inventory is full and there isn’t a nullptr Item inside our inventory the Inventory.Find function will return INDEX_NONE instead of the AvailableSlot. In this case, this means that the engine is telling you that your TArray of items doesn’t have an available slot for a new item.
Right now you have the pickup functionality of you inventory. Place a couple of pickups inside your map and test it and make sure their default static mesh/materials are different. The Inventory is exposed so you can test what’s happening inside your inventory from within the editor!
Setting up our Project for the UI logic
In order to create a functional inventory with it’s dedicated window we need to add UMG to our project. However, before doing that, we need:
- A Controller to communicate information between our Character and our UMG logic
- A GameMode which will tie our Character and our Controller together
- A User Widget responsible for the whole Inventory
- A User Widget responsible for each individual inventory item
Before adding any classes, go to your project’s header file (I named my project InventorySystem so I will select the “InventorySystem.h” file) and add the following includes:
1 2 3 4 5 |
#include "Runtime/UMG/Public/UMG.h" #include "Runtime/UMG/Public/UMGStyle.h" #include "Runtime/UMG/Public/Blueprint/UserWidget.h" #include "Runtime/UMG/Public/Slate/SObjectWidget.h" #include "Runtime/UMG/Public/IUMGModule.h" |
Moreoever, locate the [YourProjectName].Build.cs file and replace the following line from:
1 |
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" }); |
to:
1 |
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "UMG", "Slate", "SlateCore" }); |
Save and compile your project.
Then, switch to your editor and add the following C++ classes:
- A PlayerController (name it MyPlayerController)
- A UserWidget (name it InventoryWidget)
- A UserWidget (name it InventorySlotWidget)
When you’re done with that add a Blueprint Game Mode and named it BP_MyGameMode. In this case we need the game mode just to tie the controller and the character together. Since we won’t need any C++ for that we will add a blueprint instead.
After you have completed all the steps above, create a Blueprint which is based on the MyPlayerController (I named mine BP_PlayerCon). Then, open up the BP_MyGameMode and tie everything together like the following screenshot:
Now that you have completed this step, go to your map and inside the World Settings assign the BP_GameMode.
Setting up our Inventory Slot User Widget
Open up the InventorySlotWidget.h and add the following code:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
protected: /*Holds a reference to the item texture*/ UPROPERTY(VisibleAnywhere, BlueprintReadOnly) UTexture2D* ItemTexture; /*Tells the player the equip the represented item from this widget*/ UFUNCTION(BlueprintCallable, Category = UI) void SetEquippedItem(); public: /*Sets the item texture*/ UFUNCTION(BlueprintCallable, Category = UI) void SetItemTexture(APickup* Item); |
Switch to your source file and add the following implementations:
1 2 3 4 5 6 7 8 9 10 11 |
void UInventorySlotWidget::SetEquippedItem() { //Empty for now } void UInventorySlotWidget::SetItemTexture(APickup* Item) { //If the item is valid update the widget's texture. //If not, assign a null ptr to it so the widget won't broadcast wrong information to the player (Item) ? ItemTexture = Item->GetPickupTexture() : ItemTexture = nullptr; } |
Then, switch to your Editor and create a Bluprint based on the InventorySlotWidget and name it UW_InventorySlot. Open up the UMG editor and:
- Place a button which covers the whole area
- Inside the button place an image which covers the whole area
Here is the end result:
I know it looks like an abomination but please bear with me. After that Select the Image you’ve placed inside your button and in the Appearance menu, make a brush binding to the ItemTexture property we’ve created inside our C++. Here is an image to make things easier:
We’re done with our Inventory Slot Widget, for now at least!
Setting up our Inventory User Widget
Open up the InventoryWidget.h and add the following code:
1 2 3 4 5 6 7 8 9 10 11 12 |
public: /*Adds the widget into the viewport and populates the inventory slots*/ UFUNCTION(BlueprintImplementableEvent, Category = UI) void Show(); /*Removes the widget from the viewport*/ UFUNCTION(BlueprintImplementableEvent, Category = UI) void Hide(); /*Stores a reference in order to bind information on inventory slots*/ UPROPERTY(VisibleAnywhere, BlueprintReadOnly) TArray<APickup*> ItemsArray; |
Save and compile your code. After that, create a Blueprint based on the Inventory User Widget and name it UW_Inventory.
Open up the UW_Inventory and create the following UI:
Again, I know it doesn’t look like much, but bear with me!
Each InventorySlot is a UW_InventorySlot and has the Vertical and Horizontal Alignment set to Fill. Moreover, make sure you enter the following values for your inventory slots:
- All Rows set to 0
- For InventorySlot_2 set Column to 1
- For InventorySlot_3 set Column to 2
- For InventorySlot_4 set Column to 3
When you’re done with that, switch to your Event Graph and add the following logic:
We’re done with the Inventory Widget for now.
Go to your Editor and specify a new Action Mapping named Inventory.
Then, open up the header file of your character and add the following function:
1 2 3 |
/*Handles the Inventory Input by sending information to the controller*/ UFUNCTION() void HandleInventoryInput(); |
Then, switch to your source file and add the “MyPlayerController.h” file. Moreover, locate the SetupPlayerInputComponent and add the following line of code:
1 |
InputComponent->BindAction("Inventory", IE_Pressed, this, &AInventorySystemCharacter::HandleInventoryInput); |
When you’re done with that, add the following implementation of the HandleInventoryInput function:
1 2 3 4 5 |
void AInventorySystemCharacter::HandleInventoryInput() { AMyPlayerController* Con = Cast<AMyPlayerController>(GetController()); if (Con) Con->HandleInventoryInput(); } |
Don’t compile your code yet because we have yet to write the HandleInventoryInput function for our controller.
Open up your Controller’s header file and add the following code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
private: /*InventoryWidget reference*/ UInventoryWidget* InventoryWidgetRef; /*True if the inventory is currently open - false otherwise*/ bool bIsInventoryOpen; protected: /*InventoryWidget Blueprint reference*/ UPROPERTY(EditDefaultsOnly) TSubclassOf<UInventoryWidget> InventoryWidgetBP; public: virtual void Possess(APawn* InPawn) override; /*Opens or closes the inventory*/ void HandleInventoryInput(); |
Don’t forget to include the “InventoryWidget.h” file.
Then, switch to the Controller’s source file and add the following code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
void AMyPlayerController::Possess(APawn* InPawn) { Super::Possess(InPawn); if (InventoryWidgetBP) { //Create the Inventory Widget based on the Blueprint reference we will input from within the Editor InventoryWidgetRef = CreateWidget<UInventoryWidget>(this, InventoryWidgetBP); } //Initial value bIsInventoryOpen = false; } void AMyPlayerController::HandleInventoryInput() { AInventorySystemCharacter* Char = Cast<AInventorySystemCharacter>(GetPawn()); if (InventoryWidgetRef) { if (bIsInventoryOpen) { //Mark the inventory as closed bIsInventoryOpen = false; //Remove it from the viewport InventoryWidgetRef->RemoveFromViewport(); } else { //Mark the inventory as open bIsInventoryOpen = true; //Re-populate the ItemsArray InventoryWidgetRef->ItemsArray = Char->GetInventory(); //Show the inventory InventoryWidgetRef->Show(); } } } |
Don’t forget to:
- Add your Character’s file and
- Change the line 15 to match your character!
Save and compile your code!
Go to your Editor and open up the BP_PlayerCon and set up the UW_Inventory as a reference:
At this step we’re ready to test our code. Place some BP_Pickups inside your level and don’t forget to setup a texture for each and every one of them. Otherwise you will not be able to see your Pickup’s Texture!
Here is my end result after picking up 2 items at this step:
Since we have the base functionality we now need to update our code. In the next section we’re going to implement a Pause system so that the game won’t continue until you either close the inventory or select an item.
Setting up a pause state
In most games, when the player has an open inventory the game pauses. We would like to achieve this functionality. To create that, we need a bind that gets called even though the game is paused. In order to achieve that:
- Navigate to your Character .cpp file and locate the function SetupPlayerInputComponent
- And replace the Inventory action mapping:
1 |
InputComponent->BindAction("Inventory", IE_Pressed, this, &AInventorySystemCharacter::HandleInventoryInput); |
1 2 3 4 5 6 7 8 9 |
FInputActionBinding InventoryBinding; //We need this bind to execute on pause state InventoryBinding.bExecuteWhenPaused = true; InventoryBinding.ActionDelegate.BindDelegate(this, FName("HandleInventoryInput")); InventoryBinding.ActionName = FName("Inventory"); InventoryBinding.KeyEvent = IE_Pressed; //Binding the Inventory action InputComponent->AddActionBinding(InventoryBinding); |
- Pausing and unpausing the game
- Showing our Cursor
- Consume Inputs for our Inventory Slots Widgets
To do that, navigate to MyPlayerController.cpp and make the following changes in the HandleInventoryInput:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
void AMyPlayerController::HandleInventoryInput() { AInventorySystemCharacter* Char = Cast<AInventorySystemCharacter>(GetPawn()); if (InventoryWidgetRef) { if (bIsInventoryOpen) { //Mark the inventory as closed bIsInventoryOpen = false; //Remove it from the viewport InventoryWidgetRef->RemoveFromViewport(); //Hide the cursor of our game bShowMouseCursor = false; //Tell the game that we want to register inputs for our game only and not for our UI FInputModeGameOnly InputMode; SetInputMode(InputMode); //Unpause the game SetPause(false); } else { //Mark the inventory as open bIsInventoryOpen = true; //Re-populate the ItemsArray InventoryWidgetRef->ItemsArray = Char->GetInventory(); //Show the inventory InventoryWidgetRef->Show(); //Show the cursor of our game bShowMouseCursor = true; //Tell the game that we want to register inputs both for our game and UI FInputModeGameAndUI InputMode; SetInputMode(InputMode); //Pause the game SetPause(true); } } } |
With the above code we have achieved the Pause and unpause as well as showing and hiding the cursor of our game. You should compile, save and test your code at this state! Let’s move on and create the logic for our Inventory Slot Click status.
For this case, we won’t create a system that will make our Character actually equip our awesome cubes. However we will create a basic Equip functionality just for demonstration purposes.
Navigate to your Character’s header file and add the following code:
1 2 3 4 5 6 7 |
private: /*Reference to the currently equipped item*/ APickup* CurrentlyEquippedItem; public: /*Sets a new equipped item based on the given texture*/ void SetEquippedItem(UTexture2D* Texture); |
Then switch to the Character’s source file and add the following implementation for the SetEquippedItem function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
void AInventorySystemCharacter::SetEquippedItem(UTexture2D * Texture) { if (Texture) { //For this scenario we make the assumption that //every pickup has a unique texture. //So, in order to set the equipped item we just check every item //inside our Inventory. Once we find an item that has the same image as the //Texture image we're passing as a parameter we mark that item as CurrentlyEquipped. for (auto It = Inventory.CreateIterator(); It; It++) { if ((*It) && (*It)->GetPickupTexture() && (*It)->GetPickupTexture()->HasSameSourceArt(Texture)) { CurrentlyEquippedItem = *It; GLog->Log("I've set a new equipped item: " + CurrentlyEquippedItem->GetName()); break; } } } else GLog->Log("The Player has clicked an empty inventory slot"); } |
We’re done with tha Character. Locate the InventorySlotWidget.cpp file and add the following implementation of the SetEquippedItem function (remember that we’ve left that empty):
1 2 3 4 5 6 7 8 |
void UInventorySlotWidget::SetEquippedItem() { AInventorySystemCharacter* Char = Cast<AInventorySystemCharacter>(UGameplayStatics::GetPlayerCharacter(GetWorld(), 0)); if (Char) { Char->SetEquippedItem(ItemTexture); } } |
Don’t forget to include the Character.h file and replace my character’s class to fit your needs.
Save and compile your code. Then, locate the UW_InventorySlot Blueprint and select the Button we’ve added. From the details panel locate the OnPressed event and click the “+” Icon. Then, implement the following logic:
Save and compile your Blueprint. You now have an Equip functionality!
Creating the Drop Pickup Functionality
In this post we’re going to create a drop functionality. Specifically, the character will be able to drop the currently equipped item. Let’s start!
Add the following function inside the header file of the character:
1 2 3 |
/*Drops the currently equipped item*/ UFUNCTION() void DropEquippedItem(); |
Then, switch to your source file and add the following implementation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
void AInventorySystemCharacter::DropEquippedItem() { if (CurrentlyEquippedItem) { int32 IndexOfItem; if (Inventory.Find(CurrentlyEquippedItem, IndexOfItem)) { //The location of the drop FVector DropLocation = GetActorLocation() + (GetActorForwardVector() * 200); //Making a transform with default rotation and scale. Just setting up the location //that was calculated above FTransform Transform; Transform.SetLocation(DropLocation); //Default actor spawn parameters FActorSpawnParameters SpawnParams; //Spawning our pickup APickup* PickupToSpawn = GetWorld()->SpawnActor<APickup>(CurrentlyEquippedItem->GetClass(), Transform, SpawnParams); if (PickupToSpawn) { //Unreference the item we've just placed Inventory[IndexOfItem] = nullptr; } } } } |
Don’t forget to add:
- An Action Mapping using the Editor and its corresponding button
- The binding inside the SetupPlayerInputComponent that points to the above function!
If you’ve made it so far, I would like to thank you for reading my tutorial! Have fun!
In the function “DropEquippedItem(),”int32 IndexOfItemhe” haven’t be initialized,how it is works?
Hello, I’m not initializing the IndexOfItem variable because right after, I’m calling the Find function which exists in all TArrays.
This function searches your specified array for a given item (in this case the CurrentlyEquippedItem). If the item exists inside the array, the Find function will return true and will modify the second parameter (in this case IndexOfItem) to match the index of the found item. However, if it failed to locate the given item inside your TArray, it will return false.
Having said, since we’re using an if statement we have no need to initialize our variable because the Find function will take care of that.
-Orfeas
I see,thanks!
In this tutorial,your make a particular actor -“PickupActor”-for pick up.But in the real game world ,should we need let the “Pickupactor” to be the Baseclass of the whole actor or using interface ?
Yes. In fact, notice that we created only one class (the pickup class) and then we created different Blueprints derived from our pickup. This means that we used the pickup class as a Base class.
I would use the interface approach only if my game demanded it. For example, imagine that you’re building World of Warcraft. WoW has a vast variaty of items, like inventory items, collectable items, cosmetic items and so on. That said, in my opinion an interface approach would offer a more flexible solution.
-Orfeas
When i create a blueprint derived from the InventorySlotWidget C++ class, i open it up, but there is no designer tab… help?
That’s weird, can you provide a screenshot of your Blueprint?
-Orfeas
https://www.dropbox.com/home?preview=BP_2.png
For some reason I’m unable to see the image you’ve uploaded
-Orfeas
That one was directly from my personal Dropbox :p
https://www.dropbox.com/s/56va4hfppmf63l7/BP_2.png?dl=0
I had the same problem. This article has the solution:
https://wiki.unrealengine.com/Extend_UserWidget_for_UMG_Widgets
Rather than creating a blueprint derived from your custom C++ widget class you create a new widget blueprint (not derived from anything), open the widget blueprint, select class settings, then parent class.
Thanks so much for this!
Alex, I cannot reproduce your bug both in 4.11 and 4.12. Have you tried deleting and recreating a Blueprint based on your c++ user widget?
-Orfeas
Yes, several times… maybe the C++ class’ parent class could be Widget Component instead of User Widget?
You need to inherit the User Widget class since we’re not creating a widget component. Have you tried to reproduce your bug in an empty project?
-Orfeas
Yes, same result, no designer tab…
That’s strange, if you are not able to open up the designer tab at all in any projects, could it be that there’s something wrong with the installed version of the engine?
-Orfeas
I don’t think so. I can create a blueprint that is a child of UserWidget and the designer tab will show up… only when i derive it from a C++ class that is child of UserWidget does this not work…
I think you should open up a thread on answershub and describe your issue.
I understand that this is frustrating since you can’t complete the tutorial so I suggest (as a temporary workaround) to create a Blueprint user widget and transfer the logic of the widget to a Blueprint functions library which will be written in C++.
-Orfeas
Ok will do 😉 thanks for the support though, keep it up!
on “Setting up our Character” do we create a new “Character” C++ class?
and if so, im stuck on “FVector StartLocation = FirstPersonCameraComponent->GetComponentLocation();”
“”FirstPersonCameraComponent” is undefined”
I guess it can’t find the camera component to attach to
Anyone know how to fix this?
In this post we’re using the first person C++ template project, so you don’t need to create a new character class – modify the existing one
-Orfeas
I couldnt get past the Raycasting because appearantly the LineTraceSingleByChannel doesnt return true if I use the CollisionQueryParams for ignoring the character himself. If I don’t use them, it returns only the Character. Any help? (Working with 4.14)
Are you sure that you have the correct collision profile set up for your inventory item? (There are some meshes that ignore all collision by default)
-Orfeas
I’ll check on that, but wouldn’t it still be possible for the raycast to return anything at all if I ignore the player (There are other meshes in the level too)?
Hey Orfeas, can you explain a little more about how you setup your inventory_widget heirarchy.png
I followed the tutorial and everything works except for the textures are not showing up on my widgets, they are always staying as white boxes no matter what items I pickup or drop.
I am new to UI in unreal so I basically had to guess based off the screenshot and I probably have conflicting layer render issue.. but its impossible to know.
Hello,
I’m using a Canvas Panel as a root for my UI. Then, I created a Uniform Grid Panel which contains all the inventory widgets. This control makes sure that each inventory widget has the same space in your UI.
Inside the Panel I’ve placed the widgets that you see in the screenshot. Since you’re having the desired functionality but you’re not seeing any textures this means that your Blueprint cannot find a valid texture so it remains at the default texture (white in this case). Have you checked your Brush binding and your SetTexture assignments? Moreover, make sure to provide a valid texture for each item.
Update: It’s unlikely to have a layer render issue because the Add to Viewport node places your UI 10 layers above your top layer (this is done inside the engine code).
-Orfeas
Can you tell what is going on from these screenshots?
https://www.reddit.com/r/unrealengine/comments/5eptuk/finding_the_correct_viewport_setting/
I was able to get them displayed finally by adjusting my button scale really low to match the size of the ones inside of my grid panel
Hey Max, I’m glad you solved your problem!
-Orfeas
Orfeas,
First off thanks for the tutorial, it’s greatly appreciated. I’ve implemented the pickup function and it successfully adds the item to my inventory. However, from a BP I’m attempting to get the name of the item which is stored in inventory location 0. I am getting the following error in the message log.
Attempted to access TestItem_101 via property CallFunc_Array_Get_Item, but TestItem_101 is pending kill
This makes sense in that I’m storing a reference to an actor that was just destroyed and is pending garage collecion. I’m curious if you get the same message in your game.
since the item is an actor it must exist in the world, in other words it has to be spawned to hold a reference to it’s instance. Otherwise, you’d have to store it’s class and not the actor reference.
thoughts?
Hello Sean,
The behavior you’re describing seems correct (however I cannot remember if I’ve encountered the same problem). The workaround in your case would be to set the visibility of your world item to false and disable its collision. Since you’re still having a valid reference to that item (through your array), the engine’s GC won’t mark your item as pending kill and the player won’t be blocked by that item.
Check out my latest portfolio project that contains the described behavior:
https://github.com/orfeasel/PortfolioSnippets/blob/master/UE4_TechDemos/PuzzleGame/Private/Components/BackpackComponent.cpp#L47
https://github.com/orfeasel/PortfolioSnippets/blob/master/UE4_TechDemos/PuzzleGame/Private/Items/BaseItem.cpp#L73
-Orfeas
Hey there, I’ve just finished the part right before you implement the pause state. Unfortunately I’ve got a problem. Whenever I pick an item, the image of the item appears but the image in the inventory slot doesn’t change size to fit the button, so all that ends up showing up in the button is the top left corner of the image. Is there anyway to fix this?
Also for anyone in the future RemoveFromViewport no longer works, use InventoryWidgetRef->RemoveFromParent(); instead.
Hello Jacob,
Have you set the horizontal and vertical alignment of the image element to stretch (red marker in this screenshot: http://prntscr.com/dws2pf) ?
-Orfeas
Yep, I’ve tried that and still no luck. Here’s some screenshots showing the problem
Before setting to Horizontal/Vertical fill:
http://imgur.com/fo5ZuZV
After setting to H/V fill:
http://imgur.com/wJuOYJa
It doesn’t change no matter if I draw it as an image or box (which you see I’ve done in the screenshots)
Thank you for still replying so late after you posted the article.
MaxPro (a few comments above) encountered the same issue and stated that it was fixed by adjusting the buttons scale really low to match the size of the ones inside his grid panel. Can you give this workaround a go to see if this works for you?
-Orfeas
Well, I messed around with the size of the button, making it this size:
http://imgur.com/od8HRgQ
And it seemed to work allright:
http://imgur.com/6EHrkrJ
I can’t really change the size of the slot now if I want to make a bigger one, but it’ll do for now.
Thanks for your help, I look forward to following more of your tutorials in the future.
I ended up fixing this by removing the “Canvas Panel” from this picture.
https://i2.wp.com/orfeasel.com/wp-content/uploads/2016/04/inventoryslot_img.png
If I put Pickup object in my level and try to save I get some error:
Can’t save C:/Users/User/Documents/Unreal Projects/MyProject9/Content/FirstPersonCPP/Maps/FirstPersonExampleMap.umap: Graph is linked to private object(s) in an external package.
External Object(s):
AssetImportData
Try to find the chain of references to that object (may take some time)?
Any idea how to solve it? 🙂
Seems like something went wrong with your Blueprint. I would delete and then re-create it based on my C++
-Orfeas
I heard some people say going to file while inside your blueprint and selecting “Refresh all nodes” can fix this. But this error can happen for multiple reasons. It comes and goes for me all the time.
So I can get everything but the UI/UMG to work for this tutorial in 3rd person template.
Any idea as to why?
I understand the tutorial is made for the First Person template and I will do it in 1st person tomorrow night. I am just curios as to why the UI/UMG isn’t rendering, are there drastic differences in those templates when it comes to UMG/UI or am I possible horribly overlooking something?
Also thanks for three tutorials they have been a nice intro to UE4 from Unity.
Hello,
I suggest to go over your code once again. Seems like you have overlooked something.
-Orfeas
Ended up being an error in my UE install. A reinstall was needed to clear the issue.
Hey Orfeas, thanks for all these tutorials!
I got one one question. Where is function GetInventory() defined? I can’t compile my code cuz of Character class doesn’t have GetInventory() function. Should I declaare and define it in Character class? If yes, what should go in that function?
Thanks.
-Thevale
Hello,
All this function does is to return the TArray of Pickup items. Having said that, this is the whole function: https://github.com/orfeasel/UE4-Cpp-Tutorials/blob/master/InventorySystem/InventorySystemCharacter.h#L61
-Orfeas
Hey great tutorial, however I’m having an issue with the inventory. I don’t exactly know how to reproduce it, but the inventory will empty itself out sometimes and the game will crash with an error relating the the mycontroller class at this line:
//Re-populate the ItemsArray
InventoryWidgetRef->ItemsArray = Char->GetInventory();
when I check the logs it says
Unhandled Exception: EXCEPTION_ACCESS_VIOLATION reading address 0xffffffff
I’m not sure but it seems like there is an issue when populating the array. Any ideas? otherwise it seems to work fine as I’m able to euip and drop items, but I’m not understanding why this issue sometimes happens. I can replicate it after a few minutes each time I hit play.
Hello,
This error seems to be caused by attempting to read a pointer that points to either garbage memory or unavailable memory to your program. This might be caused by the way we’re initializing the inventory. Instead of initializing the inventory using the following lines:
//Initializing our Inventory
Inventory.SetNum(MAX_INVENTORY_ITEMS);
Try to initialize the inventory with the Init function: https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/TArrays/index.html
(Inventory.Init(nullptr,MAX_INVENTORY_ITEMS);
-Orfeas
Unfortunately that has not fixed the issue. The array will empty itself after testing placing it in the world and dropping it and picking it up again, and the crash will happen, however when I tested it didn’t seem to happen as quickly
Is there a possibility of something else that might be occurring? The error in the log is the same, and I have initialized it in the way you described
Chances are that you’re attempting to access a null pointer. Try to use multiple “if” statements each time you’re accessing a pointer and assign a console message for every “else” statement (so you know which code block has failed). This method will eventually show you the culprit.
-Orfeas
Such a good article, thank you very much 🙂
Finish !!! thank you very mush
hi, is this work in multiplayer ? it is work for one player but if i switch to 2 player engine was crashed