Creating Proximity Trigger and UI Widget

AZCInteractable

Actor representing items in the world that can be picked up but could be extended to anything that can be interacted with (pickup items, environment prompts, effect activations...etc).

  • Keeps track of when actors enter or exit range (based off a proximity trigger).

  • Has a UWidgetComponent who's visibility is turned on or off

Proximity Trigger

Define a USphereComponent and set collision to only notify on Overlap

.h

// ZCInteractable.h

// Proximity trigger to allow interaction (widget popup) or not
UPROPERTY(EditAnywhere, BlueprintReadOnly++)
class USphereComponent* ProximityTrigger = nullptr;

.cpp

// ZCInteractable.cpp

AZCInteractable::AZCInteractable()
{
    //...
    ProximityTrigger = CreateDefaultSubobject<USphereComponent>(TEXT("ProximityTrigger"));
    ProximityTrigger->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Overlap);
    ProximityTrigger->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
    //...
}

Bind to Overlap events

Bind custom UFUNCTION()s to the USphereComponent OnComponentBeginOverlap and OnComponentEndOverlap event delegates

.h

// ZCInteractable.h

UFUNCTION()
void OnBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
	
UFUNCTION()
void OnEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);

NOTE: Ensure the functions are marked as UFUNCTION() otherwise they will not allow binding to the dynamic delegates

.cpp

// ZCInteractable.cpp

AZCInteractable::BeginPlay()
{
    //...
    ProximityTrigger->OnComponentBeginOverlap.AddDynamic(this, &AcInteractable::OnBeginOverlap);
    ProximityTrigger->OnComponentEndOverlap.AddDynamic(this, &AInteractable::OnEndOverlap);
    //...
}

Notify objects entering/exiting proximity

Inform the actor entering or exiting that they are doing so by accessing their InteractableManagerComponent

.cpp

// ZCInteractable.cpp

void AInteractable::OnBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	if (OtherActor)
	{
		// Only actors with this component need to care about being in range or not
		UZCInteractableManager* InteractManager = Char->FindComponentByClass<UZCInteractableManager>();
		if (InteractManager)
		{
			InteractManager->ItemEnteredRange(this);
		}
	}
}

void AInteractable::OnEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
	if (OtherActor)
	{
		// Only actors with this component need to care about being in range or not
		UZCInteractableManager* InteractManager = Char->FindComponentByClass<UZCInteractableManager>();
		if (InteractManager)
		{
			InteractManager->ItemExitedRange(this);
		}
	}
}

Widget Popup

Define a UWidgetComponent and create public visibility setter

.h

// ZCInteractable.h

// UI Widget that shows the player when they are within range of and looking at
UPROPERTY(EditAnywhere, BlueprintReadOnly)
class UWidgetComponent* PopupWidget = nullptr;

.cpp

// ZCInteractable.cpp

AZCInteractable::AZCInteractable()
{
    //...
    PopupWidget = CreateDefaultSubobject<UWidgetComponent>(TEXT("PopupWidget"));
    PopupWidget->SetupAttachment(GetRootComponent());
    //...
}

Visibility Toggle

Need to provide a way for external sources to tell us to turn 'on' or 'off'

.h

// ZCInteractable.h

public:
	// Toggles the popup visibility
	void SetPopupVisibility(bool bVisible);

.cpp

// ZCInteractable.cpp

void AZCInteractable::SetPopupVisibility(bool bVisible)
{
	if (PopupWidget)
	{
		PopupWidget->SetVisibility(bVisible);
	}
}

Blueprint Implementation

Create a Blueprint class using ZCInteractableItem as the base. Make sure to expand the All Classes dropdown to type for our custom class

Assign custom Widget Blueprint class and specify the draw dimensions.

Note: Draw Size is specifying dimensions for the widget on the screen, not world size

Last updated