Broken Tutorial?

Hi, Im getting stuck on official tutorial Adding FP camera Adding a First-Person Camera, Mesh, and Animation | Unreal Engine 5.6 Documentation | Epic Developer Community
At the end I have a strange result:

  1. My camera look in wrong direction by 90 degrees in yaw, changing rotation and position via C++ or editor has no effect, and i cant understand why. Seems like its simply ignored, but FOV works great. I can change the mesh rotation to the camera, but i think its a dirty fix
  2. Character have animation, but shadow of the character is static, I fixed it by setting animation to the original mesh. But i dont know is it good workaround

I’ll be glad if you help me figure it out, sorry for my English

StudyCharacter.h

#pragma once

#include "CoreMinimal.h"
#include "EnhancedInputComponent.h"
#include "Camera/CameraComponent.h"
#include "InputActionValue.h"
#include "EnhancedInputSubsystems.h"
#include "GameFramework/Character.h"
#include "StudyCharacter.generated.h"

class UInputMappingContext;
class UInputAction;
class UInputComponent;
class UAnimBlueprint;
class UInputMappingContext;
class UInputAction;
class UInputComponent;

UCLASS()
class STUDYGAME_API AStudyCharacter : public ACharacter
{
	GENERATED_BODY()

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

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

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

	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;

	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 primitives field of view
	UPROPERTY(EditAnywhere, Category = Camera)
	float FirstPersonFieldOfView = 70.0f;

	// First-person primitives 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;
};

StudyCharacter.cpp

#include "StudyCharacter.h"

// Sets default values
AStudyCharacter::AStudyCharacter()
{
	// 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(GetMesh());

	// 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
	GetMesh()->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 AStudyCharacter::BeginPlay()
{
	Super::BeginPlay();

	check(GEngine != nullptr);

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

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

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

	// Position the camera slightly above the eyes.
	FirstPersonCameraComponent->SetRelativeLocation(FVector(2.8f, 5.9f, 0.0f));

	// Set the animations on the first person mesh.
	FirstPersonMeshComponent->SetAnimInstanceClass(FirstPersonDefaultAnim->GeneratedClass);
	
	// FIX STATIC SHADOW BUG?
	GetMesh()->SetAnimInstanceClass(FirstPersonDefaultAnim->GeneratedClass);

	// Doesnt work
	// FirstPersonCameraComponent->SetRelativeLocationAndRotation(FirstPersonCameraOffset, FRotator(0.0f, 180.0f, -90.0f));

	// 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 AStudyCharacter."));
}

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

}

// Called to bind functionality to input
void AStudyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	if (UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent))
	{
		// Bind Look Actions
		EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &AStudyCharacter::Look);

		// Bind Movement Actions
		EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AStudyCharacter::Move);

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

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

	// Check if the controller posessing 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 AStudyCharacter::Look(const FInputActionValue& Value)
{
	const FVector2D LookAxisValue = Value.Get<FVector2D>();

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

BP settings of the character if it helps
Camera on preview looks like it should, but it doesn’t work

I’m actually going through this course now and running into the same issue. Ill keep trying some things. Let me know if you find a fix!

1 Like

Yeah, same issue here. I had to change the mesh translation settings by copying the settings in the BP_FirstPersonCharacter. Besides that, I am still having issues with the camera, it’s located too much to the back, so my body is clipped.

The same problem with rotation, shadow and position of the skeleton relative to the capsule.
@Epic_ELT Unreal Engine team, please help!

I also have a question - how to correctly update the blueprint after changes in the class constructor in VS?
The fact is that after initial initialization no changes occur in the engine. At first, I couldn’t figure out what was going on for a long time. Then I decided to delete and rebuild the blueprint from the C++ class. This is the only way to get an updated result. Perhaps there is some more correct way without deleting? Which one?

Hi , I have gone through this tutorial and was able to reproduce its all chapters , my project files can be found Here if you are interested….

Unless I’m just completely missing something, there is a seriously confusing error in here. It won’t stop the code from working, but it does directly contradict the previous tutorial that feeds into this one.

If you follow the previous tutorials in this learning path, you should have a character header file with protected properties like this:

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

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input)
	TObjectPtr<UInputMappingContext> FirstPersonContext;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input)
	TObjectPtr<UInputAction> MoveAction;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input)
	TObjectPtr<UInputAction> JumpAction;

Note that the TObjectPtr wrapper is used around the raw pointer.

But in this tutorial, which is meant to proceed from where that one ended, the same properties are declared as raw pointers:

	protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;
 
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input)
	UInputMappingContext* FirstPersonContext;
 
	// Move Input Actions
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input)
	UInputAction* MoveAction;
 
	// Jump Input Actions
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input)
	UInputAction* JumpAction;

In fact, all properties are declared as raw pointers, even though the previous tutorials explained why this isn’t best practice.

As a beginner working through this to get a grip on basic concepts and best practices, this is massively confusing.

1 Like

Thank you for posting this! I was starting to feel that there was something wrong with me, but, reading your post, as well as the other ones in this thread, I’m becoming more and more confident that several small details with this tutorial series are wrong and can steer newcomers into bad directions. I understand that experienced users probably wouldn’t struggle with following this series, but small details matter for inexperienced users, for whom the series is intended.

First, as you mention, earlier in the series it is stressed that one should use TObjectPtr, as opposed to raw pointers, but later in the series the author(s) revert to using raw pointers. In a similar fashion, it is stressed that forward declaration in C++ header files should be used whenever possible, and inclusion deferred to the implementation files, but that recommendation is not followed by the articles in this series, where both forward declarations and inclusion directives are used inside the header files, which, as far as my understanding goes, defeats the whole purpose of forward declaration. So, for instance, at the top of the AdventureCharacter.h header file, one should have something like this:

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "AdventureCharacter.generated.h"

class UInputMappingContext;
class UInputAction;
class UInputComponent;
class UCameraComponent;
class USkeletalMeshComponent;
class UAnimBlueprint;
struct FInputActionValue;

and instead defer the inclusion directives to the top of the AdventureCharacter.cpp implementation file; e.g.

#include "AdventureCharacter.h"

#include "Camera/CameraComponent.h"
#include "Components/SkeletalMeshComponent.h"
#include "EnhancedInputcomponent.h"
#include "EnhancedInputSubsystems.h"
#include "InputActionValue.h"
#include "Engine/Engine.h"
#include "Engine/LocalPlayer.h"
#include "UObject/ConstructorHelpers.h"
#include "Animation/AnimBlueprint.h"

Note that I’m currently only at the end of the Add a First-Person Camera, Mesh, and Animation guide, so more forward declarations and inclusions will probably be required further down the series, but I think the above example is still useful for conveying my point.

I found another potential problem in the Add a First-Person Camera, Mesh, and Animation guide: simply calling FirstPersonCameraComponent->SetupAttachment(FirstPersonMeshComponent, FName("head")); inside the constructor may not be enough to properly set things up for the BP_AdventureCharacter. I’m not knowledgeable enough about UE5, at this point in my journey, to say for sure, but the fact that the constructor still doesn’t have information about the mesh (in this case Manny) when setting up the attachment could be a problem, since how would it know about the “head” socket. Again, maybe it’s all fine and it’s just me who doesn’t understand enough how things work, but positioning and rotating didn’t work for me until I manually added stuff like this in my constructor:

static ConstructorHelpers::FObjectFinder<USkeletalMesh> MeshAssetThirdPerson(TEXT("/Script/Engine.SkeletalMesh'/Game/Characters/Mannequins/Meshes/SKM_Manny_Simple.SKM_Manny_Simple'"));
check(MeshAssetThirdPerson.Succeeded());

static ConstructorHelpers::FObjectFinder<USkeletalMesh> MeshAssetFirstPerson(TEXT("/Script/Engine.SkeletalMesh'/Game/Characters/Mannequins/Meshes/SKM_Manny_Simple.SKM_Manny_Simple'"));
check(MeshAssetFirstPerson.Succeeded());

GetMesh()->SetSkeletalMeshAsset(MeshAssetThirdPerson.Object);
GetMesh()->SetRelativeLocation(FVector(-20.0f, 0.0f, -96.0f));
GetMesh()->SetRelativeRotation(FRotator(0.0f, -90.0f, 0.0f));
GetMesh()->SetRelativeScale3D(FVector(1.0f, 1.0f, 1.0f));

FirstPersonMeshComponent->SetSkeletalMeshAsset(MeshAssetFirstPerson.Object);

if (FirstPersonDefaultAnim != nullptr)
{
    FirstPersonMeshComponent->SetAnimInstanceClass(FirstPersonDefaultAnim->GeneratedClass);
}

I want to stress that I doubt that the above code snippet is appropriate for real projects, but it did (along with other small hacks like this one) allow me to carry on with the tutorial, so I’m posting here in case it could be of any help to other beginners trying to follow this tutorial series.

One final thing that helped me that I would like to share (for other beginners like me that could struggle with this series) is the fact that there appears to be some caching going on when subclassing the adventure character class with a blueprint (not when subclassing, but when using the subclassed blueprint). That happens when starting to use the blueprint to override properties and later add new operations inside the constructor of the C++ parent class, which may get overridden by the blueprint subclass, and therefore never apparently take effect. One trick that’s useful is to open the blueprint subclass inside the blueprint explorer and click “File > Reparent > Actor”, at which point you will get a dialog warning you that you will lose all data associated with the blueprint, which is what you want for a “fresh start”, when you suspect that caching has become an issue. Then, you “reparent” again back to your character class, and start over with the overrides inside the blueprint (e.g., IA_Move, IA_Look, Animation, etc.).

I will keep posting my findings here as I go. I would appreciate that anyone who reads my post and identifies conceptual errors in what I have said comes forward and explains what they are. That would be immensely appreciated and useful (to me and others).

Thank you to everybody who has contributed to this thread.

For the record, I was using Unreal Engine version 5.6.1 at the time of writing.