Attach a static mesh to socket via C++

Hi everyone.

I’m trying to put a sword in the hand of monster instance via C++ code. So, we have this code, which compile successfully:

MeleeWeapon.h

#pragma once
#include "GameFramework/Actor.h"
#include "MeleeWeapon.generated.h"

class AMonster;

UCLASS()
class LEARNUE4CPPANDACTORS_API AMeleeWeapon : public AActor
{
    GENERATED_UCLASS_BODY()

    // The amount of damage attacks do
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = MeleeWeapon)
    float AttackDamage;
    
    // A list of things the melee weapon already hit this swing?
    // Ensures each thing sword passes thru only gets hit once.
    TArray<AActor*> ThingsHit;
    
    // Prevents damage from occurring on frames where the sword is not swinging.
    bool Swinging;
    
    // "Stop hitting yourself" - used to check if the actor holding
    // the weapon is hitting himself
    AMonster* WeaponHolder;
    
    // The sphere you collide with to pick item up
    UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = MeleeWeapon)
    UBoxComponent* ProxBox;
    
    UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = MeleeWeapon)
    UStaticMeshComponent* Mesh;
    
    UFUNCTION(BlueprintNativeEvent, Category = Collision)
    void Prox( AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult );
    
    void Swing();
    void Rest();
};

Monster.h

#pragma once
#include "GameFramework/Character.h"
#include "Monster.generated.h"

class AMeleeWeapon;

UCLASS()
class LEARNUE4CPPANDACTORS_API AMonster : public ACharacter
{
	GENERATED_UCLASS_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=MonsterProperties)
    float Speed;
    
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=MonsterProperties)
    float HitPoints;
    
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=MonsterProperties)
    int32 Experience;
    
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=MonsterProperties)
    UClass* BPLoot;
    
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=MonsterProperties)
    UClass* BPMeleeWeapon;
    
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=MonsterProperties)
    float BaseAttackDamage;
    
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=MonsterProperties)
    float AttackTimeout;
    
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = MonsterProperties)
    float TimeSinceLastStrike;
    
    UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = Collision)
    USphereComponent* SightSphere;
    
    UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = Collision)
    USphereComponent* AttackRangeSphere;
    
    // The melee weapon instance (set if the character is using a melee weapon)
    AMeleeWeapon* MeleeWeapon;
    
	// Called every frame
	virtual void Tick(float DeltaSeconds) override;
    
    // Utility functions
    inline bool isInAttackRange(float d) {return d < AttackRangeSphere->GetScaledSphereRadius();}
    inline bool isInSightRange(float d) {return d < SightSphere->GetScaledSphereRadius();}
    
    virtual void PostInitializeComponents() override;
};

Monster.cpp

void AMonster::PostInitializeComponents()
{
    Super::PostInitializeComponents();
    
    // instantiate the melee weapon if a bp was selected
    if (BPMeleeWeapon)
    {
        MeleeWeapon = GetWorld()->SpawnActor<AMeleeWeapon>(BPMeleeWeapon, FVector(0), FRotator(0));
        
        if(MeleeWeapon)
        {
            MeleeWeapon->WeaponHolder = this;
            const USkeletalMeshSocket *socket = GetMesh()->GetSocketByName("RightHandSocket");
            socket->AttachActor(MeleeWeapon, GetMesh());  // <-- attempt to put a sword in the right hand
        }
        else
        {
            FString msg = GetName() + FString(" cannot instantiate meleeweapon ") + BPMeleeWeapon->GetName();
            GEngine->AddOnScreenDebugMessage(0, 5.f, FColor::Yellow, msg);
        }
    }
}

As a result after “play” mode Unreal Engine generates the following warning:

Warning AttachTo: '/Game/GameContent/UEDPIE_0_DefaultLevel.DefaultLevel:PersistentLevel.BP_Monster_7.CharacterMesh0' is not static  (in blueprint "BP_Monster"), cannot attach '/Game/GameContent/UEDPIE_0_DefaultLevel.DefaultLevel:PersistentLevel.BP_MeleeSword_C_28.Mesh' which is static to it. Aborting.

It leads to that I don’t see any weapon in the hands of monster. How can I fix this, without using blueprints? I saw solutions with blueprint nodes for this issue, but I want to connect all them together through C++ code.

This is a huge pain and took me days to figure out, but it turned out to not be as complicated as you’d think. I would really suggest creating a weaponclass or something similar to attach to your monster. However, I think you should be able to just attach it from BeginPlay.

As a setup though, you need to create a weapon socket in your enemy skeleton component. We will call it “Weapon”. Just go into your enemy mesh, find the location of his right hand, right click the socket and click to add a socket.

WeaponMesh->SetSimulatePhysics(false);
	WeaponMesh->SetCollisionEnabled(ECollisionEnabled::PhysicsOnly);
	WeaponMesh->AttachToComponent(Component, FAttachmentTransformRules(EAttachmentRule::SnapToTarget, true), TEXT("Weapon"));

So you see, it’s actually a bit simpler than what you’re trying to do.

I’ve tried to use this code, but compiler generates errors for the following code:

MeleeWeapon->Mesh->SetSimulatePhysics(false);
MeleeWeapon->Mesh->SetCollisionEnabled(ECollisionEnabled::PhysicsOnly);
MeleeWeapon->Mesh->AttachToComponent(this, FAttachmentTransformRules(EAttachmentRule::SnapToTarget, true), TEXT("RightHandSocket"));

where in the 3rd line of code I’m trying to connect mesh with socket in a monster mesh. It has a “RightHandSocket” name.
So, there are errors which I’m taking from the compiler:

Info                                                                                  ^
Info /Users/savicvalera/Documents/Unreal Projects/LearnUE4CppAndActors/Source/LearnUE4CppAndActors/Monster.cpp:76:32: error: no member named 'AttachToComponent' in 'UStaticMeshComponent'
Info             MeleeWeapon->Mesh->AttachToComponent(this, FAttachmentTransformRules(EAttachmentRule::SnapToTarget, true), TEXT("RightHandSocket"));
Info             ~~~~~~~~~~~~~~~~~  ^
Info /Users/savicvalera/Documents/Unreal Projects/LearnUE4CppAndActors/Source/LearnUE4CppAndActors/Monster.cpp:76:82: error: use of undeclared identifier 'EAttachmentRule'
Info             MeleeWeapon->Mesh->AttachToComponent(this, FAttachmentTransformRules(EAttachmentRule::SnapToTarget, true), TEXT("RightHandSocket"));

Also I’ve tried to change “Mobility” option from static to movable in my BP_MeleeSword blueprint. But in this case I was using my code, which I’d described at the first post. It is solve issue with the generated warning, but not solve an issue with viewing sword in the right hand of monster.

MeleeWeapon->Mesh->AttachToComponent(this, FAttachmentTransformRules(EAttachmentRule::SnapToTarget, true), TEXT(“RightHandSocket”));
After FAttachmentTransforRules, replace (EAttachentRule::SnapToTarget with ::SnapToTargetNotIncludingScale

That’s not solve an issue also. I’m catching almost the same error:

Info             MeleeWeapon->Mesh->AttachToComponent(this, FAttachmentTransformRules(EAttachmentRule::SnapToTargetNotIncludingScale, true), TEXT("RightHandSocket"));
Info /Users/savicvalera/Documents/Unreal Projects/LearnUE4CppAndActors/Source/LearnUE4CppAndActors/Monster.cpp:76:32: error: no member named 'AttachToComponent' in 'UStaticMeshComponent'
Info             MeleeWeapon->Mesh->AttachToComponent(this, FAttachmentTransformRules(EAttachmentRule::SnapToTargetNotIncludingScale, true), TEXT("RightHandSocket"));
Info             ~~~~~~~~~~~~~~~~~  ^
Info /Users/savicvalera/Documents/Unreal Projects/LearnUE4CppAndActors/Source/LearnUE4CppAndActors/Monster.cpp:76:82: error: use of undeclared identifier 'EAttachmentRule'

This code are compiled successfully, but as before, I don’t see sword in the hand of monster:

MeleeWeapon->Mesh->SetSimulatePhysics(false);
MeleeWeapon->Mesh->SetCollisionEnabled(ECollisionEnabled::PhysicsOnly);
MeleeWeapon->K2_AttachRootComponentToActor(this, TEXT("RightHandSocket"), EAttachLocation::SnapToTarget, true);

Did you ever figure this out?

You might want to check that your sword has been created successfully by calling the sword actor’s IsPendingKill() function. Sometimes you get a collision when you spawn an actor and it will destroy it immediately.

BTW I use this pattern…
bomb->AttachToComponent(mesh, FAttachmentTransformRules::SnapToTargetNotIncludingScale, socketName);
Where bomb is my actor (releases when you fire) and mesh is my parent actor’s mesh that has a socket.

No, I didn’t. I’ve tried a lot different cases (with checking socket names, models, code and so on), but none of them doesn’t helped me.

I fixed this issue. The correct peace of code looks like this:
MeleeWeapon->WeaponHolder = this;
MeleeWeapon->AttachRootComponentTo(GetMesh(), FName(TEXT(“RightHandSocket”)), EAttachLocation::SnapToTarget);

And also didn’t forget to check you blueprint with chosen model. Keep in mind, that need to define root component as any collision component (box, capsule. etc.) as root element and mesh as it child. It’s really important, because otherwise model doesn’t rendered in a hand of monster.

And also didn’t forget to check you blueprint with chosen model. Keep in mind, that need to define root component as any collision component (box, capsule. etc.) as root element and mesh as it child. It’s really important, because otherwise model doesn’t rendered in a hand of monster.

Ok and… how can I do this? My attached object (ball) is already in the Character but it’s not in the hand (where the socket is) and its behaviour is static (not moving with the running animation).