Advice on best practices for structuring components/interfaces?!

I’m relatively new to C++ and to Unreal and am trying to get my head around the best design for some key functionality in my project - any advice would be much appreciated!

Context is that I am trying to design my ‘interactables’ functionality.

At it’s base this is functionality for objects in the game world which the player, and potentially NPCs, can interact with. Effectively it boils down to:

When the player hovers the cursor over an interactable object it should highlight, and when they press the ‘interact’ key whilst hovering over the object it should check the player is in range and then trigger it’s relevant functionality.

To tackle this, initially I implemented an interface in C++ with such functions as OnMouseOver, MouseOverEnd, OnInteract. Then, I included this interface on the relevant top level classes such as DoorActorBase, ButtonActorBase, etc. so it’s available to any derived classes.

The problem I had with this is that I was then having to implement the common functionality, such as the highlighting of the object, in each top level class.

I have a feeling this isn’t the best way to do this as it means if I want to change say the default highlight colour, width, etc. (or even more fundamentally the code behind the highlight) I then need to go into each top level class to make the change(s) to the actual functionality.

Therefore after some research I’ve started to attempt to create a C++ Actor Component instead. My understanding is that then the relevant logic such as for highlighting the object can be stored in the ActorComponent, and the actual object ‘functionality’ can be stored in the Actor class. So the component takes care of the highlight and any checks and can then trigger the ‘functionality’ on the owner Actor.

I’m running into some confusion though on how to best approach this, so am hoping for some guidance from much more experienced developers! (Basically anyone :sweat_smile: )

Firstly, is my thinking on the ‘best’ approach correct, or misguided? If it is on the right path then what’s the best way to structure this? Would it involve a mix of an interface which checks the Actor has the InteractableComponent on it, and then calls the relevant function on the InteractableComponent? If so, I’m really confused on how to actually include code in UE C++ interfaces, rather than just function declarations?! Or is some other way a better approach?

P.s. If it’s helpful, as an end goal ideally what I believe I want is for default interaction functionality (as set in a single source of truth) to be available to any relevant Actors I choose (as an example of ‘default functionality’: OnMouseOver should trigger a highlight with the default settings, MouseOverEnd should remove the highlight, OnInteract would check player is in range and then call TriggerFunctionality). However ideally each of these can be overridden as required on a per class basis too.

Your new approach seems in line with what’s in my experience the “better” approach to solving this problem. You typically would have one component on the player character/camera that periodically checks for viable target interactables (typically done using using line/sphere casts that include the range validation and checking that the objects have the required component) which are placed on actors in the level. If one is available, the control component invokes any highlighting logic on the target interactable and similarly it invokes the interaction logic itself when the required input is given.

As per your question to structuring this, there are multiple options, each with its own advantages and disadvantages. Choosing how to implement this also depends on the amount of flexibility you want to have, the more you need, the more complex has to be the solution. Assuming that the highlighting logic is the same for all interactables, you can just have one component that implements this and then have a function on it through which you can submit a delegate to it that the component will invoke when it is interacted with. This way you push the interaction logic outside of the component and into the owning actor that invokes this function during BeginPlay() or other such events. Alternatively, you can use inheritance but then you will have to create a child component for each kind of interaction and assign the specific implementations on the given actors which will get ugly quick if you are going to have a lot of interactable types and their instances in the world. I don’t see much of use of interfaces in this scenario unless you want to invert this approach and have the component call an interface function on its owning actor when it is interacted with. But I feel like this approach is a bit counter-intuitive as you typically want to have the owning actor manage its components, plus it’s slightly less flexible in cases such as dynamically overriding the interaction behavior.

1 Like

Many thanks!

I think in particular it’s the concept of delegates that I was missing, I’ve only come across it in passing and so had a practically non-existent understanding of them.

I’ve done some research now and can definitely see how this would help me tackle some (if not all) of the issues I am seeing with my revised approach!

I’ll give implementing this all a go later today!