Custom First-Person Character Appears Transparent While Looking Down – Why?

I’m following the official tutorial “Code a First-Person Adventure Game”, specifically the section “Add a First-Person Camera, Mesh, and Animation,” and I ran into an issue.

I have confirmed that my C++ code is copied exactly as in the tutorial, and the character can move and rotate correctly. However, I noticed a strange phenomenon in-game:
When I move the mouse downward to look at my own body, the character’s torso becomes transparent — I can only see the legs and arms.

Here is a comparison:

  • My BP_AdventureCharacter (based on my C++ class): the torso becomes transparent.

  • The engine’s default BP_FirstPersonCharacter (official template): displays correctly, no transparency.

I want to ask:
Why does the default template character render correctly, while my custom character appears partially transparent?
Did I miss a step or setting somewhere?

The following image shows my BP_AdventureCharacter configuration.

This effect (the dissapearing) is called “backface culling”. In essence it makes any surface invisible when viewed from the inside out. It’s an optimization thing.

You can combat that by using 2-sided materials but the issue is not that. Your camera is actually inside the model and there is no way to make it look good even if rendered. (it will look like an empty sack)

To fix this, move the camera position a bit forward in the character so when looking down you are looking from somewhere around the nose instead of down the neck of the character.

1 Like

Thank you for your guidance, but I still have a question: In the official template BP_FirstPersonCharacter, the camera position is the same as mine, yet it can display normally without the need for a forward offset. Why is this the case? Does everyone encounter the same issue, or is it just me?

have you got “do collision test” enabled on camera? perhaps it is hiding the mesh because of your collision channel and the collision from camera test?

Please, check again.

I managed to produce very similar thing by centering the meshes and the camera in the default First Person Character.

1 Like

I would say this issue is not solved. I followed this tutorial and encountered exactly the same issue.

If you open the blueprint details for the default BP_FirstPersonCharacter, the camera transform location is 2.79388, 5.880724, 0.0 and likewise, in the tutorial it is:

FVector FirstPersonCameraOffset = FVector(2.8f, 5.9f, 0.0f);

If you check out the OP again, you can see that when looking down with BP_AdventureCharacterthe entire body is clipped, while with BP_FirstPersonCharacter only the chest is, despite the C++ implementation using the same positions.

Moving the camera forward to match BP_FirstPersonCharacter as suggested meant moving it very far forward to the point where it was in front of the face and it caused the hands to then be clipped while moving.

It’s unclear why there is a difference visually or why the camera would need to be moved forward in the C++ tutorial only.

Here was my C++ tutorial BP_AdventureCharacterwith the positions suggested in the tutorial:

Here was my final code, which produced the unwanted clipping.

NOTE: I had to transform the ThirdPersonMeshComponent to match the BP_FirstPersonCharacter mesh positioning, which is not mentioned in the tutorial, but following the tutorial word for word will give you a result where the character is floating in the air and the camera is rotated 90degrees left. I also matched the capsule half height using the blueprint editor.

AdventureCharacter.cpp

#include "AdventureCharacter.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "Components/CapsuleComponent.h"

// Sets default values
AAdventureCharacter::AAdventureCharacter()
{
	ThirdPersonMeshComponent = GetMesh();
	// Transform the mesh position to match the BP_FirstPersonCharacter
	ThirdPersonMeshComponent->SetRelativeTransform(
		FTransform(
			FRotator(0.0f, -90.0f, 0.0f),
			FVector(-20.0f, 0.0f, -96.f)
		)
	);

	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it
	PrimaryActorTick.bCanEverTick = true;

	// Create a first-person camera component
	FirstPersonCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
	check(FirstPersonCameraComponent != nullptr);

	// Create a first-person mesh component for the owning player
	FirstPersonMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FirstPersonMesh"));
	check(FirstPersonMeshComponent != nullptr);

	// Attach the first-person mesh to the skeletal mesh
	FirstPersonMeshComponent->SetupAttachment(ThirdPersonMeshComponent);

	// The first-person mesh is included in First Person rendering (use FirstPersonFieldofView and FirstPersonScale on this mesh) 
	FirstPersonMeshComponent->FirstPersonPrimitiveType = EFirstPersonPrimitiveType::FirstPerson;

	// Only the owning player sees the first-person mesh
	FirstPersonMeshComponent->SetOnlyOwnerSee(true);

	// The owning player doesn't see the regular (third-person) body mesh, but it casts a shadow
	ThirdPersonMeshComponent->FirstPersonPrimitiveType = EFirstPersonPrimitiveType::WorldSpaceRepresentation;

	// Set the first-person mesh to not collide with other objects
	FirstPersonMeshComponent->SetCollisionProfileName(FName("NoCollision"));

	FirstPersonCameraComponent->SetupAttachment(FirstPersonMeshComponent, FName("head"));

	// Position the camera slightly above the eyes and rotate it to behind the player's head
	FirstPersonCameraComponent->SetRelativeLocationAndRotation(FirstPersonCameraOffset, FRotator(0.0f, 90.0f, -90.0f));
	FirstPersonCameraComponent->bUsePawnControlRotation = true;

	// Enable first-person rendering on the camera and set default FOV and scale values
	FirstPersonCameraComponent->bEnableFirstPersonFieldOfView = true;
	FirstPersonCameraComponent->bEnableFirstPersonScale = true;
	FirstPersonCameraComponent->FirstPersonFieldOfView = FirstPersonFieldOfView;
	FirstPersonCameraComponent->FirstPersonScale = FirstPersonScale;
}

// Called when the game starts or when spawned
void AAdventureCharacter::BeginPlay()
{
	Super::BeginPlay();

	check(GEngine != nullptr);

	// Only the owning player sees the first person mesh
	FirstPersonMeshComponent->SetOnlyOwnerSee(true);

	check(FirstPersonMeshComponent != nullptr);

	// Set the animations on the first person mesh.
	FirstPersonMeshComponent->SetAnimInstanceClass(FirstPersonDefaultAnim->GeneratedClass);

	// The owning player doesn't see the regular (third-person) body mesh
	ThirdPersonMeshComponent->SetOwnerNoSee(true);
	ThirdPersonMeshComponent->SetAnimInstanceClass(FirstPersonDefaultAnim->GeneratedClass);

	// Get the player controller for this character
	if (APlayerController* PlayerController = Cast<APlayerController>(Controller))
	{
		// Get the enhanced input local player subsystem and add a new input mapping context to it
		if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
		{
			Subsystem->AddMappingContext(FirstPersonContext, 0);
		}
	}

	// Display a debug message for five seconds. 
	// The -1 "Key" value argument prevents the message from being updated or refreshed.
	GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("We are using AdventureCharacter."));
}

// Called every frame
void AAdventureCharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

// Called to bind functionality to input
void AAdventureCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	if (UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent))
	{
		// Bind Movement actions
		EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AAdventureCharacter::Move);

		// Bind Jump actions
		EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Started, this, &ACharacter::Jump);
		EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &ACharacter::StopJumping);

		// Bind Look actions
		EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &AAdventureCharacter::Look);
	}
}

void AAdventureCharacter::Move(const FInputActionValue& Value)
{
	// 2D Vector of movement values returned from the input action
	const FVector2D MovementValue = Value.Get<FVector2D>();

	// Check if the controller possessing this Actor is valid
	if (Controller)
	{
		// Add left and right movement
		const FVector Right = GetActorRightVector();
		AddMovementInput(Right, MovementValue.X);

		// Add forward and back movement
		const FVector Forward = GetActorForwardVector();
		AddMovementInput(Forward, MovementValue.Y);


	}
}

void AAdventureCharacter::Look(const FInputActionValue& Value)
{
	const FVector2D LookAxisValue = Value.Get<FVector2D>();

	if (Controller)
	{
		AddControllerYawInput(LookAxisValue.X);
		AddControllerPitchInput(LookAxisValue.Y);
	}
}

AdventureCharacter.h

#pragma once

#include "CoreMinimal.h"
#include "Camera/CameraComponent.h"
#include "GameFramework/Character.h"
#include "InputActionValue.h"
#include "AdventureCharacter.generated.h"

class UInputMappingContext;
class UInputAction;
class UInputComponent;
class UEnhancedInputLocalPlayerSubsystem;

UCLASS()
class ADVENTUREGAME_API AAdventureCharacter : public ACharacter
{
	GENERATED_BODY()

public:
	// Sets default values for this character's properties
	AAdventureCharacter();

	// First Person animations
	UPROPERTY(EditAnywhere, Category = Animation)
	UAnimBlueprint* FirstPersonDefaultAnim;

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

	// Link button presses to actions
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
	TObjectPtr<UInputMappingContext> FirstPersonContext;

	// Move input actions
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
	TObjectPtr<UInputAction> MoveAction;

	// Jump input actions
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
	TObjectPtr<UInputAction> JumpAction;

	// Look input actions
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
	UInputAction* LookAction;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	// Called to bind functionality to input
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

	// Handles 2D Movement Input
	UFUNCTION()
	void Move(const FInputActionValue& Value);

	// Handles Look Input
	UFUNCTION()
	void Look(const FInputActionValue& Value);

	// First Person camera
	UPROPERTY(VisibleAnywhere, Category = Camera)
	UCameraComponent* FirstPersonCameraComponent;

	// Offset for the first-person camera
	UPROPERTY(EditAnywhere, Category = Camera)
	FVector FirstPersonCameraOffset = FVector(2.8f, 5.9f, 0.0f);

	// First-person camera field of view
	UPROPERTY(EditAnywhere, Category = Camera)
	float FirstPersonFieldOfView = 70.0f;

	// First-person camera view scale
	UPROPERTY(EditAnywhere, Category = Camera)
	float FirstPersonScale = 0.6f;

	// First-person mesh, visible only to the owning player
	UPROPERTY(VisibleAnywhere, Category = Mesh)
	USkeletalMeshComponent* FirstPersonMeshComponent;

	USkeletalMeshComponent* ThirdPersonMeshComponent;
};