How did I deal with…?

Implementing an Interface for Video Game Using C++ for Unreal Engine API

The goal of this project is to use one level to prototype the C++ programming for the rest of the video game levels. Gameplay consists of a 3rd person character collecting pickups until all are collected. Once that happens, the exit gates unlock and she can go meet a counselor taking her to the next level in the game. Here you can see the schematics for gameplay in the One Page Design Document:

Single Level from One page design Single Level from One page design
Same logic repeats through all levels

Pickup up item

Here you can see her picking up one of the collectibles in the level (models are placeholders):

Getting close to pickup Getting close to pickup Getting close to pickup
The proximity to the pickup makes it change color and updates the UI.

As you can see, there are a lot of tasks accomplished via interface. These schematics show the interface wiring together the many communication channels:

C++ Logic schematics C++ Logic schematics C++ Logic schematics
General logic after functional Blueprints test in Unreal Engine.

The solution I'm developing here consists of making a prototype of the interface in Blueprints first (I'm working in Unreal Engine) and, when it's functional, translating and reorganizing the tasks in C++ using Visual Studio. This is the functional prototype in Blueprints for the main interaction:

All methods triggered by E key All methods triggered by E key All methods triggered by E key
Sequence of all methods activating when pressing “E” key

This Blueprint is showing how I'm using the interface to execute the following tasks by pressing the “E” key:

  1. Updating the counter
  2. Destroying the collected Actor
  3. Checking out whether all items in the level are collected and, if that's the case
  4. Unlocking the exit gates.

These are the tasks I'm executing to implement these methods in C++

This is what my Interface's header looks like in C++:

    
class SCIFIPROJ_API IInteractInterface
{
	GENERATED_BODY()

public:
	UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Interaction")
	void OnInteract(AActor* Caller);
};


Mapping interface to “E” key

I'm calling the Interface by pressing the “E” key, which I'm mapping to an input action. Here is the definition in the player character's header:

    
/** Interact Input Action */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
class UInputAction* InteractAction;

protected:
/** Called for interacting input */
void Interact(const FInputActionValue& Value);

Additional capsule

Also, I'm creating an additional capsule to overlap with the pickups only:

    
UPROPERTY(VisibleAnywhere, Category = "Trigger Capsule")
class UCapsuleComponent* TriggerCapsule;

Overlapping methods

And here I'm declaring begin and end overlapping functions:

    
public:
	UFUNCTION()
	void OnOverlapBegin(
		UPrimitiveComponent* OverlappedComp,
		AActor* OtherActor,
		UPrimitiveComponent* OtherComp,
		int32 OtherBodyIndex,
		bool bFromSweep,
		const FHitResult& SweepResult);

	UFUNCTION()
	void OnOverlapEnd(
		UPrimitiveComponent* OverlappedCpom,
		AActor* OtherActor,
		UPrimitiveComponent* OtherComp,
		int32 OtherBodyIndex);

Pickup variable

This is the variable I'm using to communicate through the interface with the pickups:


private:
	AActor* OverlappedActor;

When everything is declared in the header, I'm implementing them in the C++ file's constructor:


TriggerCapsule = CreateDefaultSubobject(TEXT("Trigger Capsule"));
	TriggerCapsule->InitCapsuleSize(42.f, 96.0f);
	TriggerCapsule->SetCollisionProfileName(TEXT("Trigger"));
	TriggerCapsule->SetupAttachment(RootComponent);

	TriggerCapsule->OnComponentBeginOverlap.AddDynamic(this, &ASciFiProjCharacter::OnOverlapBegin);
	TriggerCapsule->OnComponentEndOverlap.AddDynamic(this, &ASciFiProjCharacter::OnOverlapEnd);

Implementing in constructor

BeginOverlap function implementation, calling the Interface:


void ASciFiProjCharacter::OnOverlapBegin(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	IInteractInterface* Interface = Cast(OtherActor);

	if (OtherActor && (OtherActor != this) && OtherComp && Interface)
	{
		OverlappedActor = OtherActor;
		CanPickup = true;
		Overlapping_Pickup.Broadcast();
	}
}

“E” key function

Calling the interface and sending messages through delegates by pressing “E” through Interact Input Action:


void ASciFiProjCharacter::Interact(const FInputActionValue& Value)
{
	if (OverlappedActor && CanPickup)
	{
		IInteractInterface* Interface = Cast(OverlappedActor);

		if (Interface && (MissionAccomplished == false))
		{
			Interface->Execute_OnInteract(OverlappedActor, this);
			CollectedItems++;
			Add_Item.Broadcast();
		}
		if (CollectedItems == (TotalItems - 1))
		{
			Interact_OneToGo.Broadcast();
		}

		if (CollectedItems == TotalItems)
		{
			CanPickup = false;
			MissionAccomplished = true;
			Mission_Accomplished.Broadcast();
			All_Pickups_Collected.Broadcast();
			Interact_Finished.Broadcast();
		}
	}
	else
	{
		if (OverlappedActor == nullptr && CanPickup == false && MissionAccomplished == false)
		{
			Interact_Empty.Broadcast();
		}
		if (OverlappedActor == nullptr && CanPickup == false && MissionAccomplished == true)
		{
			Interact_Finished.Broadcast();
		}
	}
}

Interface on pickup

Here I'm calling the interface from my pickup's definition in the header file:

class SCIFIPROJ_API AInteractable_Base : public AActor, public IinteractInterface

And here I'm implementing the interface, right away in the header:


public:
	UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Interaction")
	void OnInteract(AActor* Caller);
	virtual void OnInteract_Implementation(AActor* Caller);

This is the Destroy Actor Command inside the source file from the pickup, also hiding a debug message and sending a delegate:


void AInteractable_Base::OnInteract_Implementation(AActor* Caller)
{
	if (GEngine)
	{
		//GEngine->AddOnScreenDebugMessage(-1, 1.0f, FColor::Emerald, TEXT("Collecting"));

		Interacting.Broadcast();

		Destroy();
	}
}

Summary

This is how the interface tunnels the functions sequence through the Interact Input Action:

  • Updating the counter by implementing Interact function in the player character node, adding #1 to the CollectedItems variable
  • Destroying the collected Actor from inside Interact Action's implementation in the pickup node, and
  • Unlocking the Exit Gates by updating the MissionAccomplished boolean variable, in case the number of CollectedItems = TotalItems' value.

If you want to take a closer look at the gameplay mechanics, please check out these workflow breakdowns:

Programming video game interaction with C++
Programming interaction

The C++ Interface is able to identify when the player picks up an item, adds to the counter, loops remaining and sends delegates when all collected.

Delegates communicating UI and Gameplay
Delegates for UI & Gameplay

Using delegates sent by the interface to push text updates in the User Interface and trigger behaviors in the environment, like opening doors.

Adding Input Action from C++ into Unreal Engine
Adding Input Action in C++

Using the existing C++ third person character syntax to create an additional input action binding on UProperties, protected functions, and source.

Programming Gameplay with C++ for Unreal Engine
Programming Gameplay C++

Programming interaction for gameplay in C++ for Unreal Engine, using an interface, delegates and 2 bases for pickups and doors.

Discover Technical Skills