Armour and level swap

oh btw thanks for the help with this mate, UE seriously needs to update their tutorials

First what you need to do is create a custom GameInstance class; call it whatever you want, in this example I’ll call it MyGameInstance.

Create a new BlueprintClass, and in the Pick Parent Class class search bar look for GameInstance.

http://images.gigasightmedia.com/GMT_graphics/UE4Tutorials/BPInventoryPersist/001-PickParent-GameInstance.png

Name your new GameInstance object class, and open it up in the BP editor.

Add an array of your ItemData structs, called “Inventory”. Add two booleans, called “ArmourActive?” and “WeaponActive?” These will replace the values in your widget class and in your MyCharacter class, so you can delete them later.

You also want to add two integer values called “ActiveArmourID” and “ActiveWeaponID”.

http://images.gigasightmedia.com/GMT_graphics/UE4Tutorials/BPInventoryPersist/002-BP-MyGameInstance.png

Save and compile your new GameInstance class.

Now open up your MyCharacter blueprint, and we’ll update it to use the new persistent inventory values.

First, add a variable called “ActiveGameInstance” of type “MyGameInstance” (or whatever you called your custom game instance class) to MyCharacter.

Then, add a variable called “Weapon” of type “Actor Reference”.

Then, in the EventGraph, from the BeginPlay event, add a call to GetGameInstance. Cast the result to your MyGameInstance class, and set the returned value to ActiveGameInstance.*

http://images.gigasightmedia.com/GMT_graphics/UE4Tutorials/BPInventoryPersist/003-MyCharacter-Graph.png

    • Ignore the part about “LoadActiveGameInventory” for now.

Now we can build the weapon and armour equipping methods. First, add a “SetArmour” method and a “SetWeapon” method to your MyCharacter. Open up those methods and build them like these:

http://images.gigasightmedia.com/GMT_graphics/UE4Tutorials/BPInventoryPersist/004-BP-SetArmour.png

http://images.gigasightmedia.com/GMT_graphics/UE4Tutorials/BPInventoryPersist/005-BP-SetWeapon.png

You’ll find they are very similar to the methods in your widget, but a little simpler since we don’t have to cast anything or pull data from an external object.

The input pin for SetArmour is a SkeletalMesh Reference. The input pin for SetWeapon is an Actor Reference. You’ll see why we have that Weapon variable set here in a bit.

Now add two more methods to MyCharacter, one called “EquipArmour” and another called “EquipWeapon”. Let’s look at EquipArmour first:

http://images.gigasightmedia.com/GMT_graphics/UE4Tutorials/BPInventoryPersist/006-BP-EquipArmour.png

Again, it’s very similar to your functionality within the widget, but there are some important changes. First, the the Inventory data is pulled from the ActiveGameInstance variable we set on BeginPlay. Because the Inventory and ArmourActive? values are stored in the GameInstance, they will persist between level changes. I also assign the retrieved item’s ItemID value from the ItemData struct to the ActiveArmourID variable in the ActiveGameInstance. I’m assuming your ItemIDs are unique to each item in the game world, so if they’re not you’ll need to adjust this for this method to work properly. We set the ArmourActive? value to true, and then call SetArmour, passing that method the Mesh within the ItemData struct.
ButtonClicked is the same variable you pulled from MyCharacter in your widget method (I’m guessing that’s something your inventory menu assigns); but since we’re in MyCharacter already it’s a local variable.

EquipWeapon is also going to look familiar:

http://images.gigasightmedia.com/GMT_graphics/UE4Tutorials/BPInventoryPersist/007-BP-EquipWeapon.png

Again, the big differences are pulling the Inventory and WeaponActive? values from the MyGameInstance class instead of MyCharacter, and we set the ActiveWeaponID there too. After spawning the weapon actor we pass the result to our new SetWeapon method.

Now we add two more methods, “UnEquipArmour” and “UnEquipWeapon”; again, you’ll find them very similar to your widget events:

http://images.gigasightmedia.com/GMT_graphics/UE4Tutorials/BPInventoryPersist/008-BP-UnEquipArmour.png

http://images.gigasightmedia.com/GMT_graphics/UE4Tutorials/BPInventoryPersist/009-BP-UnEquipWeapon.png

In fact, UnEquipArmour looks almost identical to EquipArmour; the only differences are performing the assignment if ArmourActive? is true, and we set ArmourActive? to false when we’re done.

UnEquipWeapon is almost completely the same, the only difference is the location of the WeaponActive? variable.

However, calling “GetAllActorsOfClass” is not a very efficient way of doing this. What if you have a bunch of enemy characters that are also carrying IronSwordBP weapons? Or if the character is wielding weapons in both hands and you only want to destroy one of them?

This is why I opted to store the spawned weapon in a local variable. Instead of using this version of UnEquipWeapon, you can do something like this:

http://images.gigasightmedia.com/GMT_graphics/UE4Tutorials/BPInventoryPersist/010-BP-UnEquipWeaponAlt.png

This way we don’t have to get an array and parse it, and we don’t have to search the game world for all actors of a class (which is slow); we already have a reference to the weapon we want, so we just destroy it directly!

Now let’s do something really new; we’re going to create the method that restores your active Inventory settings after loading a new level (or loading from a game save). Create a new method called “LoadActiveInventory” and another called “GetInventoryItem”. First we’ll look at GetInventoryItem:

http://images.gigasightmedia.com/GMT_graphics/UE4Tutorials/BPInventoryPersist/011-BP-GetInventoryItem.png

This is a pretty simple method; the input pin is an integer value. It pulls the Inventory array from our game instance, and then cycles through it looking for an item with an ItemID that matches the passed in TargetItemID. If it finds one, it assigns the found item to a local variable called “FoundItem” and immediately exits the method. If it doesn’t find one when it finishes cycling through the array, it exits the method and returns FoundItem without assigning a value to it. By default, FoundItem is a default ItemData struct.

This is important, so let’s look at how I have ItemData structured, so you can make sure your setup will work. I believe you have a value called “Empty” that corresponds to an empty mesh value; you may want to create an EmptyWeapon and EmptyArmour item that hold mesh and actor values appropriate for those situations, and make the default FoundItem struct correspond to them.

In my case, I’ve made the default values for the mesh and actor references empty, so if they are returned my methods assign NULL values.

http://images.gigasightmedia.com/GMT_graphics/UE4Tutorials/BPInventoryPersist/012-BP-ItemDataDefaults.png

Now let’s look at LoadActiveInventory:

http://images.gigasightmedia.com/GMT_graphics/UE4Tutorials/BPInventoryPersist/013-BP-LoadActiveInventory.png

Here we have a sequence with two branches. The first updates our Armour, and the second updates our Weapon. The branches are very similar to EquipArmour and EquipWeapon, but the key difference is that we don’t pull from the Inventory by index, we use the ActiveArmourID and ActiveWeaponID values to retrieve items from the Inventory with the GetInventoryItem we just created. If we get valid values, we call SetArmour, and SetWeapon as normal.
In the case that we receive a default ItemData struct (because a matching ItemID wasn’t found), the Equippable AND IsArmour/IsWeapon checks will not pass, so SetArmour and SetWeapon will never be called.
Also notice that we don’t modify the Active? booleans or ID integers as we do in Un/EquipArmour/Weapon; we aren’t modifying our state here, we are restoring our state.

The last modification we make to MyCharacter is to add the call to LoadActiveInventory to our BeginPlay event, we’ll do it right after we set the ActiveGameInstance value:

http://images.gigasightmedia.com/GMT_graphics/UE4Tutorials/BPInventoryPersist/003-MyCharacter-Graph.png

So now that we have all our inventory functionality built into our persistent GameInstance and the MyCharacter class, we can clean up our widget:

http://images.gigasightmedia.com/GMT_graphics/UE4Tutorials/BPInventoryPersist/014-BP-InventoryWidgetGraph.png

When the buttons are clicked, the corresponding methods are called. We can get rid of the local WeaponActive? and ArmourActive? booleans and rely on the value in MyGameInstance; and we only need the PlayerRef to call the appropriate methods from MyCharacter.

Finally, we have a very important step. We need to let the engine know to use our new GameInstance.

Open the Edit->Project Settings window, and under the Project heading click on Maps & Modes. Change the Game Instance Class to your new, custom GameInstance:

http://images.gigasightmedia.com/GMT_graphics/UE4Tutorials/BPInventoryPersist/015-ProjectSettings-GameInstance.png

So that’s it. Now your Inventory items and state variables will persist between levels. You can also write those variables into your GameSave, and then when you reload a game you can pull the saved values from your GameSave and apply them to the active game instance. Upon level load the MyCharacter will update its local values just like it transferred between levels and restore the proper game state. Your widget clicks will automatically update the GameInstance values whenever the player modifies anything, and you don’t need to worry about keeping duplicate values in multiple objects!

I’ve also included some suggestions that might make things smoother for you going forward.

Instead of holding a reference to the Weapon Actor in your ItemData struct, you can instead hold a reference to the class. Then, when you retrieve that ItemData struct, you can pass the Class value into your SpawnActor method node, and it will spawn a weapon of that specific class. This will allow you to have the character equip different weapon types, rather than always spawning the same IronSwordBP weapon.

You can modify your EquipWeapon method, and include a SpawnWeapon instead of SetWeapon as below:

http://images.gigasightmedia.com/GMT_graphics/UE4Tutorials/BPInventoryPersist/016-BP-EquipWeaponAlt.png

http://images.gigasightmedia.com/GMT_graphics/UE4Tutorials/BPInventoryPersist/017-BP-SpawnWeapon.png

Holding a direct reference to the actor loads the object into memory. If you have a full inventory with multiple weapons and armour, that means all of your weapons and armour are in memory the entire time. Instead, if you hold references to the classes, the objects are only loaded into RAM when you spawn them as needed.

http://images.gigasightmedia.com/GMT_graphics/UE4Tutorials/BPInventoryPersist/018-BP-ItemData-ClassReference.png

If you opt to do that, you can eliminate the Actor and Mesh variables in the ItemData struct, and just use the ActorClass and MeshClass values.

You also might have noticed I have an Enum value called “Type” in my struct. You can use that to replace your IsWeapon? and IsArmour? booleans, and accomodate all kinds of items without having to add a unique boolean for each.

So in your EquipWeapon method, instead of IsWeapon? you can check if your Type value is “Weapon”:

http://images.gigasightmedia.com/GMT_graphics/UE4Tutorials/BPInventoryPersist/019-BP-EquipWeapon-ItemTypeCheck.png

When you are trying to equip your armour, weapon, clothing, or whatever, your equip methods can pull the appropriate class type (Actor, Mesh, etc) and then perform the proper spawning actions. That way all your items can be stored in the same ItemData structure with little memory overhead.

Here’s what my dummy Enum looks like:

http://images.gigasightmedia.com/GMT_graphics/UE4Tutorials/BPInventoryPersist/020-BP-Enum---Item-Type.png

As you can see, you can easily accommodate all sorts of items here. You can also then make a generic “EquipItem” method like this:

http://images.gigasightmedia.com/GMT_graphics/UE4Tutorials/BPInventoryPersist/021-BP-EquipItem.png

http://images.gigasightmedia.com/GMT_graphics/UE4Tutorials/BPInventoryPersist/022-BP-EquipItem-DetermineType.png

http://images.gigasightmedia.com/GMT_graphics/UE4Tutorials/BPInventoryPersist/023-BP-EquipItem-EquipWeapon.png

http://images.gigasightmedia.com/GMT_graphics/UE4Tutorials/BPInventoryPersist/024-BP-EquipItem-EquipArmour.png

Lots of flexibility here.

Hope this helps.

Wow mate this is all amazing mate im gonna try implimenting all this right now,

before i get all into this tho for the weapons function, will this actually equip the weapon from the blueprint and not just an actor mesh, cause my weapons are swords and i need the collision capsules in the blueprint version to implement damage.
that was why i had two different equip methods for armour and weapons lol

The implementation is the same as you had before, it’s just done within the MyCharacter class instead of within your widget.

cool, ill put all this in and test it, for the ID’s it can just be numbers right? from like 1 to say 20+

It doesnt work for me, i had everything set up properly but neither weapons or armour would equip to my character :frowning:

im not sure wether its the equip and set options or not though, caue i tried some of the older equip function i had and when i use the active game instance varialbe to get inventory and weapon/armour active? the stuff doesnt equip to my character. so it could also be my game instance isnt loading even tho i have it at begin play and on maps/modes on project settings

I even tried changing the empty static mesh attached to my characters hand socket to a child actor and that worked with my equip system without havin to use the spawn actor option but once again when i added the active game instance functions to it the stuff wouldnt equip
infact when i think about it some of the armour in my game will have abilities to it aswell so i should probly also spawn the armour bp aswell.

lol this is headache inducing

Don’t forget that you’ll have to populate the inventory array like you did when it was in the player character.

You can use a PrintString node to test the state of your methods at various stages to track down exactly what problem you have.

For example, just after you set the ActiveGameInstance variable in your character, pull out a PrintString node and print the value of ActiveArmourID and ActiveWeaponID, then change the values and print them again. If everything prints out correctly, your game instance is working fine and the problem is somewhere else.

You can go through your execution sequence like this and find where the problem(s) occur.

yea there is gonna be some trial and error for something this big lol I’m gonna keep lookin into these child actors aswell.

In the first image, you’re calling “GetPlayerCharacter” and casting to MyCharacter from within the MyCharacter class; you don’t need to do that, just use the ChildTest variable directly, it’s local.

In the second image, you’ve set up “EquipIronSword” as an event, rather than a method. This will work, but it does operate a bit differently, and I think execution time is a bit slow since it runs through the UE event system.
Also, you’re retrieving the Inventory from MyCharacter, rather than from your GameInstance.
The ButtonClicked value is part of MyCharacter, when you’re within MyCharacter you don’t need to use “Self” to get local variables, you can just access them directly.

Looks like your character has a sword in hand in that last image, though! Something’s going right.
:wink:

oh yea i forgot to remove the cast to in the first image lol

yea i have added it as an event as i test this child actor stuff, if i can get it workin im gonnamake it a function instead.

yea the inventory is still with character, i done it this way to send you a couple of pics of how i want it to work im gonna start tryin it with the instance and print strings and see whats not workin right

yea i left the collision capsule visible to verify it had spawned the weapon blueprint and not just the mesh.

btw where would i assign the active armour/weapon ID’s?
is that done in game world on the pickup items or in the struct or some other way?

Do you mean where to initialize the Armour/Weapon IDs?
You can set the default value within the GameInstance; same with the Weapon/ArmourActive booleans, as well as the inventory array.

The values are updated within the EquipArmour/Weapon methods.

If you mean, how do you assign ItemIds for each item in your game, that will be part of however you are building your items; when you set a mesh to the item, the item type, etc. you can give it a unique item id.

As soon as i add the active instance variable it stops working

Have you populated the Inventory within the GameInstance?
Have you verified the game instance class is loading properly?

Are you getting a compile error? What do you mean when you say it stops working? What behavior are you getting, what behavior do you expect?

“Have you populated the Inventory within the GameInstance?” how do i do that?
game instance class? you mean the game instance blueprint, yea i have that compiled and set in character bp at begin play

no compile errors appearing

i check this while i was in game pressing the buttons and when it hits the second branch its progress stops

You have to add Items to the GameInstance for them to be retrieved.
How did you do it before when the inventory was in MyCharacter? Do it the same way in GameInstance.

So, either Equipable or IsWeapon, or both, are reading as False.
But, that means ActiveGameInstance->WeaponActive? is reading as True, so your GameInstance is loading properly and read that value properly.

If you are trying to get an ItemData from an empty array, it’s never going to pass.

You can set the default values in the MyGameInstance blueprint, just like you would any other blueprint.
You’ll want to populate your inventory with the available items the same way you did when you populated the inventory in MyCharacter.

And make sure each of your entries has a unique ItemID.