The Basic Interaction System
Welcome to the tutorial and explanation of the Basic Interaction System that comes as one of the tools you can leverage for testing and developing the mounting system of your game. This Interaction system is free to use and leverage however you see fit.
1.0 Overview
First we will go over the Blueprint classes supplied with the Interaction System. The directory structure provides some insight into the parts that compose the system.
First is the interaction component, this is where the bulk of the interaction system logic exists from scanning for things to interact to executing the interaction command, it is all within this small component.
Then there is the interfaces folder. Here are the three interfaces required for interaction.
- BPI_InteractableActor is the interface for any actor that can be interacted with.
- BPI_InteractingActor is the interface for the actor (in this case our Pawn) that is performing interactions.
- BPI_InteractionManagerOwner is the interface for the Object that has the component. In our case it will be our Player Controller.
Next up the Interaction System has a small customization
There there is a specialization for for this system for the Mounting system. Since this system needs to be a universal framework (for other projects) I opted to create a secondary interface for specializations instead of lumping it into BPI_InteractableActor interface
- BPI_LinkActorInteractionOwner - This is a specialization of the interaction interface that allows linked actors to route their interaction to their main mount. This allows you to interact with a linked actor and mount its parent actor.
2.0 Collision Setup
For our mounting system and our Interaction system we will be changing some of the UE4 default collision presets. This will solve two problems. The first is, in its default state a mounted passenger’s camera will be blocked by the mount’s capsule and mesh components causing it to be trapped usually inside the mesh or center of the mount. The Second is, it will allow our visibility trace to hit the mesh component of our mount we setup, the BP_Horse.
There are other ways of doing this but for demonstration purposes this is the easiest way to do it. You could create a whole new Preset just for your mounts, you could create a new Object Channel or Trace Channel. But getting into the details of setting that up is beyond the scope of this tutorial and this plugin. It is very dependent upon the type of game you want to make. If you want to know more about Unreal Engine Collisions visit the Collisions Documentation page in the UE4 Documents.
Go to Edit drop down menu and select the Project Settings.
Then go to Engine / Collision Settings.
Open the Preset tab. The ones we will be most interested in are Pawn and Character Mesh.
;n style="font-weight: 400;">Double click on Character Mesh preset. We want to set it up as the image by setting the camera trace value to ignore. This preset is used by the Character’s Mesh Component and will prevent the mount's mesh from interfering with passenger cameras.
3.0 Controller Setup
First we will be working with our PlayerController, this is where I like to implement interaction systems as it allows for the controller to have direct control over the types of interactions that are done. For instance AI interaction querries are very different from player interaction queries and this interaction system provides both an Line trace for players an an Area trace for AI.
So now open up the BP_MountingPlayerController.
3.1 Add Component and Interfaces
First we need to add our Interaction Component and the proper Interfaces to our Player Controller.
Click the "Add Component" button at the top and search for the BPI_InteractionComponent and add it to the PlayerController
.
Next click on "Class Settings" and on the Details panel go to the "Interfraces" section and search for BPI_InteracitonManagerOwner.
Add the BPI_InteractionManagerOwner interface to our PlayerController
.
3.2 Component Configuration
Lets take a quick moment to look at some of the configuration values on the Interaciton Component now that it is on our Controller.
As you can see there are quite a few options to configure the InteractionComponent. We'll go over each one.
- Is Active Tracing - When true this means the Interaction Component is actively scanning for interactable objects, if false then it is disabled. You can toggle this on and off at runtime
- Interaction Distance - This is the length of the trace to perform when scanning for interactables.
- Is AI Mode - When true this component is put into AI mode meaning instead of doing line traces it will do sphere traces to find objects. You can use the Delegate OnScanForInteractionComplete to decide which target to choose and filter through when a sphere scan completes.
- Scan Object Types - The Traces for this interaction system rely on colliison object types, you can define your own collision object types and it is wise to do so. For demonstration purposes we are simply going to be scanning for the "Pawn" type.
We will also go over the Debug categories as these maybe useful to you in debugging.
- Trace Debug Message Duration - This is how long a debug Trace line or sphere will show up while debugging.
- Trace Debug Type: This is an identical input to the one you see in any of the UE4 Blueprint Trace options. You have the options of None, For One Frame, for Duration, or Persistent
3.3 Setup Controller Functions
Now we need a to define a few functions we will be using for the interaction system.
3.3.1 Dismount
This function will handle our dismounting of the current mount.
Input/Output | Type | Name |
---|---|---|
Output | Bool | Success |
I am forcing these functions to remain functions by returning a boolean
value. This ensures that later if/wen we have to override them in child blueprints they do not become events.
Implementing this function is simple, we use the IRiderControllerInterface::BeginDismountingActor()
function and return its boolean
value.
3.3.2 OnInteractionPressed
This function's only purpose is to toggle between performing interactions or dismounting from the mount if the Rider is mounted.
Input/Output | Type | Name |
---|---|---|
Output | bool | Successful |
To implement this function I use a Sequence Node immediately after the Function Start Node. Because this function will always succeed but we also want it to always remain a function and not become an event in child blueprints.
Connect the Return node to the "Then 1" execution line and check it as true.
Next place down a Branch.
Then right click and search for "IsMounted" to get the IADMUnifiedControllerPawn::IsMounted()
function and place it on the graph.
Connect the return value of IsMounted()
to the branch.
Then from the "True" execution line place down the Dismount() function node we created earlier.
Then drop a get node for the BP_InteractionComponent.
Draw a line from the BP_InteractionComponent and search for its function "PerformInteraction".
3.3.3 Change Seats
Next we need to create the ChangeSeat() function that allows us to change seats while mounted.
Input/Output | Type | Name |
---|---|---|
Input | integer | NewSeatId |
Output | Boolean | Success |
To implement this function add the IRiderControllerInterface::BeginChangingSeatById()
function to the graph.
Connect the input parameter NewSeatId Input parameter SeatId
of the BeginChangingSeatById()
function.
Finally connect the Return Value
of BeginChangingSeatById()
to the Return Value of the function.
3.3.4 MountToSeat
Now we implement the MountToSeat() function. This function leverages direct mounting, (rather than positional mounting). Direct Mounting allows us to mount to a specific seat, instead of leveraging relative position of the character to calculate the best seat to mount to. Instead you can use mechanisms such as overlaps to tell the system which seat to mount to.
Input/Output | Type | Name |
---|---|---|
Input | integer | SeatId |
To implement this function we first place down a Sequence Node and connect a return true to its "Then 1" execution Line.
Then we place down a Branch and connect it to the "Then 0" execution line.
Now get the RiderControllerComponent and use its IsMounted()
function to ensure we are not mounted already and connect it to the branch condition.
Then place down get node for the BP_InteractionComponent.
From BP_InteractionComponent draw a line and search for GetCurrentInteracitonTarget(). This function returns the currently targeted Interaction Actor (if one exists).
We check if the Target is valid, and if it is then we call IRiderControllerInterface::BeginMountingActorToSeat()
.
Supply the return value of GetCurrentInteractionTarget() to the NewMount
input parameter of BeginMountingActorToSeat()
.
Connect the input value SeatId to the SeatId
parameter of BeginMountingActorToSeat()
.
3.3.5 Enable and Disable Interaction Scanning
There are times when we do not want the interaction system to perform scans of things to interact with. For this demo, while mounted we do not need to be scanning the environment for things. Your game maybe different and you can tailor this as much as you like but this is the basic way to start and stop the interaction system from performing periodic scans.
First select the RiderControllerComponent and in the details panel find the "Events" section.
Click the button next to the OnMountActorChanged event. This is called when the Rider Changes the mount they are riding, this is usually due to mount and dismount functions.
To implement this we check if the NewActor is valid or invalid. If it is valid then, then our Rider has mounted a new mount and we want to turn off scanning for intractables. if it is invalid then our rider has dismounted and we want to enable scanning for interactables.
This is done through the BP_InteractionComponent by calling the function SetIsActiveTracing().
3.4 Implement InteractionManagerOwner Interface
Now we will implement the interface for the Interaction system owner. It has only two functions to deal with.
3.4.1 GetIntearctingActor
This function is used to fetch from the Owner the actor it should be performing the traces from, in this case because we have implemented this on our Controller we need the return value to be the Pawn. The system uses this actor to base the start and end points of the scan traces.
We implement this by first dropping a Sequence Node that returns an Null actor from the "Then 1" execution line. This is generally the failed condition.
next we use the function AController::GetControlledPawn()
and place it onto the graph.
Then from GetControlledPawn() we use the function BPI_InteractingActor::CanInteract() to ensure that our pawn has implemented the BPI_InteractingActor interface.
Finaly we return the result of GetControlledPawn()
.
3.4.2 GetInteractionComponent
This functions goal is simple, return the InteractionComponent. This is useful for when you need to do things with the interaction component not covered in one of the interfaces for protyping.
To Implement this function we simply return the BP_InteractionComponent.
4.0 Setup the Mount
Now we must set up our Mount for interaction. Open up your mount, in this tutorial I am using the “BP_Horse”.
4.1 Add Interfaces
We need to add our BP_InteractableActor Interface to our mount.
Go to Settings and under "interfaces" add the BPI_InteractableActor interface to our mount.
4.2 Implement Interface
Now we will implement the interface functions for our Mount.
4.2.1 Interact
This function is called by the BP_InteractionComponent when ever PerformInteraction() is triggered. It allows the mount to decide what exactly happens when an actor wants to interact with it. You can place all sorts of logic in here to bring up radial menus or perform other operations.
We'll go over how to implement this function bit by bit.
First we need to get the Controller, so we call the IADMUnifiedControllerPawn::GetCharacterController()
function. This function should only ever be run on the server, so we know PlayerControllers
exist and can get them.
Then we call the IADMUnifiedControllerPawn::GetCharacterPawn()
as we will need both of these things to start the mounting process.
We should really spend a second to check both the Controller and the Pawn, but throw some IsValid()
macros up there and make sure you have things to work with.
Place the node IMountablePawnInterface::IsMountableByPawn()
on the graph and supply it the return value of GetCharacterPawn()
.
Then place a Branch after the IsValid()
check and connect the result of IsMountableByPawn()
to the Condition
input pin of the branch.
If our mount can be mounted by this pawn then we can call IRiderControllerInterace::BeginMountingActor()
, make sure the Controller is the Target
for this interface function.
Get a reference node for self and supply it to the NewMount
input parameter of BeginMountingActor()
.
Connect the Return Value
of BeginMountingActor()
to the IsInteracting output pin.
If our mount cannot be mounted by this pawn then we simply return a false value.
4.2.2 Set Is Focused
This functions primary purpose is to provide a space for your Visual Effect Artists to put in some interaction feed back. Maybe you want to add a glow around the selected item, maybe you want an interaction message to appear. For the Final Demo I added a simple widget that pops up and says you can interact with this thing. That bit is a bit outside the scope of this, but I suggest taking a look at the finished demo to get some ideas.
We implement this function by leaving it as it is.
4.2.3 IsInteractable
This final function is a simple bool, it tells the Interaction Component if the thing it is looking at is considered interactable.
You can implement this in a number of ways but for this tutorial we simply return true, this horse will always be interactable.
5.0 Setup Rider
Finally we need to setup our Rider. Open up BP_MountingCharacter.
5.1 Add Interfaces
First we'll add the interface we need. The BPI_InteractingActor Interface is used by the BP_InteractionComponent as the target of its trace queries. The component leverages the functions of this interface to get start and stop locations for traces and scan radius for AI.
Go to the "Class Settings" and look for the "Interfaces" section.
Click on the "Add" Button and search for BPI_InteractingActor Interface and click on it to add it to our Character..
5.2 Implement Interfaces
Now we can implement our interface functions.
5.2.1 IsLocallyControlledInteractingActor
This function helps the component know if and when it needs to send RPCs between client and server.
To implement this we simply use the APawn::IsLocallyControlled()
function and return its value.
5.2.2 GetScanRadius
This function is leveraged to perform a Wide area Scan, this is often used for AI BP_InteractionComponent::ScanForInteractables()
function but could be used by players if directly called. The Component itself does not have this as a default value because it could be different depending on the character rather than the controller.
We implement this by simply returning a value of 400 units, you can setup a variable on your characters instead which is what is done in the Final Project Files.
5.2.3 CanInteract
This function is a simple bool the BP_InteractionComponent uses to check if an actor can be interacted with. If an Actor does not implement this interface it will always be false. You can also setup any custom logic to determine if an actor is interactable or not.
To implement this function we simply return true.
5.2.4 GetCameraArmLength
The main purpose of htis function is to fetch the Camera Arm length being used, The system simply wants to know what the distance between the camera and the character is, so if you are in First Person mode this will the value of 0.0
.
To implement this function we place a get node to the CameraBoon Component that is part of our ThirdPersonCharacter.
Then from the CameraBoon Component we draw a line and look for TargetArmLength.
Then we connect that value into the CameraDistance output parameter.
5.2.5 GetViewPositionAndLook
This funcitons job is to supply our BP_InteractionComponent with the current view position and look direction of our Rider.
To implement this function we use the FollowCamera component that is part of our ThridPersonCharacter. The important thing about this is it will be the "view" location of our Rider. For AI, it uses a Scan Radius based on the position of the pawn itself so this function is not used for AI, but with a bit of checking it could be leveraged for such tasks.
Place a get node for the FollowCamera on the graph.
Draw a line and look for GetWorldLocation() and plug it into the Location output parameter.
Draw another line and look for GetWorldRotation() and plug that into the Rotation output Parameter.
6.0 Finished.
That is all there is to using the Ads Basic Interaction System. It is important to note that this interation system is an optional piece and any interaction system can be used for the PNMS system to work. To find out more about the basic functions see Interaction Systems Document.