Tag

engine

Unreal Engine C++ Fundamentals – Using Spline Components

By | Development, Tutorial, Unreal | No Comments

Hey guys,

Today we are going to take a look how to use Spline Components and modify their various properties to allow us to specify custom materials as well as determine how meshes are attached to the spline.

The project files for this video & article can be found on our GitHub page.

Splines splines splines !

So why do we care about splines ? Well splines allow us to construct various objects within our world by providing us with easily extensible and modifiable joined sets of meshes.

Splines can also provide support for navigation by being used as a path for something or someone to move along. In our case though we are going to use it for prop building.

Let’s dive into how to initialize a Spline Component.

#include "Components/SplineComponent.h"

UPROPERTY(VisibleAnywhere, Category = "Spline")
USplineComponent* SplineComponent;

Inside our constructor we simply instantiate it.

// Sets default values
ASplineActor::ASplineActor()
{
 	// 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;

  SplineComponent = CreateDefaultSubobject<USplineComponent>("Spline");
  if(SplineComponent)
  {
    SetRootComponent(SplineComponent);	
  }	
}

With our spline component setup we can proceed with attaching Spline Mesh Components by iterating over the available spline points.

To do this we want to override the OnConstruction method in order to allow us to specify the Spline Mesh Components during the process of modifying our spline within the editor.

void ASplineActor::OnConstruction(const FTransform& Transform)
{	
  Super::OnConstruction(Transform);

  if(SplineComponent && SplineMeshMap.Num() > 0)
  {
    // lookup all pertinent values
    FSplineMeshDetails* StartMeshDetails = nullptr;
    if(SplineMeshMap.Contains(ESplineMeshType::START))
    {
      StartMeshDetails = SplineMeshMap.Find(ESplineMeshType::START);	
    }

    FSplineMeshDetails* EndMeshDetails = nullptr;
    if(SplineMeshMap.Contains(ESplineMeshType::END))
    {
      EndMeshDetails = SplineMeshMap.Find(ESplineMeshType::END);
    }

    FSplineMeshDetails* DefaultMeshDetails = nullptr;
    if(SplineMeshMap.Contains(ESplineMeshType::DEFAULT))
    {
      DefaultMeshDetails = SplineMeshMap.Find(ESplineMeshType::DEFAULT);	
    }
    else
    {
      // exit if we don't have a default mesh to work with
      return;
    }
    
    const int32 SplinePoints = SplineComponent->GetNumberOfSplinePoints();

    for(int SplineCount = 0; SplineCount < (SplinePoints - 1); SplineCount++)
    {
      USplineMeshComponent *SplineMesh = NewObject<USplineMeshComponent>(this, USplineMeshComponent::StaticClass());

      UStaticMesh* StaticMesh = DefaultMeshDetails->Mesh;
      UMaterialInterface* Material = nullptr;
      ESplineMeshAxis::Type ForwardAxis = DefaultMeshDetails->ForwardAxis;
  
      // start mesh
      if(StartMeshDetails && StartMeshDetails->Mesh && SplineCount == 0)
      {
        StaticMesh = StartMeshDetails->Mesh;
        ForwardAxis = StartMeshDetails->ForwardAxis;
        
        if(StartMeshDetails->DefaultMaterial)
        {
          Material = StartMeshDetails->DefaultMaterial;
        }					
      }
      else if(EndMeshDetails && EndMeshDetails->Mesh && SplinePoints > 2 && SplineCount == (SplinePoints - 2))
      {
        // end mesh
        StaticMesh = EndMeshDetails->Mesh;
        ForwardAxis = EndMeshDetails->ForwardAxis;
        
        if(EndMeshDetails->DefaultMaterial)
        {
          Material = EndMeshDetails->DefaultMaterial;
        }	
      }
      else
      {
        // default assignment - middle mesh
        if(DefaultMeshDetails->AlternativeMaterial && SplineCount > 0 && SplineCount % 2 == 0)
        {
          Material = DefaultMeshDetails->AlternativeMaterial;
        }
        else if(DefaultMeshDetails->DefaultMaterial)
        {
          Material = DefaultMeshDetails->DefaultMaterial;
        }			
      }

      // update mesh details
      SplineMesh->SetStaticMesh(StaticMesh);
      SplineMesh->SetForwardAxis(ForwardAxis, true);
      SplineMesh->SetMaterial(0, Material);
      
  
      // initialize the object
      SplineMesh->RegisterComponentWithWorld(GetWorld());
  
      SplineMesh->CreationMethod = EComponentCreationMethod::UserConstructionScript;
      SplineMesh->SetMobility(EComponentMobility::Movable);
  
      SplineMesh->AttachToComponent(SplineComponent, FAttachmentTransformRules::KeepRelativeTransform);
  
      // define the positions of the points and tangents
      const FVector StartPoint = SplineComponent->GetLocationAtSplinePoint(SplineCount, ESplineCoordinateSpace::Type::Local);
      const FVector StartTangent = SplineComponent->GetTangentAtSplinePoint(SplineCount, ESplineCoordinateSpace::Type::Local);
      const FVector EndPoint = SplineComponent->GetLocationAtSplinePoint(SplineCount + 1, ESplineCoordinateSpace::Type::Local);
      const FVector EndTangent = SplineComponent->GetTangentAtSplinePoint(SplineCount + 1, ESplineCoordinateSpace::Type::Local);
      SplineMesh->SetStartAndEnd(StartPoint, StartTangent, EndPoint, EndTangent, true);
  
      // query physics
      SplineMesh->SetCollisionEnabled(ECollisionEnabled::Type::QueryAndPhysics);
    }
  }
}

WHOA that’s a lot of stuff , slow down !

That was a whole pile of code so let’s break it up bit by bit.

void ASplineActor::OnConstruction(const FTransform& Transform)
{	
  Super::OnConstruction(Transform);
}

Here we end up overwriting the OnConstruction method, ensuring we call the parent’s OnConstruction method before we proceed with any of our code. This method is going to be called when our blueprint is being modified in the editor or manipulated in the level.

To provide greater flexibility in our spline actor we introduce a struct and an enum to let us handle multiple different types of meshes / materials and forward axis definitions for our spline mesh components.

UENUM(BlueprintType)
enum class ESplineMeshType: uint8 {
  DEFAULT		UMETA(DisplayName = "Default Mesh"),
  START		UMETA(DisplayName = "Starting Mesh"),
  END			UMETA(DisplayName = "EndingMesh"),
};

USTRUCT(BlueprintType)
struct FSplineMeshDetails : public FTableRowBase
{
  GENERATED_BODY()
  
  UPROPERTY(EditAnywhere, BlueprintReadOnly)
  UStaticMesh* Mesh;

  UPROPERTY(EditAnywhere, BlueprintReadOnly)
  TEnumAsByte<ESplineMeshAxis::Type> ForwardAxis;
  
  UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
  class UMaterialInterface* DefaultMaterial;

  UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
  class UMaterialInterface* AlternativeMaterial;

  FSplineMeshDetails() : ForwardAxis(ESplineMeshAxis::Type::X)
  {
  }
};

We convert this enum / struct combination into a map that will allow us to configure multiple versions of these definitions.

UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Spline")
TMap<ESplineMeshType, FSplineMeshDetails> SplineMeshMap;

From which we can load the appropriate details and make any necessary checks to determine if our map and the contents are valid.

if(SplineComponent && SplineMeshMap.Num() > 0)
{
  // lookup all pertinent values
  FSplineMeshDetails* StartMeshDetails = nullptr;
  if(SplineMeshMap.Contains(ESplineMeshType::START))
  {
    StartMeshDetails = SplineMeshMap.Find(ESplineMeshType::START);	
  }

  FSplineMeshDetails* EndMeshDetails = nullptr;
  if(SplineMeshMap.Contains(ESplineMeshType::END))
  {
    EndMeshDetails = SplineMeshMap.Find(ESplineMeshType::END);
  }

  FSplineMeshDetails* DefaultMeshDetails = nullptr;
  if(SplineMeshMap.Contains(ESplineMeshType::DEFAULT))
  {
    DefaultMeshDetails = SplineMeshMap.Find(ESplineMeshType::DEFAULT);	
  }
  else
  {
    // exit if we don't have a default mesh to work with
    return;
  }
}

The spline is made up of spline points that which allow for transformation of the spline component using the level editor but it also provides you with information that we require for our construction script as each spline point can change.

We need to capture these changes by iterating over the spline points and then update your spline component accordingly.

const int32 SplinePoints = SplineComponent->GetNumberOfSplinePoints();

for(int SplineCount = 0; SplineCount < (SplinePoints - 1); SplineCount++)
{
}

In order to render any meshes along our spline we need to create spline mesh components during our OnConstruction call.

USplineMeshComponent *SplineMesh = NewObject<USplineMeshComponent>(this, USplineMeshComponent::StaticClass());

With the object instantiated we can determine which of the meshes we want to assign.

Do we want a mesh at the start of our spline ? Do we want one at the end ? Do we want to apply some in the middle ? This type of logic fits extremely well with assets like pipes, fences, ladders and various other common props that need to be adjusted to fit their environment as well as provide quick work flow.

Here we set some environments to hold our mesh details. We use a UMaterialInterface in order to provide flexibility of using master or instance materials.

UStaticMesh* StaticMesh = DefaultMeshDetails->Mesh;
UMaterialInterface* Material = nullptr;
ESplineMeshAxis::Type ForwardAxis = DefaultMeshDetails->ForwardAxis;

In order for us to determine which part of our spline we are working on we can simply keep count of the spline points and react accordingly.

// start mesh
if(StartMeshDetails && StartMeshDetails->Mesh && SplineCount == 0)
{
  StaticMesh = StartMeshDetails->Mesh;
  ForwardAxis = StartMeshDetails->ForwardAxis;
  
  if(StartMeshDetails->DefaultMaterial)
  {
    Material = StartMeshDetails->DefaultMaterial;
  }					
}
else if(EndMeshDetails && EndMeshDetails->Mesh && SplinePoints > 2 && SplineCount == (SplinePoints - 2))
{
  // end mesh
  StaticMesh = EndMeshDetails->Mesh;
  ForwardAxis = EndMeshDetails->ForwardAxis;
  
  if(EndMeshDetails->DefaultMaterial)
  {
    Material = EndMeshDetails->DefaultMaterial;
  }	
}
else
{
  // default assignment - middle
  if(DefaultMeshDetails->AlternativeMaterial && SplineCount > 0 && SplineCount % 2 == 0)
  {
    Material = DefaultMeshDetails->AlternativeMaterial;
  }
  else if(DefaultMeshDetails->DefaultMaterial)
  {
    Material = DefaultMeshDetails->DefaultMaterial;
  }			
}

With our assignment determined we register which mesh / material and forward axis we are going to be assigning to our spline mesh component.

// update mesh details
SplineMesh->SetStaticMesh(StaticMesh);
SplineMesh->SetForwardAxis(ForwardAxis, true);
SplineMesh->SetMaterial(0, Material);

Before the spline component is available to our editor and blueprint we need to register it with the world it’s going to be present in.

// initialize the object
SplineMesh->RegisterComponentWithWorld(GetWorld());

We also tell this component that it needs to be processed at construction time.

SplineMesh->CreationMethod = EComponentCreationMethod::UserConstructionScript;

We let the component know it can be moved and adjusted. This can also be static for specific needs.

SplineMesh->SetMobility(EComponentMobility::Movable);

Then finally we attach the spline mesh component to our spline component by keeping it’s transformation relative to the spline component.

SplineMesh->AttachToComponent(SplineComponent, FAttachmentTransformRules::KeepRelativeTransform);

Lastly we need to define the starting / ending points as well as the tangents that control the curvature of the spline points along said spline.

This allows the spline to determine the position of our new mesh along that spline so everything appears one after the other as we extend our spline points.

// define the positions of the points and tangents
const FVector StartPoint = SplineComponent->GetLocationAtSplinePoint(SplineCount, ESplineCoordinateSpace::Type::Local);
const FVector StartTangent = SplineComponent->GetTangentAtSplinePoint(SplineCount, ESplineCoordinateSpace::Type::Local);
const FVector EndPoint = SplineComponent->GetLocationAtSplinePoint(SplineCount + 1, ESplineCoordinateSpace::Type::Local);
const FVector EndTangent = SplineComponent->GetTangentAtSplinePoint(SplineCount + 1, ESplineCoordinateSpace::Type::Local);
SplineMesh->SetStartAndEnd(StartPoint, StartTangent, EndPoint, EndTangent, true);

Lastly we specify the collision response for each mesh component. This allows us to specify and register different overlap / hit events for each part of the spline providing a lot of flexbility.

SplineMesh->SetCollisionEnabled(ECollisionEnabled::Type::QueryAndPhysics);

See not too bad at all and at the end of this little implementation you should have a fairly modular spline actor for various uses.

For further reading about these topics take a look at the following links:

Unreal Engine C++ Fundamentals – Character possession and changing materials

By | Development, Tutorial, Unreal | No Comments

Hey guys,

We are back after a short summer break with more tutorials.

Today we go back to our C++ Fundamentals lessons by looking at possession and how we can control different charactesr in our world. In addition to that we are going to setup a few materials so we can change our characters appearance to showcase the possession a bit better.

As usual you can find the start project on our GitHub page.

Possession ? That sounds scary

Don’t worry it’s not halloween yet, and possession is just a fancy term for taking complete control over another character or actor within your game world.

This allows our player to be able to experience different character behaviors, it allows for playing different stories like in GTA 5 or you can use it for simple mount mechanics. Tons of possibilities.

So let’s take a look at how we possess our characters

// save a copy of our controller
AController* SavedController = GetController();
// unpossess first ( helps with multiplayer )
SavedController->UnPossess();
// disable movement mode
GetCharacterMovement()->SetMovementMode(EMovementMode::MOVE_None);
// possess our new actor
SavedController->Possess(Cast<APawn>(ActorToPossess));

Seems pretty straight forward but let’s see what is going on here.

First we end up saving a copy of our current player controller so we can reference it later. This can also be used to save the “AI” brain of a character in the world and put it back when we unpossess said character.

We then go ahead and unpossess the current controller to ensure it’s cleaned up.

We then go ahead and set the movement mode on our player to none so he doesn’t move when we leave his body.

And lastly we possess the actor of our choosing.

So what about these materials ? Are they also haunted ?

As far as we know, they are not, but we only know so much.

Materials are pretty easy to setup and change, you just need to be aware where on the character in question they reside. Let’s take a look at this example

header

UPROPERTY(EditDefaultsOnly, Category = Possession)
class UMaterialInterface* DefaultMaterialBody;
  
UPROPERTY(EditDefaultsOnly, Category = Possession)
class UMaterialInterface* DefaultMaterialChest;

cpp

if(DefaultMaterialBody)
{
  GetMesh()->SetMaterial(0, DefaultMaterialBody);	
}
if(DefaultMaterialChest)
{
  GetMesh()->SetMaterial(1, DefaultMaterialChest);	
}

As you can see we do really just a few things to switch over the materials.

We first define the properties on our blueprint so we can assign material instances to our player.

Then based on our logic we can set the material to the newly defined variables.

The only little gotcha is to ensure you are referencing the right material index on your mesh. In the case of our Unreal mannequin he has two materials, one for the body and the other for the chest piece. They are respectively assigned to index 0 and 1 on the mesh. So just ensure you are setting your material on the correct section of your model.

It still feels like a need an old priest and a young priest

Nope that’s really it friends, unpossess current controller and possess new target. Rinse and repeat.

Here is a much more involved example lifted from our sample project combining both material switching as well as possession

void AUE4Fundamentals11Character::Interact()
{
  FVector Start;
  FVector End;

  FVector PlayerEyesLoc;
  FRotator PlayerEyesRot;

  GetActorEyesViewPoint(PlayerEyesLoc, PlayerEyesRot);

  Start = PlayerEyesLoc;
  End = PlayerEyesLoc + (PlayerEyesRot.Vector() * LineTraceDistance);

  FCollisionQueryParams TraceParams(FName(TEXT("InteractTrace")), true, this);

  FHitResult InteractHit = FHitResult(ForceInit);

  bool bIsHit = GetWorld()->LineTraceSingleByChannel(InteractHit, Start, End, ECC_GameTraceChannel3, TraceParams);

  if(bIsHit && InteractHit.GetActor() != this)
  {
    // Log(ELogLevel::WARNING, InteractHit.Actor->GetName());
    // start to end, green, will lines always stay on, depth priority, thickness of line
    DrawDebugLine(GetWorld(), Start, End, FColor::Green, false, 5.f, ECC_WorldStatic, 1.f);

    // implements interface
    if(InteractHit.GetActor()->GetClass()->ImplementsInterface(UInteractiveActor::StaticClass()))
    {
      IInteractiveActor::Execute_Interact(InteractHit.GetActor());
    }
    else if(InteractHit.GetActor()->IsA(ACharacter::StaticClass()))
    {
      // check to see if we are a possessed entity
      if(bIsCurrentlyPossessed)
      {
        bIsCurrentlyPossessed = false;

        if(DefaultMaterialBody)
        {
          GetMesh()->SetMaterial(0, DefaultMaterialBody);	
        }
        if(DefaultMaterialChest)
        {
          GetMesh()->SetMaterial(1, DefaultMaterialChest);	
        }
      }

      AUE4Fundamentals11Character* PossessableCharacter = Cast<AUE4Fundamentals11Character>(InteractHit.GetActor());
      if(PossessableCharacter)
      {
        if(!PossessableCharacter->bIsCurrentlyPossessed)
        {
          // handle possession
          if(!SavedController)
          {
            // save the controller
            SavedController = GetController();	
          }

          // unpossess first ( helps with multiplayer )
          SavedController->UnPossess();

          // disable current player state management
          bIsKeyboardEnabled = false;					
          bIsRunning = false;
          bIsArmed = false;
          // disable movement mode
          GetCharacterMovement()->SetMovementMode(EMovementMode::MOVE_None);
          
          // possess our new actor
          SavedController->Possess(Cast<APawn>(InteractHit.GetActor()));
          // enable movement back on the possessed actor
          PossessableCharacter->GetCharacterMovement()->SetMovementMode(EMovementMode::MOVE_Walking);
          
          // set the values for our possession materials
          if(PossessableCharacter->PossessMaterialBody)
          {
            PossessableCharacter->GetMesh()->SetMaterial(0, PossessableCharacter->PossessMaterialBody);	
          }

          if(PossessableCharacter->PossessMaterialChest)
          {
            PossessableCharacter->GetMesh()->SetMaterial(1, PossessableCharacter->PossessMaterialChest);	
          }	

          // ensure the new player is correctly marked as possesed and can be interacted with
          PossessableCharacter->bIsKeyboardEnabled = true;
          PossessableCharacter->bIsCurrentlyPossessed = true;
        }
      }
    }
  }
  else
  {
    DrawDebugLine(GetWorld(), Start, End, FColor::Purple, false, 5.f, ECC_WorldStatic, 1.f);
  }
}

As you can see I am doing a few moves to update the material on the target mesh we want to possess and reset the materials on the mesh we are currently occupying.

Additionally there is a bit of logic to update the state of a few variables that control our animation presentation. For example I am turning off the armed animation logic.

But otherwise it’s just a call to Possess / Unpossess as well as SetMaterial

That’s it folks, hope you found this helpful and here are a few links for some light reading:

Unreal Engine C++ Fundamentals – Using Tags with Transformations !

By | Development, Tutorial, Unreal | No Comments

Hey guys,

Today we resume our C++ Fundamentals lessons by looking at interfaces and how they can help simply our life by allowing for common contracts to be shared by different game actors.

As usual you can find the start project on our GitHub page.

What are these tags ? And why do I care ?

Tags are essentially a collection of strings that can be applied to an Actor or an Actors Component and then referenced by other Actors or parts of the game world by those tags.

Very similar to using tags on a blog to include metadata for search engines to query … like this blog !

Tags ?! We don’t need to no stinking tags !

Sure you do, and they are really easy to use which is why this post is going to be super short.

Take a look, in this case we are displaying all tags for a specific actor.

// iterate over all of our actors
for(TActorIterator<AActor> ActorIterator(GetWorld()); ActorIterator; ++ActorIterator)
{
  AActor* Actor = *ActorIterator;
  // ensure actor is not null
  // ignore self if found
  // ensure we find actors of a specific interface only
  if(Actor && Actor != this && Actor->GetClass()->ImplementsInterface(UInteractiveActor::StaticClass()))
  {
    // display all available tags for an actor
    for(FName Tag : Actor->Tags)
    {
     	GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Cyan, Tag.ToString());
    }
  }
}

We can also just check for a specific tag without iterating over all of them by using ActorHasTag method.

if(Actor->ActorHasTag(TagToCheck))
{
    IInteractiveActor::Execute_Interact(Actor);		
}

Wait you forgot about lerping ? What is lerping !?

Lerping is a way of translating between vector A and vector B over some period of time.

For example if you need to scale an object from one size to another you can use FMath::Lerp to accomplish this.

BaseMesh->SetWorldScale3D(FMath::Lerp(BaseMesh->GetComponentScale(), FVector(2.f, 2.f, 2.f), 0.05f));

In this example we go from vector A ( GetComponentScale() of our mesh ) to vector B ( 2x the size ) over a period of time represented by 0.05f time.

Take a look.

That’s it folks, not much more reading but here are a few links for you to check out:

Unreal Engine C++ Fundamentals – Interfaces!

By | Development, Tutorial, Unreal | 3 Comments

Hey guys,

Today we resume our C++ Fundamentals lessons by looking at interfaces and how they can help simply our life by allowing for common contracts to be shared by different game actors.

As usual you can find the start project on our GitHub page.

What is an interface ? Tell me !

An interface is an abstract definition of a contract. What this means is an interface defines how our class can be interfaced ( hah ! ) with while keeping the implementation details away.

More than that it allows us to create a common method signature between various disparate objects.

Consider I have an interface that has a method on it called “DoStuff” and I have two objects, Dog and Person, that inherit this interface. When I call Dog->DoStuff he may perform a trick while if I call the same method of Person->DoStuff he may tell me to fuck off and to stop bothering them. But from the point of view of the code interacting with those actors it’s the same behavior.

This allows for a lot of re-use and generic implementations while keeping the details of those implementations specific.

This all seems very abstract, show me code !

So let’s take a look at how an interface class is structured

UINTERFACE(MinimalAPI)
class UInteractiveActor : public UInterface
{
  GENERATED_BODY()
};

/**
 * 
 */
class UE4FUNDAMENTALS09_API IInteractiveActor
{
  GENERATED_BODY()

  // Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:

  UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Interact")
  void Interact();
};

As you can see it’s pretty thin in definition, we simply have a class that inherits from UInterface and then defines the method signature of “Interact”.

Excellent now we are cooking with fire, now what ?

Once we have the interface in place we now start including it on our objects. Let’s take a look at our InteractiveProp.

UCLASS()
class UE4FUNDAMENTALS09_API AInteractiveProp : public AActor, public IInteractiveActor
{
  GENERATED_BODY()
  
public:	
  // Sets default values for this actor's properties
  AInteractiveProp();

  UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Interact")
  class UStaticMeshComponent* BaseMesh;

protected:
  // Called when the game starts or when spawned
  virtual void BeginPlay() override;

public:	
  // Called every frame
  virtual void Tick(float DeltaTime) override;

  UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Interact")
  void Interact();	// prototype declaration
  virtual void Interact_Implementation() override;	// actual implementation of our interact method

private:
  bool bIsBig;
};

The major thing to note is that in order to leverage the interface, we modify our class signature to bring in “IInteractiveActor”

class UE4FUNDAMENTALS09_API AInteractiveProp : public AActor, public IInteractiveActor

Then we overwrite the Interact method by first declaring the prototype method signature and then the implementation one. Please note that the “_Implementation” is important and has to be exact for the reflection mechanic in Unreal to process your interface + implementation.

UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Interact")
void Interact();	// prototype declaration
virtual void Interact_Implementation() override;	// actual implementation of our interact method

So that was pretty straight forward, now how do I use these guys ?

To execute interface definitions we have a few ways of going about.

First we need to determine if an object uses an interface and we have two ways of checking that.

  1. Based on the ImplementsInterface check
  2. Using a cast to an interface object

Here is the first example

InteractHit.GetActor()->GetClass()->ImplementsInterface(UInteractiveActor::StaticClass())

And the second one

IInteractiveActor* InteractiveActor = Cast<IInteractiveActor>(InteractHit.GetActor());
if(InteractiveActor)
{
  // do stuff
}

Once you determine if an object has an interface the way you execute a call on that object is by passing it into the interface “Execute_” call.

IInteractiveActor::Execute_Interact(InteractHit.GetActor());

This basically says, for interface InteractiveActor Execute the method Interact and pass in our Actor on which that method will be triggered.

Here is a full code block that does this logic using a line trace fired from the player against an object.

void AUE4Fundamentals09Character::Interact()
{
  FVector Start;
  FVector End;

  FVector PlayerEyesLoc;
  FRotator PlayerEyesRot;

  GetActorEyesViewPoint(PlayerEyesLoc, PlayerEyesRot);

  Start = PlayerEyesLoc;
  End = PlayerEyesLoc + (PlayerEyesRot.Vector() * LineTraceDistance);

  FCollisionQueryParams TraceParams(FName(TEXT("InteractTrace")), true, this);

  FHitResult InteractHit = FHitResult(ForceInit);

  bool bIsHit = GetWorld()->LineTraceSingleByChannel(InteractHit, Start, End, ECC_GameTraceChannel3, TraceParams);

  if(bIsHit)
  {
    Log(ELogLevel::WARNING, InteractHit.Actor->GetName());
    // start to end, green, will lines always stay on, depth priority, thickness of line
    DrawDebugLine(GetWorld(), Start, End, FColor::Green, false, 5.f, ECC_WorldStatic, 1.f);

    // implements interface
    if(InteractHit.GetActor()->GetClass()->ImplementsInterface(UInteractiveActor::StaticClass()))
    {
      IInteractiveActor::Execute_Interact(InteractHit.GetActor());
    }
  }
}

That’s it folks !

In addition to the video here are a few resources for further reading: