Over the years, I've worked on a wide range of projects from small indie ideas to AAA games.
Here is how I would build an interaction system from scratch in Unreal Engine if I were to start a new project today.
A thread π§΅ππΌ
1/17 General Philosophy
The main goal is to make an architecture that's easy to maintain. To achieve this we are going to go for simple bare bones solutions.
We are not going to develop a one-size-fits-all solution, instead, we aim to keep complexity to a minimum.
2/17 Limiting inheritance
Let's take the example of a door.
If we have an Interactable base class, the next logical step is to create a Door class inheriting from it and add the rotation logic.
This way the user can open the door by interacting with it.
3/17 Limiting inheritance
If we need a broken door (BrokenDoor) that can be opened only by an explosion, now we have to:
β’ Copy-paste the rotation logic to a new component
or
β’ Add the option to disable the interaction - which makes inheriting from Interactable useless
4/17 Interfaces
Interfaces are a great way to ensure that classes of unrelated types have common functionality implemented.
They are a solid choice, but in the context of Unreal, they have some limitations (e.g.: complicated setup to expose configurable per-object properties).
5/17 Interfaces
For this reason, we are going to avoid this time around and go with actor components.
(However, for other systems like an Events System, where we want to broadcast events to both actor-based components and widgets, they are the go-to choice).
6/17 What are we going to do?
This time around, we are going to go for an ActorComponent. This way the component can be added when actors need to be interacted with.
We are going to focus on C++ mainly, but similar delegates & functions can be exposed to blueprints.
7/17 The InteractableComponent
β’ OnInteract - executed upon interacting, e.g.: rotate the door 90 degrees
β’ CanInteract - performs additional tests before interaction, e.g.: does the user have the required key
β’ TryInteract - called when the user wants to interact
8/17 InteractableComponent implementation
In the component's implementation, we have 2 important aspects:
β’ if the OnInteract delegate is not bound, we are going to raise a warning
β’ if the CanInteract delegate is not bound, the user is always allowed to interact
9/17 How to trigger interactions
To allow the player to interact with objects, we need to give him the ability to call the TryInteract function.
To achieve this, we are going to add an InteractorComponent component to the player which will scan for nearby interactable actors
10/17 The InteractorComponent
This component will do a Sweep in front of the player to check for interactables every frame.
We have a few configurable properties which we will cover as we go over their usage.
11/17 InteractorComponent Implementation
β’ Constructor: enables the component's tick function
β’ BeginPlay: binds the user's "Interact" input to the PerformInteraction function
β’ Tick: runs the UpdateInteractionCandidates each frame
12/17 UpdateInteractionCandidates
This function performs a trace with debugging data - which we will live view.
Afterward, we look for potential interaction targets (you can sort them based on distance, but for simplicity's sake, we are just going to go for the first hit).
13/17 PerformInteraction
The hard work has already been done, here we can just check if we currently have a valid target and try interacting with it.
14/17 Player setup
Now that all the code has been laid out, we will begin adding our new components starting with the player's Interactor.
15/17 Interactable setup
Next up, we will add the Interactable component to the rifle.
(This project started from the first-person C++ template).
16/17 Interact input setup
And finally, we will add the same Interact input we assigned in the code and bind it to the "E" key.
(As pointed out in the warning below, we should use the Enhanced Input system, but that would result in a way longer thread).
17/17 Live Demo
Here we have a live demo of the interaction system with the additional debug drawings.
That's all for today!
If you learned something new:
1. Follow me @OutoftheboxP for more of these 2. Retweet the tweet below to share it with your fellow developer friends.
Here is how you can use FScopedSlowTask to make it more enjoyable.
A thread π§΅
1/11 What is FScopedSlowTask ?
Is an editor-utility class that helps developers display loading bars when creating editor tools.
It's super easy to add to your existing logic and makes the waiting much better for your end users.
2/11 How does FScopedSlowTask work?
It takes advantage of the object's lifetime and automatically initializes the task's data in the constructor (you still have to show it by using MakeDialog).
And destroys the pop-up once the object is out of scope (e.g. function ended).
Here is how to create your own editor mode inside Unreal Engine.
Extend the editor with custom tools like the Landscape mode tailor to your own project.
A thread π§΅ππΌ
1/4 Create the EdMode subclass
The EdMode subclass is responsible for registering and initializing the mode. It provides access to crucial virtual functions such as UEdMode::Enter & UEdMode::Exit which are triggered selecting and leaving the edit mode.
2/4 Create the ModeToolkit subclass
The Toolkit is responsible for creating the UI panel. It allows you to fully customize the panel with custom widgets are leverage the built-in DetailsView panels.
Here is how you can run custom logic during the engine initialization from your own modules.
A thread π§΅ππΌ
Registering callbacks is usually straight forward:
You get a reference to an object, access the delegate and add your callback.
However things can get tricky when it comes to engine initialization.
The main problem is you risk registering your custom logic too late - after the delegate has been fired.
To avoid this kind of problems, Unreal has FDelayedAutoRegisterHelper which gives access to a bunch of critical delegates and automatically registers the callback.
Here is how to run your Unreal C++ code asynchronously to avoid bottlenecks.
A thread π§΅ππΌ
A little bit of context:
I am working on integrating @Tolgee_i18n localization tools inside Unreal Engine.
One of problem I've encountered is the performance hit coming from refreshing the Localization Manager resources.
A single call would take 450ms and drop the FPS to 2.
1. Investigating & diagnosing the issue
The first step we need to take is to add as many TRACE_CPUPROFILER_EVENT_SCOPE in performance-heavy code. This will measure the impact of the function's execution.
As a general rule of thumb, when in doubt, always add more.
How to write and refactor code without introducing any bugs.
Write your first Unreal Engine test today in 6 steps using Rider.
A thread π§΅ππΌ
First off, let's discuss what automation tests are and what their purpose is:
Automated Tests are a controlled environment where you can verify the outcome of a piece of code against the expected result. They ensure our code behaves as expected while developing/refactoring it.
Step 1: Create the test class
1. Navigate to the module you want to add the test file to inside Rider. 2. Right-click the module project 3. Go to Add β Unreal Test Class