Download

Checking attached actor on skeleton socket

Hi, I’m new to unreal/programming, I’m trying make a walking arsenal of a character/all the weapons he’ll use is attached to him, so far it worked but I wanna check if a certain slot on the skeleton already has the weapon attached to it, if so I want to either destroy the already attached weapon or do nothing with the new weapon that I’ve clicked to equip. (Or later maybe to compare the two weapons if I add upgrades/different weapons of the same variant…)



const USkeletalMeshSocket* KnifeSheath = GetMesh()->GetSocketByName("KnifeSheath");
const USkeletalMeshSocket* PistolSheath = GetMesh()->GetSocketByName("PistolSheath");
...

switch (Weapon->WeaponType)
{
case EWeaponType::EWT_Knife:

if (KnifeSheath)
{
Weapon->AttachToComponent(GetMesh(), FAttachmentTransformRules::SnapToTargetNotIncludingScale, "KnifeSheath");
Weapon->WeaponMesh->SetCollisionResponseToChannel(ECC_Pawn,ECR_Ignore);
}
break;

case EWeaponType::EWT_Pistol:

if (PistolSheath)
{
...


How do I check if there is already a weapon attached to a skeleton slot?
How do I get a hold of it?
How do I destroy it?

Want to know these before I try equipping the weapons to main hand/switching between the attached weapons next.

Thanks.

As far as I know, there is no way to check if an actor is attached to a socket. I think you should create an actor component that handles these equipments.

@ **samtim89 In the file SceneComponent.h at around line 630 to 661 there are a bunch of functions you will want to use to achieve that.


** /** Get the socket we are attached to. */
UFUNCTION(BlueprintCallable, Category="Utilities|Transformation")
FName GetAttachSocketName() const;**

line 729 has this


**/**
* Detach this component from whatever it is attached to. Automatically unwelds components that are welded together (See WeldTo)
* @param DetachmentRules How to handle transforms & modification when detaching.
*/
virtual void DetachFromComponent(const FDetachmentTransformRules& DetachmentRules);**

That should get you what you are wanting.**

I don’t know what use will be getting the socket name without the actor attached to it since I’m dealing with specific sockets already but DetachFromComponent() seems to be used a lot with switching weapons, along with setting visibility from some source files I looked into. Thanks.

The quick and dirty solution is to just make hard references.


const USkeletalMeshSocket* KnifeSheath = GetMesh()->GetSocketByName("KnifeSheath");
UKnife* Knife = nullptr;
const USkeletalMeshSocket* PistolSheath = GetMesh()->GetSocketByName("PistolSheath");
UPistol* Pistol = nullptr;
...]
switch (Weapon->WeaponType) {
case EWeaponType::EWT_Knife:
    if (KnifeSheath) {
        if(!Knife){
            Weapon->AttachToComponent(GetMesh(), FAttachmentTransformRules::SnapToTargetNotIncludingScale, "KnifeSheath");
            Weapon->WeaponMesh->SetCollisionResponseToChannel(ECC_Pawn,ECR_Ignore);
            Knife = Cast<UKnife>(Weapon);
        } else {
            //Do comparison between Weapon and Knife to choose whether to swap
            //or do Knife->Destroy() then assign Weapon as new Knife regardless of which is 'better'
            //or do nothing
        }
    }
...]

Creating SceneComponents would be the ideal soluton.

Thanks for the solution. I didn’t know you could create & assign to pointers like that.

Can you explain what do you mean by this if it isn’t too much trouble?

Creating components provides encapsulation of weapon and sheath specific code and functionality. I was thinking you should create multiple scene components for each weapon type, which allows you to easily ‘scale’ how many sheathes your actor has without cluttering your code with all kinds of extra pointers and conditionals. This will be exceptionally useful for your scenario since your character is going to be an “arsenal.” As an example, lets suppose you wanted one sheath on your character’s left thigh at the start of the game, then at some point later you wanted them to have an additional sheath on their right thigh. Your code begins to become something like:


const USkeletalMeshSocket* KnifeSheathLeft = GetMesh()->GetSocketByName("KnifeSheathLeft");
AKnife* KnifeLeft = nullptr;
const USkeletalMeshSocket* KnifeSheathRight = GetMesh()->GetSocketByName("KnifeSheathRight");
AKnife* KnifeRight = nullptr;
const USkeletalMeshSocket* PistolSheath = GetMesh()->GetSocketByName("PistolSheath");
APistol* Pistol = nullptr;
...
switch (Weapon->WeaponType) {
case EWeaponType::EWT_Knife:
    if (KnifeSheathLeft) {
        if(!KnifeLeft){
            ...]
        } else {
            //This logic changes since you need to account for the right sheath as well
            if(HasUnlockedRightKnifeSheath && KnifeSheathRight){
                 if(!KnifeRight){
                     //Assign to right knife
                 } else {
                    //What ever you do when both are occupied
                 }
             }
        }
    } else if(HasUnlockedRightKnifeSheath && KnifeSheathRight){
        ...] //See above
    }

As you can see, your conditionals start to become a mess. Some of it can be cleaned up by checking socket validity in the actor’s constructor, but you’ll still have quite a bit of branching. Now consider what happens if you add a third knife or you decide that you want two pistols on your character’s hip and one on each ankle. The same applies for the rest of your weapons. Also keep in mind, your character shouldn’t need to have references to all these weapons. At any given time, they only need to know about whatever is actually equiped and being used, one maybe two weapons. So most of those pointers are only being used to check whether or not the character can pick up a new weapon. What I had in mind was a setup more like:



//I'll leave you to actually define the classes, but here's the hierarchy
class USheath : USceneComponent;
class UKnifeSheath : USheath;

bool USheath::AddWeapon(AWeapon NewWeapon){
    if(IsValidSocket() && IsUnlocked() && IsAvailable()){
        //Attach NewWeapon to owning actor's mesh at SocketName socket
        Weapon = NewWeapon;
        return true;
     }
     return false;
}

void USheath::SetSocketName(FName NewSocketName){
    SocketName = NewSocketName;
    Socket = GetOwner()->GetMesh()->GetSocketByName(SocketName)
    //Attach this component to socket, not required but helps visualize sheath location
}



UPROPERTY()
UKnifeSheath* LeftSheath;
UPROPERTY()
UKnifeSheath* RightSheath;

AYourCharacter::OnConstruct(){
    ...]
    KnifeSheathLeft = NewObject<UKnifeSheath>();
    //Attach to mesh
    KnifeSheathLeft->SetSocketName("KnifeSheathLeft");
    if(!KnifeSheathLeft->IsValidSocket()){
       //Check that Socket member is a valid pointer
       //Log that socket was invalid
    }
    KnifeSheathRight = NewObject<UKnifeSheath>();
    //Attach to mesh
    KnifeSheathRight->SetSocketName("KnifeSheathRight");
    if(!KnifeSheathRight->IsValidSocket()){
        //Check that Socket member is a valid pointer
        //Log that socket was invalid
    }
}
...]
switch (Weapon->WeaponType) {
case EWeaponType::EWT_Knife:
    TArray<UKnifeSheath*> KnifeSheathes;
    GetComponents<UKnifeSheath>(KnifeSheathes);
    UKnifeSheath* WorstKnife = nullptr;
    bool KnifeAdded = false;
    for(UKnifeSheath* KnifeSheath : KniveSheathes){
        KnifeAdded = KnifeSheath->AddWeapon(Weapon);
        if(KnifeAdded){
            break;
        } else if(KnifeSheath->ShouldReplaceWeaponWith(Weapon)
        //Check if the sheathed weapon is 'worse' than new weapon
        && (!WorstKnife || KnifeSheath->ShouldReplaceWeaponWith(WorstKnife->GetWeapon()))){
            WorstKnife = KnifeSheath;
        }
    }
    if(!KnifeAdded){
        //What ever you do when all are unavailable
        if(WorstKnife)
            WorstKnife->ReplaceWeapon(Weapon);
        }
    }

Each sheath references one weapon and vice versa. One of the nice things about this approach is that once a derived Sheath component is created and the socket name has been set, it’ll manage getting ‘filled’ by itself for the most part. Another plus is that most of the functions can be defined in the base class since they are
the same logic regardless of the weapon. Then you can simply create or override weapon specific functions like ShouldReplaceWeaponWith, for comparing weapons, in the derived sheath classes.

Alternatively, you could create a component that manages all weapons instead of inheriting a different component for each weapon type. My main issue with going that route is that you still have to manage the socket names and their corresponding states, which would still be best handled by defining new classes and/or structs anyway. You also can’t fall back on the GetComponents function template to find the relevant objects for you. In any case, it’s up to you figure out what will work best for your game, but hopefully this post has given you some different ideas on how to manage your character’s weapons.

Thanks for taking the time to expand on the issue. Tbh I don’t entirely understand your code as a beginner, will need to spend some time on it but I can tell its a more flexible system. I decided on attaching weapons on the character cos it seemed more feasible for me than some alternative approach such as creating an inventory system.

I’ve added bools for each type of weapon pickup to prevent pickups of the same type again and I was gonna use the “hard reference” approach for switching between attached weapons to main hand, now I’ll need to think about how to proceed/revise after studying the code above. Thanks again.

You will need an inventory system to use for the switching of the weapons. Just extend actor and make one. Then spawn it from your character and tie into it thru the character.
In your character file put this and that will get you started.

In character.h file


 UPROPERTY()
TSubclassOf<class AInventoryManager> InventoryManagerClass;

UPROPERTY(Replicated, ReplicatedUsing = OnRep_InvManager)
AInventoryManager* InvManager = nullptr;

UFUNCTION()
void OnRep_InvManager();

In yourcharacter.cpp file
in constructor



InventoryManagerClass = AInventoryManager::StaticClass();


add to GetLifetimeReplicatedProps function


    DOREPLIFETIME_CONDITION(AYourCharacter, InvManager, COND_OwnerOnly);

add to BeginPlay()



// Spawn Inventory Manager
if (HasAuthority() && InvManager == nullptr && InventoryManagerClass != nullptr)
{
//get and spawn the Inventory Manager.
FActorSpawnParameters SpawnInvManInfo;
SpawnInvManInfo.Instigator = GetInstigator();
SpawnInvManInfo.ObjectFlags |= RF_Transient; // We never want to save Inventory Manager into a map
InvManager = GetWorld()->SpawnActor<AInventoryManager>(InventoryManagerClass.Get(), SpawnInvManInfo);

if (InvManager == nullptr)
{
UE_LOG(LogCharacter, Log, TEXT("AYourCharacter::BeginPlay() Warning! Couldn't spawn InventoryManager"));
}
else
{
InvManager->SetupInvManager(this);
UE_LOG(LogCharacter, Log, TEXT("AYourCharacter::BeginPlay() InvManager = %s"), *InvManager->GetName());
}
}


void AYourCharacter::OnRep_InvManager()
{
InvManager = this->InvManager;
}