Inventory : Why can't i access my Item properties?

hi,

I have created an UActorComponent for my Inventory manager, this one is on the character

on my Inventory Manager i have some atribute :

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Armor")
	TSubclassOf<class AArmor> Helmet;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Armor")
	TSubclassOf<class AArmor> Body;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Armor")
	TSubclassOf<class AArmor> LeftArm;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Armor")
	TSubclassOf<class AArmor> RightArm;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Armor")
	TSubclassOf<class AArmor> LeftLeg;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Armor")
	TSubclassOf<class AArmor> RightLeg;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapons")
	TSubclassOf<class AWeapon> PrimaryWeapon;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapons")
	TSubclassOf<class AWeapon> SecondaryWeapon;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Bag")
	TArray<TSubclassOf<class AItem>> Bag;

and for testing in my level BP, when I want to access to the Armor or Weapon I can’t get anny property of it

my weapon for example is created in BP and extend from Weapon (which extend from Item, which extend from Actor)

so How can I access to any Item atribute in BP ? what I have done wrong ?

Since i have a pretty fixed setup for a working inventory in my head, i need some infos about what you are doing here.

So you have an InventoryManager and you created these different variables that can hold an item per slot. Where is your actual inventory? Is it the “Bag”?
I guess it is. So you want to access an item that is inside this Bag Array? You can just take the Item you want to view out of your Bag Array and maybe cast to it the specifc item you are using. Now you should be able to get the attributes you made inside your ItemClass.

I want to acces to an item in a slot, by example PrimaryWeapon

this is what I have done :

  • I have creat a BP weapon “W_test”
  • I have edited the PlayerCharacter BP to add W_Test in the PrimaryWeapon slot
  • I try to access to some atribute or function of the primary weapon like that (printinting the name or description of it):

in my ItemClass I have the name, the description the Mesh of the Item (generale atribute of an item)

What are the members of your AWeapon class? If there is an FName Name attribute that is also a UPROPERTY(BlueprintReadOnly), you should be able to drag from your Primary Weapon node and get the Name attribute and cast it to a string before plugging it into Print String.

I would take a look at the TSubclassOf template again. What you are getting in you Blueprints is actually a UClass object if I am not mistaken. To access the properties, I would suggest spawning an instance of the object first. This is similar to how the examples spawn a projectile from a TSubclassOf object in the shooter examples. Long story short your Primary Weapon get is returning a UClass object, not an AWeapon object, hence no access to the properties.

More on TSubclassOf: https://docs.unrealengine.com/latest/INT/API/Runtime/CoreUObject/UObject/TSubclassOf/index.html

More on spawning an object from TSubclassOf objects: A new, community-hosted Unreal Engine Wiki - Announcements - Unreal Engine Forums

That’s why i ask how your inventory is managed.

I would recommened not to save the whole actor AS actor, although you could if it’s an item that is spawned to your character like the sword. I would rather save the item properties itself. You could make a struct for them and make an array of this struct for your character. You could define itemtypes with an enum also stored in the struct and check the enum if you want to know if its a sword/weapon. You can display the model of the weapon by just assigning it to a component you have created for you character etc…

so you recomend to me to use a TSubobjectPtr instead of TSubclassOf

I have to spawn the Item and “atach” it in the slot before using it right ?

Weapon.h : Weapon.h - Pastebin.com
Item.h : Item.h - Pastebin.com
InventoryManager.h InventoryManager - Pastebin.com

That seems like a lot of overhead to save it as a struct. What if theyes want to spawn a particular weapon? Or if they have over 500 different items in the game? That’s too large for an enumerated list and it becomes unmanageable, imagine the switch statement. Plus they would them need to fetch mesh, collision, and other references for when they do spawn an item. It would be far better to save a pointer to the base item class in the array and when it is picked up, disable the collision and rendering of it. Another option would be an Item Factory that tales in these subclasses and spawns them in the world when needed, then you wouldn’t need multiple objects existing in the world for different inventories.

No and yes. Keep it as a TSubclassOf but run code similiar to spawn it:

AFPSProjectile* const Projectile = World->SpawnActor<AFPSProjectile>(ProjectileClass, MuzzleLocation, MuzzleRotation, SpawnParams); 

Then using the pointer, attach it to a slot for the actor.

What? I never said that the enum should be the item. It only tells the type. Like armor, weapon, trinket etc. I don’t think you have 500 item types.

The struct contains every information about the item. So you just spawn the base class depending on the itemtype and fill the struct back in zo the spawned item. My inventory system is completely running so i know it works like this :smiley:

I have to do this at the Inventory manager init, and for equiping item right ?

or I can do it with pointer, and use only equip() function where equip will be charged to spawn and attach the Item. My slot will bee TSubobjectPtr initialized by default a NULL and equip() will set it to the pointer of the equiped item what do you think ?

Yes, it’s data driven. (: And yes, you can handle this with a data row etc.

I didn’t know how his Inventory worked, so thought that he maybe got the idea wrong.

He can’t access the properties because he only gets the uclass, you are right there too. He needs to spawn the Actor to be able to access the properties.

As long as the item is spawned in the world, he can reference it with his pointers etc.

Ah, I see you what you are saying now. I was confused, my apologies. This is a more data-driven approach then? Because if that is the case you could create a data row handle from the struct and fill in properties from there… But this is all out of scope of the question. I think Talus just wanted to know why he could not access his properties from the TSubclassOf getter in BP and that is because it is returning a pointer to a UClass base object.

The problem with that implementation, according to my understanding, with your use of TSubobjectPtr comes from the docs:

It can only be assigned to with
PCIP.CreateDefaultSubobject (via
TSubobjectPtrConstructor) in the
owning object’s constructor.
https://docs.unrealengine.com/latest/INT/API/Runtime/CoreUObject/UObject/TSubobjectPtr/index.html

Personally, I have always gone the raw pointer route. The only caveat is that the item must exist in the world before assigning it. In which case, equip would not need to spawn the object.

Here is an awesome blog post about a UsableItem system that evolves into a robust inventory system, I highly suggest reading it as it comes with source code downloads (C++ and BP). I have used it for the basis of two inventory systems for some of my clients:

http://www.tomlooman.com/category/unreal-engine-4/

Wups, i wanted to convert your comment to an answer and keep your konversation with Talus in it. I guess i can’t do this :open_mouth: Sorry for messing up the Comment order/structure >.<

EDIT: Wuhu, managed to merge the comments xD now he can accept your answer if you are finished helping him.

Ok I see

I change my slot to AWeapon * weaponSlot

and my equip() will be a generic function for equiping Weapon or Armor (item directly see in world), I need to spawn it before assigning it

but this will work with class derived in BP, by example my test weapon W_test is a BP extended from AWeapon, W_test can assign into the pointer weaponSlot after spawn ? with cast ?

for the Bag slot, it’s a “garbage” inventory, just SubclassOf can be use here and if the player equip from the bag I can spawn the correct object using the subclass.

Pointers in C++ can point to inherited class objects without issue. The only thing is, if you need to access a child class’s function (W_test) you will then need to cast to W_test. For instance:

AWeapon* MyWeapon = new AChildWeapon(); //Where AChildWeapon : public AWeapon

Would be totally valid in raw, vanilla C++. Of course, you wouldn’t want to use new (for the most part) in UE4 engine (as objects won’t be created properly).

But yes, that is the general idea. Again look at the blog posts from tomlooman.com as it is very helpful in doing what you are trying to do.

I have done this for the equip function :

bool UInventoryManager::equip(TSubclassOf<class AItem> Item, TEnumAsByte<ESlotType::Type> Slot){
	AItem * tmpItem = Cast<AItem>(Item);

	switch(tmpItem->ItemType)
	{
	case EItemType::IT_WEAPON:
		if (Slot == ESlotType::ST_WEAPON_PRIMARY){
			PrimaryWeapon = GetWorld()->SpawnActor<AWeapon>(Item);
			return true;
		}
		else if (Slot == ESlotType::ST_WEAPON_SECONDARY){
			SecondaryWeapon = GetWorld()->SpawnActor<AWeapon>(Item);
			return true;
		}
		else{
			return false;
		}
		break;
	case EItemType::IT_ARMOR:
		break;
	}
	return false;
}

What do you think ?

I have some crash at this line :

switch(tmpItem->ItemType)

Problem solved

bool UInventoryManager::equip(TSubclassOf<class AItem> Item, TEnumAsByte<ESlotType::Type> Slot){
	AItem * tmpItem = GetWorld()->SpawnActor<AItem>(Item);

	switch(tmpItem->ItemType)
	{
	case EItemType::IT_WEAPON :
		if (Slot == ESlotType::ST_WEAPON_PRIMARY){
			PrimaryWeapon = GetWorld()->SpawnActor<AWeapon>(Item);
			tmpItem->Destroy();
			return true;
		}
		else if (Slot == ESlotType::ST_WEAPON_SECONDARY){
			SecondaryWeapon = GetWorld()->SpawnActor<AWeapon>(Item);
			tmpItem->Destroy();
			return true;
		}
		else{
			return false;
		}
		break;
	case EItemType::IT_ARMOR :

		break;

	}
	tmpItem->Destroy();
	return false;
}