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;
};