Hi,
I’m trying to find a way to delete gun attached to character.
I’ve already searched for solution checking similar topics.
- The actor(weapon) is spawned and attached to character shoulder.
void APlayerCharacter::SpawnAndAttachToSocket(UWeaponItem* WeaponItem, int32 SlotId)
{
FActorSpawnParameters SpawnParams;
SpawnParams.bNoFail = true;
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn;
SpawnParams.Owner = SpawnParams.Instigator = this;
if (SlotId == 0)
{
LeftShoulderWeapon = GetWorld()->SpawnActor<AProjectileWeapon>(WeaponItem->BPWeaponClass, SpawnParams);
if (!LeftShoulderWeapon)
{
return;
}
LeftShoulderWeapon->Item = WeaponItem;
LeftShoulderWeapon->AttachToComponent(GetMesh(), FAttachmentTransformRules::KeepRelativeTransform, LeftShoulderSocket);
}
}
- When different weapon is selected, the one attached should be destroyed.
void APlayerCharacter::RemoveFromSocket(int32 SlotId)
{
if (SlotId == 0)
{
if (LeftShoulderWeapon->Destroy())
{
WeaponsBar[SlotId] = nullptr;
UE_LOG(LogPlayerCharacter, Log, TEXT("RemoveFromSocket 2. slot:(%d), destroyed"), SlotId);
}
else
{
UE_LOG(LogPlayerCharacter, Log, TEXT("RemoveFromSocket 3. slot:(%d), Actor invincible!!! "), SlotId);
LeftShoulderWeapon->DestroyAttachedWeapon();
}
}
}
- When this fails, it triggers function in parent class:
void AWeaponBase::DestroyAttachedWeapon()
{
if (Destroy())
{
UE_LOG(LogTemp, Log, TEXT(" AWeaponBase::DestroyAttachedWeapon 1. destroyed"));
}
else
{
UE_LOG(LogTemp, Log, TEXT(" AWeaponBase::DestroyAttachedWeapon 2. invincible!!!"));
}
}
And the output:
LogPlayerCharacter: PutWeaponBackIntoInventory 1. (0)
LogPlayerCharacter: RemoveFromSocket 3. slot:(0), Actor invincible!!!
LogTemp: AWeaponBase::DestroyAttachedWeapon 2. invincible!!!
I as mentioned earlier, I’ve checked other topics like:
https://forums.unrealengine.com/t/proper-way-to-destroy-an-an-attached-actor/24889
https://forums.unrealengine.com/t/got-some-problems-with-destroy-actor/44769/2
That doesn’t seem to be the answer.
Hi There,
Possibly something simple should be without knowing your system deeply, my first gut feeling is something about the destroy is being async. So when you say Destroy() to some actor it doesn’t immidiately removes but marks to be deletion (garbage collected).
So from my perspective
if (LeftShoulderWeapon->Destroy())
{
WeaponsBar[SlotId] = nullptr;
UE_LOG(LogPlayerCharacter, Log, TEXT("RemoveFromSocket 2. slot:(%d), destroyed"), SlotId);
}
Should already turn true so it is marked for deletion and garbage collected soon. I don’t see your call from :RemoveFromSocket(int32 SlotId)
but in your log PutWeaponBackIntoInventory(0)-> RemoveFromSocket(0) if check turns false and prints out the other one . Which brings me the question of how you call these, maybe there is same operation on same tick RemoveFromSocket
?
I see you have a destroy function in WeaponBase which is ok LeftShoulderWeapon->DestroyAttachedWeapon();
However you can just call
LeftShoulderWeapon->Destroy();
Simply you can check is valid since you holding already the reference
if (IsValid(LeftShoulderWeapon))
{
LeftShoulderWeapon->DetachFromActor(FDetachmentTransformRules::KeepWorldTransform);
LeftShoulderWeapon->Destroy();
WeaponsBar[SlotId] = nullptr;
LeftShoulderWeapon = nullptr;
UE_LOG(LogTemp, Log, TEXT("Weapon marked for destruction"));
}
After this just to be sure to quickly understand the things right.
- I see you spawning SpawnActor is this Derived from AWeaponBase? Since if not and there IF there is no void AProjectileWeapon::DestroyAttachedWeapon() won’t work but you should see errors.
- This is single player logic.
- if (LeftShoulderWeapon->Destroy()) should already runs it turns true in RemoveFromSocket then will be false at DestroyAttachedWeapon since its already marked for deletion. you can use IsPendingKillPending() to check that.
Let me know if it helps.
void APlayerCharacter::PutWeaponBackIntoInventory(int32 SlotId, UItem* WeaponItem)
{
UE_LOG(LogPlayerCharacter, Log, TEXT("PutWeaponBackIntoInventory 1. (%d)"), SlotId);
if (WeaponItem)
{
PlayerInventory->TryAddItem(WeaponItem);
if (SlotId == 0)
{
if (LeftShoulderWeapon)
{
RemoveFromSocket(SlotId);
WeaponsBar[SlotId] = nullptr;
}
}
}
LeftShoulderWeapon holds a pointer to a weapon that derives from WeaponBase.
It’s just a place for currently attached weapon.
PutWeaponBackIntoInventory is used in UI on drop function.
The node in Widget check if WeaponBar[SlotId] GET[0] is valid, that means there is already a weapon actor attached so connected node calls PutWeaponBackIntoInventory first.
If WeaponBar[SlotId] GET[0] is not valid, then it executes AssignWeaponToSlot.
void APlayerCharacter::AssignWeaponToSlot(int32 SlotId, UWeaponItem* WeaponItem)
{
UE_LOG(LogPlayerCharacter, Log, TEXT("AssignWeaponToSlot -- 1"));
if (!WeaponItem)
{
return;
}
WeaponsBar[SlotId] = WeaponItem;
SpawnAndAttachToSocket(WeaponItem, SlotId);
PlayerInventory→RemoveItem(WeaponItem);
}
DestroyAttachedWeapon() was created just for debugging, it shorter to use Destroy() from actor.
I’ve recreated this logic in Blueprints and I have the same result. That means there is something wrong.
Some time ago I used similar code in UE5.4 it worked. Now it doesn’t want to.
I don’t see a specific problem over here to be honest however only thing bothers me is on RemoveFromSocket
function we don’t see a log of UE_LOG(LogPlayerCharacter, Log, TEXT("RemoveFromSocket 2. slot:(%d), destroyed"), SlotId);
which should be, since if nothing is running twice or nothing has already called to destroy it should anyway go true and log it.
That means either something is preventing it to be destroyed or something is overridden. Did you do anything with BP C++ override destroy function?
Edit:
Also noticed something that IF you AssignWeaponToSlot
before PutWeaponBackIntoInventory
it would change references already for LeftShoulderWeapon in spawn so old reference would be already marked for deletion.
PutWeaponBack func is called first when weapon actor is valid, only when it’s not, it skips to assignWeaponToSlot func.
When it comes to destroy function, there is nothing else in BP or CPP.
It usually works with Destroy().
You mean this ?
if (!WeaponItem)
{
return;
}
I think that maybe not protecting you, I understand that you passing and item over there however I think maybe in the flow its not null at all ? Can you check that cause, if thats not null on the order that would definetely run SpawnAndAttachToSocket(WeaponItem, SlotId);
→ that would overwrite LeftShoulderWeapon thus will result exactly the same output since reference can be new actor which is just spawned and ontick it doesn’t generally destroy new item afaik.
Maybe trying a log inside AssignWeaponToSlot
after the the if
in order to see something happening cause can be culprit over there or related.