Help debugging GAS Inventory Item Equipping

Hey,

So I have been trying to get my Equip Item for inventory functionality working. As far as I know the code is correct, but for some reason, when I equip the item the location gets set to 0,0,0 and the target socket is none…

A quick overview:

I have an Item class with all the things you want your item to have and it’s made to be blueprint driven, so it has things like mesh, item instance, item state, root scene component, sphere component etc. and then I have an Item static data class, which is a custom class used to store the items actor information, and what socket it’s supposed to connect to.

This is the code for processing an equipped item, in Item Actor.cpp:

void AItemActor::OnEquipped()
{
	
	ItemState = EItemState::Equipped;
	SphereComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
	SphereComponent->SetGenerateOverlapEvents(false);

	// Make sure the item is visible to the player
	//SetActorHiddenInGame(false);

	// Get the name of the socket that the item attached to and print it to the screen
	GEngine->AddOnScreenDebugMessage(-1, 20.f, FColor::Green, FString::Printf(TEXT("(AItemActor::OnEquipped())EQUIPPED ItemActor attached to socket: %s"), *GetAttachParentSocketName().ToString()));
	

	// UEngine print to screen the location of the item mesh
	GEngine->AddOnScreenDebugMessage(-1, 20.f, FColor::Green, FString::Printf(TEXT("(AItemActor::OnEquipped())EQUIPPED ItemActor location after attachment: %s"), *GetActorLocation().ToString()));
}

This part handles overlap events:

void AItemActor::OnSphereOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
								 UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	
	// Check if the current item actor has authority (e.g., it is running on the server)
	if (HasAuthority())
	{		
		// Create a new FGameplayEventData structure for the payload of the gameplay event
		FGameplayEventData EventPayload;

		// Set the instigator of the event as the current item actor
		EventPayload.Instigator = this;

		// Set the optional object of the event as the ItemInstance of the current item actor
		EventPayload.OptionalObject = ItemInstance;

		// Set the event tag as the EquipItemActorTag, which may be used to identify the event type
		EventPayload.EventTag = UInventoryComponent::EquipItemActorTag;

		// Send the gameplay event with the EquipItemActorTag and the event payload to the overlapping actor (OtherActor)
		UAbilitySystemBlueprintLibrary::SendGameplayEventToActor(OtherActor, UInventoryComponent::EquipItemActorTag, EventPayload);
	}
}

I have these 3 on my Inventory component:

void UInventoryComponent::EquipItem(TSubclassOf<UItemStaticData> InItemStaticDataClass)
{
	if (GetOwner()->HasAuthority())
	{
		for (const auto Item : InventoryList.GetItemsRef())
		{
			if (Item.ItemInstance->ItemStaticDataClass == InItemStaticDataClass)
			{
				Item.ItemInstance->OnEquipped(GetOwner());

				CurrentItem = Item.ItemInstance;

				break;
			}
		}
	}
}

void UInventoryComponent::EquipItemInstance(UInventoryItemInstance* InItemInstance)
{
	if (GetOwner()->HasAuthority())
	{
		for (const auto Item : InventoryList.GetItemsRef())
		{
			if (Item.ItemInstance == InItemInstance)
			{
				Item.ItemInstance->OnEquipped(GetOwner());
				CurrentItem = Item.ItemInstance;
				break;
			}
		}
	}
}

void UInventoryComponent::EquipNext()
{
	TArray<FInventoryListItem>& Items = InventoryList.GetItemsRef();

	const bool bNoItems = Items.Num() == 0;
	const bool bOneAndEquipped = Items.Num() == 1 && CurrentItem;

	if(bNoItems || bOneAndEquipped) return;

	UInventoryItemInstance* TargetItem = CurrentItem;

	for (const auto Item : Items)
	{
		if (Item.ItemInstance->GetItemStaticData()->bCanBeEquipped)
		{
			if (Item.ItemInstance != CurrentItem)
			{
				TargetItem = Item.ItemInstance;
				break;
			}
		}
	}

	if (CurrentItem)
	{
		if (TargetItem == CurrentItem)
		{
			return;
		}

		UnequipItem();
	}

	EquipItemInstance(TargetItem);
}

and this does the actual equipping:

void UInventoryItemInstance::OnEquipped(AActor* InOwner)
{
	if (UWorld* World = InOwner->GetWorld())
	{
		const UItemStaticData* StaticData = GetItemStaticData();

		FTransform Transform;
		ItemActor =	World->SpawnActorDeferred<AItemActor>(StaticData->ItemActorClass, Transform, InOwner);
		ItemActor->Init(this);
		ItemActor->OnEquipped();
		ItemActor->FinishSpawning(Transform);

		ACharacter* Character = Cast<ACharacter>(InOwner);
		if (USkeletalMeshComponent* SkeletalMesh = Character ? Character->GetMesh() : nullptr)
		{
			ItemActor->AttachToComponent(SkeletalMesh, FAttachmentTransformRules::SnapToTargetNotIncludingScale, StaticData->AttachmentSocket);
		}
	}

	bEquipped = true;
}

Any assistance would be much appreciated, if more info is required you can find my git repo here:

Are you sure about usinng exact same name of the socket? Can you check it from skeletal mesh of your character

Yes, the name is copy-pasted. I have just double checked, and it’s definitely the same.

This:

void UInventoryItemInstance::OnEquipped(AActor* InOwner)
{
	if (UWorld* World = InOwner->GetWorld())
	{
		const UItemStaticData* StaticData = GetItemStaticData();

		FTransform Transform;
		ItemActor =	World->SpawnActorDeferred<AItemActor>(StaticData->ItemActorClass, Transform, InOwner);
		ItemActor->Init(this);
		ItemActor->OnEquipped();
		ItemActor->FinishSpawning(Transform);

		ACharacter* Character = Cast<ACharacter>(InOwner);
		if (USkeletalMeshComponent* SkeletalMesh = Character ? Character->GetMesh() : nullptr)
		{
			ItemActor->AttachToComponent(SkeletalMesh, FAttachmentTransformRules::SnapToTargetNotIncludingScale, StaticData->AttachmentSocket);
		}
	}

	bEquipped = true;
}

calls ItemActor->OnEquipped() which reports the item location before the call to ItemActor->AttachToComponent() which sets the location - move it to after that call

Oh that actually did help a lot. Now when I’m doing my logs I can see that it’s setting the right socket and the right location on the actor. But it’s still not visible…

The Item is also not spawning in another location…

Edit:

I found it was attaching the root component which is somehow not linked properly to the actor. So I have some work to do in setting up the actor properly.