How to do it...

  1. Create a new StaticMeshActor class called Spotter. Remember to use the Show All Classes button to select StaticMeshActor as the parent class.
  2. Make sure that the following functions are defined and overridden in the class header:
#pragma once

#include "CoreMinimal.h"
#include "Engine/StaticMeshActor.h"
#include "Spotter.generated.h"

UCLASS()
class CHAPTER_09_API ASpotter : public AStaticMeshActor
{
GENERATED_BODY()

public:
// Sets default values for this actor's properties
ASpotter();

// Called every frame
virtual void Tick(float DeltaSeconds) override;

UFUNCTION(BlueprintImplementableEvent)
void OnPlayerSpotted(APawn* Player);

};
  1. In the implementation file (Spotter.cpp), update the code to the following:
#include "Spotter.h"
#include "ConstructorHelpers.h"
#include "DrawDebugHelpers.h"

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

// Set up visual aspect of the spotter
auto MeshAsset =
ConstructorHelpers::FObjectFinder<UStaticMesh>
(TEXT("StaticMesh'/Engine/BasicShapes/Cone.Cone'"));

UStaticMeshComponent * SM = GetStaticMeshComponent();

if (SM != nullptr)
{
if (MeshAsset.Object != nullptr)
{
SM->SetStaticMesh(MeshAsset.Object);
SM->SetGenerateOverlapEvents(true);
}

SM->SetMobility(EComponentMobility::Movable);
SM->SetRelativeRotation(FRotator(90, 0, 0));
}

}

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

auto EndLocation = GetActorLocation() +
ActorToWorld().TransformVector(FVector(0, 0, -200));

// Check if there is an object in front of us
FHitResult HitResult;
GetWorld()->SweepSingleByChannel(HitResult,
GetActorLocation(), EndLocation, FQuat::Identity,
ECC_Camera, FCollisionShape::MakeSphere(25),
FCollisionQueryParams("Spot", true, this));

APawn* SpottedPlayer = Cast<APawn>(HitResult.Actor.Get());

// If there is call the OnPlayerSpotted function
if (SpottedPlayer != nullptr)
{
OnPlayerSpotted(SpottedPlayer);
}

// Displays where we are checking for collision
DrawDebugLine(GetWorld(), GetActorLocation(), EndLocation, FColor::Red);

}
  1. Compile and start the editor. Find your Spotter class in Content Browser, then left-click and drag a copy out into the game world.
  2. When you play the level, you'll see the red line showing the trace that the Actor is performing:

  1. However, nothing will happen if the player walks in front of it because we haven't implemented our OnPlayerSpotted event.
  2. To implement this event, we need to create a blueprint subclass of our Spotter.
  3. Right-click on Spotter in the Content Browser and select Create Blueprint class based on Spotter. Name the class BPSpotter:

Creating a Blueprint class based on Spotter
  1. Inside the Blueprint editor, click on the Override button in the Functions section of the My Blueprint panel:

  1. Select On Player Spotted:

  1. To see the event, click on the Event Graph tab. Left-click it and drag it away from the white execution pin on our event. In the context menu that appears, select and add a Print String node so that it is linked to the event:

  1. Delete your previous Spotter object in the level and then drag and drop a BPSpotter in. Play the level again and verify that walking in front of the trace that the BPSpotter is using now prints a string to the screen: