Adding to this thread as I encountered the same issue with attaching my Camera to the head socket of my Mesh in the constructor. I believe this is a runtime issue. I managed to solve it by moving most of the logic to PostInitializeComponents
function. Here’s an example of my code:
character.cpp:
AVampireHunterCharacter::AVampireHunterCharacter()
{
...
FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
FollowCamera->SetupAttachment(GetMesh());
}
This code segment works correctly, and I can see in the editor that the camera is properly attached to my mesh component. However, when I specify the socket name, I encounter the same issue mentioned earlier in the thread, where the camera points at the character’s feet. To debug, I attempted to validate the socket’s existence by printing out all sockets for the mesh. I added the following code inside my constructor:
if (GetMesh())
{
TArray<FName> SocketNames = GetMesh()->GetAllSocketNames();
UE_LOG(LogTemplateCharacter, Warning, TEXT("Mesh exists obtaining all sockets:"));
for (const FName &SocketName : SocketNames)
{
UE_LOG(LogTemplateCharacter, Warning, TEXT("Socket: %s"), *SocketName.ToString());
}
if (SocketNames.Num() == 0)
{
UE_LOG(LogTemplateCharacter, Warning, TEXT("No sockets"));
}
}
else
{
UE_LOG(LogTemplateCharacter, Warning, TEXT("No Mesh"));
}
Result:
LogTemplateCharacter: Warning: Mesh exists obtaining all sockets:
LogTemplateCharacter: Warning: No sockets
LogTemplateCharacter: Warning: Mesh exists obtaining all sockets:
LogTemplateCharacter: Warning: No sockets
LogTemplateCharacter: Warning: Mesh exists obtaining all sockets:
LogTemplateCharacter: Warning: No sockets
This explains why we experience this unexpected behavior. While the mesh exists when attaching the camera to it in the constructor, the sockets are not yet initialized. I discovered the PostInitializeComponents
method, which allows us to execute logic after the components have been fully initialized.
I updated my Character.h
and Character.cpp
files to log the results in the PostInitializeComponents
function.
.h:
...
public:
void PostInitializeComponents() override;
.cpp:
void AVampireHunterCharacter::PostInitializeComponents()
{
Super::PostInitializeComponents();
if (GetMesh())
{
TArray<FName> SocketNames = GetMesh()->GetAllSocketNames();
UE_LOG(LogTemplateCharacter, Warning, TEXT("Mesh exists obtaining all sockets:"));
for (const FName &SocketName : SocketNames)
{
UE_LOG(LogTemplateCharacter, Warning, TEXT("Socket: %s"), *SocketName.ToString());
}
if (SocketNames.Num() == 0)
{
UE_LOG(LogTemplateCharacter, Warning, TEXT("No sockets"));
}
}
else
{
UE_LOG(LogTemplateCharacter, Warning, TEXT("No Mesh"));
}
}
Result:
LogTemplateCharacter: Warning: Mesh exists obtaining all sockets:
LogTemplateCharacter: Warning: Socket: weapon_r_muzzle
LogTemplateCharacter: Warning: Socket: foot_r_Socket
LogTemplateCharacter: Warning: Socket: foot_l_Socket
LogTemplateCharacter: Warning: Socket: headSocket
LogTemplateCharacter: Warning: Socket: root
LogTemplateCharacter: Warning: Socket: pelvis
LogTemplateCharacter: Warning: Socket: spine_01
LogTemplateCharacter: Warning: Socket: spine_02
LogTemplateCharacter: Warning: Socket: spine_03
LogTemplateCharacter: Warning: Socket: spine_04
LogTemplateCharacter: Warning: Socket: spine_05
LogTemplateCharacter: Warning: Socket: neck_01
LogTemplateCharacter: Warning: Socket: neck_02
LogTemplateCharacter: Warning: Socket: head
LogTemplateCharacter: Warning: Socket: clavicle_l
LogTemplateCharacter: Warning: Socket: upperarm_l
LogTemplateCharacter: Warning: Socket: lowerarm_l
LogTemplateCharacter: Warning: Socket: lowerarm_twist_02_l
LogTemplateCharacter: Warning: Socket: lowerarm_twist_01_l
LogTemplateCharacter: Warning: Socket: hand_l
...
Seems we now have access to all the sockets to the mesh. Update function to now hold code:
void AVampireHunterCharacter::PostInitializeComponents()
{
Super::PostInitializeComponents();
FollowCamera->SetupAttachment(GetMesh(), TEXT("head"));
}
Does not like when using SetupAttachment here, I get the following error from UE5:
LogOutputDevice: Error: === Handled ensure: ===
LogOutputDevice: Error: Ensure condition failed: !bRegistered [File:D:\build\++UE5\Sync\Engine\Source\Runtime\Engine\Private\Components\SceneComponent.cpp] [Line: 1958]
LogOutputDevice: Error: SetupAttachment should only be used to initialize AttachParent and AttachSocketName for a future AttachToComponent. Once a component is registered you must use AttachToComponent. Owner [/Engine/Transient.World_0:PersistentLevel.BP_ThirdPersonCharacter_C_0], InParent [CharacterMesh0], InSocketName [head]
My workaround is to use SetupAttachment
to attach the camera to the mesh in the constructor, as this works as intended. Then, in this function, I use AttachToComponent
to attach it to the socket. Full result:
.cpp:
AVampireHunterCharacter::AVampireHunterCharacter()
{
...
// Create a follow camera
FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
FollowCamera->SetupAttachment(GetMesh());
}
// This function is needed as sockets are not present during construction
void AVampireHunterCharacter::PostInitializeComponents()
{
Super::PostInitializeComponents();
FollowCamera->AttachToComponent(GetMesh(), FAttachmentTransformRules::SnapToTargetIncludingScale, TEXT("head"));
FollowCamera->bUsePawnControlRotation = true;
FollowCamera->SetRelativeLocation(FVector(0, 15, 0));
}
Note that I added the relative location to prevent the camera from clipping through the character. However, this change does not reflect in the Editor. Enabling the flag or modifying the location values has no visible effect in the detail panel, but after a live code sync, the changes are updated when running the game.
Hope this helps someone else, as I wanted to achieve this via C++ and not by Blueprints. Note version of Unreal I am on is 5.5.1