How to add collision to Pawn?

I have a Pawn that is basically just a point camera, I’d like to have this pawn bump into walls.

Here’s my progress so far…


#pragma once

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

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

public:

	AVehicleEditorPawn();

	virtual void BeginPlay() override;

	virtual void Tick(float DeltaSeconds) override;

	virtual void SetupPlayerInputComponent(class UInputComponent *) override;

	UFUNCTION(BlueprintCallable, Category = "Physics")
		void OnHit(AActor * SelfActor, UPrimitiveComponent * OtherActor, FVector NormalImpulse, const FHitResult& Hit);

protected:

	UPROPERTY(EditAnywhere)
		UCameraComponent* CameraComponent;

	UPROPERTY(EditAnywhere)
		USphereComponent * SphereCollisionComponent;


#include "Editor4.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("Camera Component"));
	CameraComponent->AttachTo(RootComponent);

	// Create and setup the sphere collision component
	SphereCollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("Collision Component"));
	SphereCollisionComponent->AttachTo(RootComponent);
	SphereCollisionComponent->SetSphereRadius(50.f);
	SphereCollisionComponent->BodyInstance.SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics, true);
	SphereCollisionComponent->OnComponentHit.AddDynamic(this, &AVehicleEditorPawn::OnHit);

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

void AVehicleEditorPawn::OnHit(AActor * SelfActor, UPrimitiveComponent * OtherActor, FVector NormalImpulse, const FHitResult& Hit)
{
	GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Red, FString::Printf(TEXT("Hit")));
	UE_LOG(LogTemp, Warning, TEXT("HIT!"));
}

The OnHit delegate isn’t getting called, not sure what I’ve missed.

Check you’ve enabled collision on the Sphere and it’s set to be blocked by something too, that’s probably the first point to check!

I’m don’t know how to do that in C++.

I did the following…


SphereCollisionComponent->SetNotifyRigidBodyCollision(true);

But it’s still not working. Should be noted that I’m testing this on BSP geometry.

Also tried…


SphereCollisionComponent->SetCollisionProfileName("Pawn");

To no avail.

Here’s the full 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 EDITOR4_API AVehicleEditorPawn : public APawn
{
	GENERATED_BODY()

public:

	AVehicleEditorPawn();

	virtual void BeginPlay() override;

	virtual void Tick(float DeltaSeconds) override;

	virtual void SetupPlayerInputComponent(class UInputComponent *) override;

	void OnConstruction(const FTransform &) override;

	UFUNCTION(BlueprintCallable, Category = "Physics")
		void OnHit(AActor * SelfActor, UPrimitiveComponent * OtherActor, FVector NormalImpulse, const FHitResult& Hit);

protected:

	FVector MovementInput;

	FVector2D CameraInput;

	float CameraZoom;

	UPROPERTY(EditAnywhere)
		UCameraComponent* CameraComponent;

	UPROPERTY(EditAnywhere)
		USphereComponent * SphereCollisionComponent;

private:

	FInputModeGameAndUI MouseInputMode, CameraInputMode;

	APlayerController * PlayerController;

	FViewport * Viewport;

	FVector2D MousePosition;

	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 "Editor4.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"));
	SphereCollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("Collision Component"));
	RootComponent = SphereCollisionComponent;

	// Create and setup the camera
	CameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera Component"));
	CameraComponent->AttachTo(RootComponent);

	// Create and setup the sphere collision component
	//SphereCollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("Collision Component"));
	//SphereCollisionComponent->AttachTo(RootComponent);
	SphereCollisionComponent->SetSphereRadius(50.f);
	SphereCollisionComponent->SetNotifyRigidBodyCollision(true);
	SphereCollisionComponent->OnComponentHit.AddDynamic(this, &AVehicleEditorPawn::OnHit);

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

void AVehicleEditorPawn::OnConstruction(const FTransform &)
{

}

void AVehicleEditorPawn::OnHit(AActor * SelfActor, UPrimitiveComponent * OtherActor, FVector NormalImpulse, const FHitResult& Hit)
{
	GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Red, FString::Printf(TEXT("Hit")));
	UE_LOG(LogTemp, Warning, TEXT("HIT!"));
}

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

	// Grab a ref. to the PlayerController
	PlayerController = (APlayerController *)GetController();

	// Grab a ref. to the Viewport
	Viewport = CastChecked<ULocalPlayer>(PlayerController->Player)->ViewportClient->Viewport;

	// Set the mouse cursor style
	PlayerController->CurrentMouseCursor = EMouseCursor::Default;
}

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

	// Enable / disable cursor based on Secondary
	PlayerController->bShowMouseCursor = !SecondaryCurrentlyPressed;
	PlayerController->bEnableClickEvents = !SecondaryCurrentlyPressed;
	PlayerController->bEnableMouseOverEvents = !SecondaryCurrentlyPressed;

	// Force hold mouse position
	if (SecondaryCurrentlyPressed) Viewport->SetMouse(MousePosition.X, MousePosition.Y);

	static const auto XAxisNormal = FVector(0, 0, 1);

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

	auto NewLocation = GetActorLocation();

	auto PlayerProjected = FVector::VectorPlaneProject(GetActorForwardVector(), XAxisNormal);
	PlayerProjected.Normalize();

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

	// Camera Zoom
	NewLocation += GetActorForwardVector() * CameraZoom * DeltaTime * 400;
	SetActorLocation(NewLocation);

	// Actor rotation
	auto CameraRotator = RootComponent->GetComponentRotation();
	CameraRotator.Pitch = FMath::Clamp(CameraRotator.Pitch + CameraInput.Y, -89.5f, 89.5f);
	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;

	// Capture mouse position
	if (PlayerController) PlayerController->GetMousePosition(MousePosition.X, MousePosition.Y);
}

// 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
	CameraInput.Y = (SecondaryCurrentlyPressed) ? AxisValue : 0.f;
}

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

// 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()
{

}

Ahhh okay, BSP geo doesn’t actually generate Hit events for collision. It’s a small caveat of using them, so try it with a static mesh instead!

I found this out a while ago when my projectiles would never kill themselves on collision with the floor, turns out it’s just because it was BSP. Cost me hours!

So I spawned a cube into the level and walked into it, nothing.

OK I’ve solved this on my own.

The tutorial I was following suggested directly moving the object, without a movement controller, this WILL NOT WORK WITH PHYSICS. You need a MovementController in order for ANY hit/block event to work.

If you use the DefaultPawn you will have a MovementController made for you. However I went ahead and used Pawn and just added and registered a flying movement controller to my Pawn.

Thanks for the help guys.

OMG, you’ve saved my nervous system. Thank you so much!