Interface blueprints that run identical code with different outputs

I have an interface blueprint called InteractInterface_BP. I have a lot of different kind of actors that share a particular function, and its displaying text in my hud widget after the interaction occurs. For example, if the player character interacts with a locked door, the hud widget will say “This door is locked”. Or if i pickup an item it will say, “You picked up x item”. The way I’m implementing this is be setting a public string variable in each interact event. In each event I call GetPlayerPawn and cast to the ThirdPersonCharacter blueprint and set my public string variable. The hud accesses it via a cast as well.

My question is, is there a way for every actor the calls the interact event to be able to set this text while only casting to the player character once?

alt text
alt text
alt text

I would use this solution (note I’m approaching this from a C++ perspective, but I believe this is all possible in blueprints):

  • Have an IInteractable interface which has a ‘GetInteractionText’ method (a enum type would probably be better, but I’ll use text for now)
  • Have an Actor Component UInteractableComponent, which implemented the IInteractable interface. This would preferably be abstract.
  • Create further classes which use UInteractableComponent as a base / parent. You should have one per type of interaction. Each should also implement IInteractible and GetInteractionText should return the text / type approproate for that interaction type. For example, you can have UInteractableComponent_Open or UInteracibleComponent_Pickup, and GetInteractionText would return “Open” or “Pickup” respectively.
  • You can add these components to the actors in your world as needed.
  • The the point your player interacts with the door you can just call GetInteractionText (from the player itself, or perhaps a component on the player). This would give you the correct text for the component / actor you’re trying to interact with. Note that your player should be checking that this is an IInteractable object, as then we know GetInteractionText exists. If it’s not IInteractable, then it’s not interactable.
  • Now your player has the text appropriate for the interaction and can set it on the HUD.

So, this is similar to what your were doing, but it’s shifting the responsibilities of the components a bit. Each interactable component only concerns itself with being able to tell anything which queries it what type of intractable component it is, and not about providing it to any specific actor / class / component. The actor / class / components which do care about getting the information (i.e. the player), can instead fetch it when there is a need.

Also, I mentioned that returning an Enum would be better than returning text. This is because it’s slightly cheaper, and it means that if you were to ever change what you wanted the text to be, you can just change it in one place rather than many,

So, I was thinking about prototyping in blueprints and then moving to c++, so I can understand the c++ perspective. So, let me ask. If I were to use an intractable interface, how would i use an enum to determine what text to send to the player? Like lets say I have to main intractables: Doors and Items. Doors will have a few different states, locked, inaccessible, and unlocked. And of course I’d have several dozen items. How would the parent GetInteractionEnum know what door or item to use?

As with everything, there are a million ways to do this but personally I’d define an eInteractionType enum. This would return values like ‘Use, Pickup, Open, Close’ etc, or it would define ‘Chest, Door, Lamp, Switch’, whichever makes more sense for your game.

GetInteraction type would just return the enum. In C++ terms this would look something like the following for anything which has the ‘Use’ interface.

EInteractionType GetInteractionType const { return EInteractionType::Use;}

Of course, you can set this in any number of ways, but if you know a certain interaction component will always return Use, then the above will work fine.

Now you know the interaction type, you would probably be best sending this straight to the HUD. The HUD could either have a switch which for each possible value of EInteractionType would get some text (probably an FText which would make sense to expose as a variable), or get it from a DataTable. Again, whichever makes most sense for you.

Doing it this way you’re tightly controlling what can be sent to the hud, because only valid values of EInteractionType will be accepted and anything invalid will fail, probably at compilation time, which really helps keep your game stable and helps ensure you always get consistent results. Without that, you might find one component sends ‘open’ and another sends ‘Open’, which will be considered different values and could have an impact on other methods called further down the chain.

So, I already have a function that does a line trace every frame and if it collides with an interactable object the hud will display, “ItemName, Press E to Action”. itemName and Action are setup in the Actor BP as UPROPERTIES. Now, i’d like to set something up that after a successful interaction a different message pops up such as, “You picked up the item”.

243854-cubepickupbpoverride.png

Basically take whats in the print string and throw it on the hud.

Is that screenshot on the interactable component, or on the player? If it’s on the interactable component then I suggest you also have a similar event raised on the player. My flow would look something like:

  1. PlayerCharacter runs linetrace
  2. Line trace detects IInteractableComponent
  3. PlayerCharacter calls something like CanInteract() and Interact() on the object. The Interact should be what fires your EventInteract above, although it shouldn’t need to do anything at this point.
  4. PlayerCharacter calls some method to get the data out of the component. Maybe GetInteractType, GetInteractText, GetInteractData (return a struct maybe) etc. It depends how you want to do it but these should probably all be methods in your interface to keep things consistent.
  5. Once the PlayerCharacter has all the information it needs, it can then send this to the HUD in whatever way you want.

Following the above means that only the PlayerCharacter is responsibly with sending the data to the HUD. Each component has a responsibility to report it’s values like Text and Type to whatever asks, but it doesn’t care what asks as it’s just returning known data types. They never send without being asked, so there is no need for any casting. The component can also perform some action on interact if it’s needed (change colour, enable physics, trigger sound etc), but that event isn’t responsible for passing data to anything else.

The whole idea of the above is to try and decouple all the blocks of code, which means that they are re-usable and there are as few dependencies as possible. It also means that because you have just one thing (the PlayerController) asking about interactions and it is also responsible for figuring out which one it cares about, you have much greater control than potentially many Interaction objects shouting ‘you’ve interacted with me’ to the player controller without any ability to properly filter or select them.

Yeah so my line trace is done in the player character. Here is the function:

 void AMemoriesCharacter::CheckForInteractable()
    {	
    	FHitResult HitResult;
    	FVector StartTrace = FollowCamera->GetComponentLocation();
    	FVector EndTrace = (FollowCamera->GetForwardVector() * 350) + StartTrace;
    
    	FCollisionQueryParams QueryParams;
    	QueryParams.AddIgnoredActor(this);
    
    	AGameplayController* Controller = Cast<AGameplayController>(GetController());
    	
    	if (!Controller) { UE_LOG(LogTemp, Warning, TEXT("here")) return; }
    
    	if (GetWorld()->LineTraceSingleByChannel(HitResult, StartTrace, EndTrace, ECC_Visibility, QueryParams) && Controller)
    	{
    		// Check if the item is interactable
    		if (AInteractable* Interactable = Cast<AInteractable>(HitResult.GetActor()))
    		{
    			Controller->CurrentInteractable = Interactable;
    			return;
    		}
    	}
    
    	// If nothing was hit or was not an interactable set to nullptr
    	Controller->CurrentInteractable = nullptr;
    }

I was following a tutorial on youtube, but want to extend it in my own way.

I know this is kinda old, but I was curious if you could, or anybody else that might know the answer, guide me as to where would you put the enum? In the Interactable interface or in the abstract parent? By the way, I’m not quite sure I follow why you’d need an abstract parent in the first place since I don’t really see what you’d use it for. The concept of having an interface + a bunch of components that implement the interface makes more sense to me.