Having an issue getting a C++ Pawn to spawn on level start

I’m roughly following this tutorial.

I’ve created my own GameMode just to set the player pawn.

The Pawn itself is a C++ Pawn class.

Here’s that code.


// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "GameFramework/Pawn.h"
#include "VehicleEditorPawn.generated.h"

UCLASS()
class EDITOR2_API AVehicleEditorPawn : public APawn
{
	GENERATED_BODY()

public:
	// Sets default values for this pawn's properties
	AVehicleEditorPawn();

	// Called when the game starts or when spawned
	virtual void BeginPlay() override;
	
	// Called every frame
	virtual void Tick(float DeltaSeconds) override;

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

protected:

	FVector MovementInput;

	FVector2D CameraInput;

	float CameraZoom;

	UPROPERTY(EditAnywhere)
	USpringArmComponent* CameraSpringArmComponent;

	UPROPERTY(EditAnywhere)
	UCameraComponent* CameraComponent;

private:

	bool PrimaryCurrentlyPressed;
	bool SecondaryCurrentlyPressed;

	// State Input
	void PrimaryPressed();
	void SecondaryPressed();
	void PrimaryReleased();
	void SecondaryReleased();

	// Transmation Input
	void MoveX(float);
	void MoveY(float);
	void MoveZ(float);

	// Camera Input
	void Pitch(float);
	void Yaw(float);
	void Zoom(float);

	// Action Input
	void RotateClockwise();
	void RotateCounterclockwise();
};


// Fill out your copyright notice in the Description page of Project Settings.

#include "Editor2.h"
#include "VehicleEditorPawn.h"

// Sets default values
AVehicleEditorPawn::AVehicleEditorPawn()
{
 	// Set this pawn to call Tick() every frame. You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	// Create and attach the scene component to root
	RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("Root Component"));

	// Create and setup the camera spring arm
	CameraSpringArmComponent = CreateDefaultSubobject<USpringArmComponent>(TEXT("Camera Spring Arm"));
	CameraSpringArmComponent->AttachTo(RootComponent);
	CameraSpringArmComponent->SetRelativeLocationAndRotation(FVector(0.0f, 0.0f, 50.0f), 
		FRotator(-60.0f, 0.0f, 0.0f));
	CameraSpringArmComponent->TargetArmLength = 400.f;
	CameraSpringArmComponent->bEnableCameraLag = true;
	CameraSpringArmComponent->CameraLagSpeed = 3.0f;

	// Create and setup the camera
	CameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("GameCamera"));
	CameraComponent->AttachTo(CameraSpringArmComponent, USpringArmComponent::SocketName);

	// Take control of the default Player
	AutoPossessPlayer = EAutoReceiveInput::Player0;
}

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

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

	// Movement
	if (!MovementInput.IsNearlyZero())
	{
		MovementInput = MovementInput.GetSafeNormal() * 20.0f;

		FVector NewLocation = GetActorLocation();

		NewLocation += GetActorForwardVector() * MovementInput.X * DeltaTime;

		NewLocation += GetActorRightVector() * MovementInput.Y * DeltaTime;

		NewLocation += GetActorUpVector() * MovementInput.Z * DeltaTime;

		SetActorLocation(NewLocation);
	}

	// Camera Zoom
	// Blend our camera's FOV and our SpringArm's length based on ZoomFactor
	CameraComponent->FieldOfView = FMath::Lerp<float>(90.0f, 60.0f, CameraZoom);
	CameraSpringArmComponent->TargetArmLength = FMath::Lerp<float>(400.0f, 300.0f, CameraZoom);

	// Camera Pitch
	FRotator CameraRotator = CameraSpringArmComponent->GetComponentRotation();
	CameraRotator.Pitch = FMath::Clamp(CameraRotator.Pitch + CameraInput.Y, -80.0f, -15.0f);
	CameraSpringArmComponent->SetWorldRotation(CameraRotator);

	// Camera Yaw
	FRotator ActorRotator = GetActorRotation();
	ActorRotator.Yaw += CameraInput.X;
	SetActorRotation(ActorRotator);
}

// Called to bind functionality to input
void AVehicleEditorPawn::SetupPlayerInputComponent(class UInputComponent * InputComponent)
{
	Super::SetupPlayerInputComponent(InputComponent);

	// Bind input for status based input
	InputComponent->BindAction("Primary",
		IE_Pressed, this, &AVehicleEditorPawn::PrimaryPressed);
	InputComponent->BindAction("Secondary",
		IE_Pressed, this, &AVehicleEditorPawn::SecondaryPressed);
	InputComponent->BindAction("Primary",
		IE_Released, this, &AVehicleEditorPawn::PrimaryReleased);
	InputComponent->BindAction("Secondary",
		IE_Released, this, &AVehicleEditorPawn::SecondaryReleased);

	// Bind input for transmation
	InputComponent->BindAxis("Move_X", this, &AVehicleEditorPawn::MoveX);
	InputComponent->BindAxis("Move_Y", this, &AVehicleEditorPawn::MoveY);
	InputComponent->BindAxis("Move_Z", this, &AVehicleEditorPawn::MoveY);

	// Bind input for camera
	InputComponent->BindAxis("Aim_Y", this, &AVehicleEditorPawn::Pitch);
	InputComponent->BindAxis("Aim_X", this, &AVehicleEditorPawn::Yaw);
	InputComponent->BindAxis("Scroll", this, &AVehicleEditorPawn::Zoom);

	// Bind input for editor actions
	InputComponent->BindAction("Editor Rotate Clockwise",
		IE_Pressed, this, &AVehicleEditorPawn::RotateClockwise);
	InputComponent->BindAction("Editor Rotate Counterclockwise",
		IE_Pressed, this, &AVehicleEditorPawn::RotateCounterclockwise);

}

// Primary Pressed
void AVehicleEditorPawn::PrimaryPressed()
{
	PrimaryCurrentlyPressed = true;
}

// Primary Released
void AVehicleEditorPawn::PrimaryReleased()
{
	PrimaryCurrentlyPressed = false;
}

// Secondary Pressed
void AVehicleEditorPawn::SecondaryPressed()
{
	SecondaryCurrentlyPressed = true;
}

// Secondary Released
void AVehicleEditorPawn::SecondaryReleased()
{
	SecondaryCurrentlyPressed = false;
}

// Transmation X
void AVehicleEditorPawn::MoveX(float AxisValue)
{
	MovementInput.X = FMath::Clamp<float>(AxisValue, -1.0f, 1.0f);
}

// Transmation Y
void AVehicleEditorPawn::MoveY(float AxisValue)
{
	MovementInput.Y = FMath::Clamp<float>(AxisValue, -1.0f, 1.0f);
}

// Transmation Z
void AVehicleEditorPawn::MoveZ(float AxisValue)
{
	MovementInput.Z = FMath::Clamp<float>(AxisValue, -1.0f, 1.0f);
}

// Camera Pitch
void AVehicleEditorPawn::Pitch(float AxisValue)
{
	// Only move the camera if the player is holding down the right mouse button
	if (SecondaryCurrentlyPressed) CameraInput.Y = AxisValue;
}

// Camera Yaw
void AVehicleEditorPawn::Yaw(float AxisValue)
{
	// Only move the camera if the player is holding down the right mouse button
	if (SecondaryCurrentlyPressed) CameraInput.Y = CameraInput.X = AxisValue;
}

// Camera Zoom
void AVehicleEditorPawn::Zoom(float AxisValue)
{
	CameraZoom = FMath::Clamp<float>(AxisValue, -1.0f, 1.0f);
}

// Rotate Clockwise
void AVehicleEditorPawn::RotateClockwise()
{

}

// Rotate Counterclockwise
void AVehicleEditorPawn::RotateCounterclockwise()
{

}

I don’t have any of these pawns in the level nor do I have any kind of spawn / player start.

When I run the game I game basically a fixed view from whatever position I was in on the editor.

I’m sure I’ve missed (at least one) small thing.

EDIT: I’m confused, aside from the issue possessing the Pawn, this guide doesn’t create a MovementController of any kind.

A small update, I didn’t manage to set the GameMode in Project Settings > Maps & Modes. It seems I now spawn in (again wherever the editors camera was) but with my custom Pawn.

Movement controls aren’t working, I’m guessing this is due to the lack of a MovementController. I’m not really sure what to do about that atm.

Right clicking to pitch/yaw the Camera does work but whenever I let go of the right mouse button the camera keeps on drifting in whatever direction I was moving in.

OK I managed to figure this issue out and solved it with the following tweak:


// Camera Pitch
void AVehicleEditorPawn::Pitch(float AxisValue)
{
	// Only move the camera if the player is holding down the right mouse button
	if (SecondaryCurrentlyPressed)
		CameraInput.Y = AxisValue;
	else
		CameraInput.Y = 0.f;
}

// Camera Yaw
void AVehicleEditorPawn::Yaw(float AxisValue)
{
	// Only move the camera if the player is holding down the right mouse button
	if (SecondaryCurrentlyPressed)
		CameraInput.X = CameraInput.X = AxisValue;
	else
		CameraInput.X = 0.f;
}

Zooming tries to work for like a frame and then snaps back, like the value is being reset or going back or something. This one actually makes sense, I think I know how to fix this. Instead of mapping it directly to the axis, I need to add the axis value times some scale factor to a current zoom variable (is my guess).

Could you guys point me in the right direction regarding the movement issue. I don’t really understand what a MovementController is / what it does / how to add one to a Pawn.

Thanks.

This issue ended up being a bunch of surprisingly simple mistakes on my part. After specifying the correct GameMode, I got control of my Pawn.

Right clicking to move the view did work, but the view kept moving afterwards, a simple else clause fixed this.

Zooming for tweaked to += a clamped [0, 1] value which was lerp’d between an FOV of 60 to 15

And finally moving, best I can tell it was working just really slowly, a much higher value * the normal of the FVector worked out (I used 400 vs 20).

For anyone who cares, here’s the working code:


// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "GameFramework/Pawn.h"
#include "VehicleEditorPawn.generated.h"

UCLASS()
class EDITOR2_API AVehicleEditorPawn : public APawn
{
	GENERATED_BODY()

public:
	// Sets default values for this pawn's properties
	AVehicleEditorPawn();

	// Called when the game starts or when spawned
	virtual void BeginPlay() override;
	
	// Called every frame
	virtual void Tick(float DeltaSeconds) override;

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

protected:

	FVector MovementInput;

	FVector2D CameraInput;

	float CameraZoom;

	UPROPERTY(EditAnywhere)
	UCameraComponent* CameraComponent;

private:

	bool PrimaryCurrentlyPressed;
	bool SecondaryCurrentlyPressed;

	// State Input
	void PrimaryPressed();
	void SecondaryPressed();
	void PrimaryReleased();
	void SecondaryReleased();

	// Transmation Input
	void MoveX(float);
	void MoveY(float);
	void MoveZ(float);

	// Camera Input
	void Pitch(float);
	void Yaw(float);
	void Zoom(float);

	// Action Input
	void RotateClockwise();
	void RotateCounterclockwise();
};


// Fill out your copyright notice in the Description page of Project Settings.

#include "Editor2.h"
#include "VehicleEditorPawn.h"

// Sets default values
AVehicleEditorPawn::AVehicleEditorPawn()
{
 	// Set this pawn to call Tick() every frame. You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	// Create and attach the scene component to root
	RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("Root Component"));

	// Create and setup the camera
	CameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("GameCamera"));
	CameraComponent->AttachTo(RootComponent, USpringArmComponent::SocketName);

	// Take control of the default Player
	AutoPossessPlayer = EAutoReceiveInput::Player0;
}

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

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

	// Movement
	MovementInput = MovementInput.GetSafeNormal() * 400.0f;

	FVector NewLocation = GetActorLocation();

	NewLocation += GetActorForwardVector() * MovementInput.Y * DeltaTime;
	NewLocation += GetActorRightVector() * MovementInput.X * DeltaTime;
	NewLocation += GetActorUpVector() * MovementInput.Z * DeltaTime;

	SetActorLocation(NewLocation);

	// Camera Zoom
	CameraComponent->FieldOfView = FMath::Lerp<float>(60.0f, 15.0f, CameraZoom);

	// Actor rotation
	FRotator CameraRotator = RootComponent->GetComponentRotation();
	CameraRotator.Pitch = FMath::Clamp(CameraRotator.Pitch + CameraInput.Y, -85.0f, 85.0f);
	CameraRotator.Yaw += CameraInput.X;
	CameraRotator.Roll = 0;
	SetActorRotation(CameraRotator);
}

// Called to bind functionality to input
void AVehicleEditorPawn::SetupPlayerInputComponent(class UInputComponent * InputComponent)
{
	Super::SetupPlayerInputComponent(InputComponent);

	// Bind input for status based input
	InputComponent->BindAction("Primary",
		IE_Pressed, this, &AVehicleEditorPawn::PrimaryPressed);
	InputComponent->BindAction("Secondary",
		IE_Pressed, this, &AVehicleEditorPawn::SecondaryPressed);
	InputComponent->BindAction("Primary",
		IE_Released, this, &AVehicleEditorPawn::PrimaryReleased);
	InputComponent->BindAction("Secondary",
		IE_Released, this, &AVehicleEditorPawn::SecondaryReleased);

	// Bind input for transmation
	InputComponent->BindAxis("Move_X", this, &AVehicleEditorPawn::MoveX);
	InputComponent->BindAxis("Move_Y", this, &AVehicleEditorPawn::MoveY);
	InputComponent->BindAxis("Move_Z", this, &AVehicleEditorPawn::MoveZ);

	// Bind input for camera
	InputComponent->BindAxis("Aim_Y", this, &AVehicleEditorPawn::Pitch);
	InputComponent->BindAxis("Aim_X", this, &AVehicleEditorPawn::Yaw);
	InputComponent->BindAxis("Scroll", this, &AVehicleEditorPawn::Zoom);

	// Bind input for editor actions
	InputComponent->BindAction("Editor Rotate Clockwise",
		IE_Pressed, this, &AVehicleEditorPawn::RotateClockwise);
	InputComponent->BindAction("Editor Rotate Counterclockwise",
		IE_Pressed, this, &AVehicleEditorPawn::RotateCounterclockwise);

}

// Primary Pressed
void AVehicleEditorPawn::PrimaryPressed()
{
	PrimaryCurrentlyPressed = true;
}

// Primary Released
void AVehicleEditorPawn::PrimaryReleased()
{
	PrimaryCurrentlyPressed = false;
}

// Secondary Pressed
void AVehicleEditorPawn::SecondaryPressed()
{
	SecondaryCurrentlyPressed = true;
}

// Secondary Released
void AVehicleEditorPawn::SecondaryReleased()
{
	SecondaryCurrentlyPressed = false;
}

// Transmation X
void AVehicleEditorPawn::MoveX(float AxisValue)
{
	MovementInput.X = FMath::Clamp<float>(AxisValue, -1.0f, 1.0f);
}

// Transmation Y
void AVehicleEditorPawn::MoveY(float AxisValue)
{
	MovementInput.Y = FMath::Clamp<float>(AxisValue, -1.0f, 1.0f);
}

// Transmation Z
void AVehicleEditorPawn::MoveZ(float AxisValue)
{
	MovementInput.Z = FMath::Clamp<float>(AxisValue, -1.0f, 1.0f);
}

// Camera Pitch
void AVehicleEditorPawn::Pitch(float AxisValue)
{
	// Only move the camera if the player is holding down the right mouse button
	if (SecondaryCurrentlyPressed)
		CameraInput.Y = AxisValue;
	else
		CameraInput.Y = 0.f;
}

// Camera Yaw
void AVehicleEditorPawn::Yaw(float AxisValue)
{
	// Only move the camera if the player is holding down the right mouse button
	if (SecondaryCurrentlyPressed)
		CameraInput.X = CameraInput.X = AxisValue;
	else
		CameraInput.X = 0.f;
}

// Camera Zoom
void AVehicleEditorPawn::Zoom(float AxisValue)
{
	CameraZoom += 0.2 * FMath::Clamp<float>(AxisValue, -1.0f, 1.0f);
	CameraZoom = FMath::Clamp<float>(CameraZoom, 0.0f, 1.0f);
}

// Rotate Clockwise
void AVehicleEditorPawn::RotateClockwise()
{

}

// Rotate Counterclockwise
void AVehicleEditorPawn::RotateCounterclockwise()
{

}

I still have no idea what the hell MovementControllers are/do/how they interact with Pawns, any kind of explanation would be extremely welcomed.