Introduction
Welcome to the guided tutorial for the Pro Networked Mounting System version 2.0 for Unreal Engine 4.25 through 4.27. In this tutorial we will go over the bare bone setups for getting up and running with the plugin. To follow along you will need to have purchased the plugin from the Unreal Marketplace. You can then create a new project or download my starting project from the link below. There will also be a guide on setting up a project independent of the starting project.
Before beginning I highly suggest you look take a look at the Additional Resources for demo project downloads.
Also there are a few important concepts about how the system works you should become familiar with. You will find this in the Concepts Article and it may help clarify a few questions about how the sytem works.
Table of Contents
1.1 Mannequin Animations
3.0 Interface and Component Setup
4.0 Implement the Player Controller
4.1 General Setup
4.2 Unified Controller Pawn Interface
4.3 Rider Controller Interface
5.1 General Setup
5.2 Character Unified Interface
6.1 General Setup
7.1 Setup Sockets
8.1 Event Graph
8.4 Assign the Animation Graph
1.0 Project Tour
First I highly suggest downloading the finished projects. You can find download links on the Resources page. I do not suggest trying to export the already setup mount assets and characters into your own project. The main reason is you should be implementing these things on your own Players, Controllers, NPCs, and Vehicles within your game. The other reason is I have modified the Collision system a bit to allow Camera Traces to hit the player body and allow you to select them, and to allow the camera spring arm to ignore other pawn bodies so it does not get traped within your mounted character or any passangers that are traveling with you. The topic on collision settings is covered in the A Basic Interaction System Tutorial.
1.1 The Finished Project
The Finished project includes many examples you can use to create various behaviors for your mounts and vehicles depending on the needs of your game. Nearly all Blueprint Examples come with a built in Blueprint Tutorials to help get you familiar with the project. I highly suggest you click on them to get an idea of where everything is and what it does.
I Also suggest looking at the "AdvancedDemo" which adds animations and movement enhancements into the mounting system, giving you a general idea on how those can be implemented or where to implement them.
1.2 Starting Project
The Starting project itself it is much more stripped down and bare bones. It is meant to allow you to follow along these tutorials to help you get familiar with the Mounting System with a bit of ease. You can also follow along by simply creating a BP or C++ Third Person Game Template, then you just need to find a mount to use. However, the Starting Project does come with a few useful tools that you may freely leverage in your own games and systems which makes things a bit easier. In the next few sections we will go over some of the additions you will find in the Starting and Finished Projects.
1.2.1 Basic Interaction System
First is a simple blueprint interaction system that comes with the Starting and Finished Projects. With this basic interaction system you will be able to setup interactions without fully creating the interaction system as we did before. You can read more about it in the Basic Interaction System Tutorial.
This system comes with a Component and three blueprint interfaces that you can implement in your game/system to get up and running with a Interaction System as Fast as possible. It also has an example of a modification made to the Interaction System specifically for the Mounting System in use for Linked actors. We'll go over this in more detail in the Basic Interaction System tutorial.
1.2.2 Mannequin Animations
The Demo Project also comes with a few animations that have been built for demonstration purposes. They are proof of concept for mounting, dismounting, and sitting, and changing seat animations.
Check out these animations at “Content/Mannequin/Animations”. They include the following:
- AM_Horse_MountLeftSide
- AM_Horse_MountRightSide
- AM_Horse_DismountLeftSide
- AM_Horse_DismountRightSide
- AM_Horse_MoveToFrontSeat
- AM_Horse_MoveToBackSeat
- A_Idle_HorseDriving
- A_Idle_HorseRiding
- AM_Buggy_MountLeft
- AM_Buggy_MountRight
- AM_Buggy_DismountLeft
- AM_Buggy_DismountRight
- A_Idle_BuggyDriving
- A_Idle_BuggySeated
The “A” stand for normal Animation Sequences you can plugin to your anim graph while the AM stand for AnimMontages that can be played over your current Animations if setup properly.
All of these AnimMontages are setup to play from the Default Slot, nothing really special has been set up for them beyond that. You will see anim notifies if you are looking at the Finished project and we will cover that later.
1.2.3 Robot Horse
The legendary Robot Horse! It has no animations, and a body built of highly detailed reflective spheres and cubes, but it does the job of a stand in horse mount with a skeleton of its own. Maybe one day it will get animations and improved body details. Currently it is programmer art at best, I highly suggest replacing it with your own mount or using one of the Epic Assets. I have found the Bear in the Infinity Blade : Advisaries pack or even Shinbi's Wolf make great replacements.
2.0 Create the Basic Assets
Return to Table of Contents
In this section we will be setting up our basic workspace for the Demo Project. We will establish our folder structure and create our map we will be using and create our various blueprint classes such as the Character, Mount, Controller, and GameMode.
2.1 Setup Folder Structures
First thing we will do is setup our folder structure for the tutorial.
First if it is not already created, let us start by creating the MountingContent folder.
Next we create the Blueprints Folder inside the MountingContent folder
Finally create the Maps Folder also under the MountingContent folder.
2.2 Create a Demo Map
Now we need a map to work on. We will create a new map based on the First Person Template level as it is setup and easy to use.
Open the ThirdPersonExampleMap found under “Content/ThirdPersonBP/Maps” and then select "Save Current As" in the File Menu.
Navigate to the Maps Directory under “Content/MountingContent/Maps” and save a new map as Map_MountingDemo. I like to prefix my maps with "Map_" to make them easy to search for.
2.3 Setup Core Classes
Now we need to setup our Core classes that we will be working with. This will include our Character, Mount, Controller, and GameMode.
2.3.1 Create a new Character
Now we will create a new Character that will act as our players representation in the world, our player character. To make things simple we will be basing it off of the Third Person Character supplied by Epic.
Go to “MountingContent/Blueprints” and create a new Blueprint Class.
Choose the ThirdPersonCharacter as the base class and name it BP_MountingCharacter. We’ll use the ThirdPersonCharacter as a starting point for our own character because it is already setup for basic movement and a camera controls.
2.3.2 Create the Mount
Next we need a mount, this can be any Actor, Pawn or Character, or WheeledVehicle derrived class / blueprint. When interacted with our controller will take control of the Mount and place our character on the mount at a specified seat location. It is important to know that only Pawns and Characters can be possessed by the PlayerController and Actors would be things your character simply sits on but would not have direct control over with out additonal programming outside the mounting system. The Controls of the mount would need to also be defined either in the PlayerController itself or on the Mount Class / blueprint.
Next Duplicate BP_MountingCharacter or create another Blueprint Class from the ThirdPersonCharacter and name it BP_Horse. This will be our mount character. Alternatively you can use the Buggy from the Advanced Vehicle Content Package.
Open up the “BP_Horse” and select the Mesh Component.
Set the Mesh property of the MeshComponent to be “RobotHorse_01” (or whatever your preferred mount will be).
Then Set the “Anim Class” to “ABP_RobotHorse_C”. This AnimBlueprint currently does nothing interesting but is a placeholder for the future when I decide to add a bit more to the Robot Horse.
2.3.3 Create the Player Controller
Now we need a Player Controller. The Mounting system leverages the player controller as its main coordinator between Character and Mount.
Next Create a new Blueprint Class using the “PlayerController” as a base class.
Name the new Player Controller “BP_MountingPlayerController”.
2.3.4 Create the Game Mode
Next we need a game mode, this is not specifically part of the mounting system, but will serve to tell our map what Controllers, Pawns, and UI to spawn on start up.
Finally create another new blueprint using the "GameModeBase" as the base class. We will be using it for playing the game.
Call the new GameMode class “BP_MountingGameMode”.
Open up the newly created game mode and set “BP_MountingPlayerController” for the “Player Controller Class”.
Then set “BP_MountingCharacter” for the “Default Pawn Class”.
Finally with the “MountingDemoMap” open go to World Settings. You may need to activate this pane by going to “Window->WorldSettings”.
In the GameMode section choose “BP_MountingGameMode” from the dropdown menu.
3.0 Interface and Component Setup
Return to Table of Contents
The PNMS operates on a few simple principles and guidelines that make it universal to implement on nearly every game.
The Main principle is that it makes very few assumptions about the types of actors it can be implemented on. The assumptions it does make is that your Mounts base class will be at least an Actor, and if it is a mount you can possess it needs to be at least a Pawn or a Wheeled Vehicle base class. Your Riders are assumed to be at least Pawns. Each Pawn is assumed to be Controlled by a controller either a PlayerController or an AIController but the existence of a Controller is required for the system to work as the Controller is the primary coordinator between the rider and the mount.
To implement these principles and assumptions the Components of the System leverage interfaces to communicate with whatever Actors they are attached to rather than performing casts. Some of you may recognize this as the Dependency Inversion Principle. Basically, The Actors know about the Component they possess but the Components only know about the interface their owners implement not what their owners are (other than Actor Objects). Further more Components that need to communicate with each other on different actors do so through these interfaces.
In this section we will be adding the required Interfaces and Components to each of the assets we have created. This is only to show you what each of the components and interfaces that will be needed on the Mounts, Riders, and Controllers that you intend to work with in your game. In the following sections we will implement the Interfaces for each (Controllers, Riders, and Mounts).
3.1 Setup the Rider
We will first setup our Rider. This is the character our player is usually controlling in the world and the actor that becomes the rider of our mounts. Riders need 3 things.
- URiderComponent - This is the main component all riders need to function as a Rider of a Mount
- IADMUnifiedControllerPawn Interface - This interface is leveraged as an easy method to retrieve references of your (Rider, Mount, or Controller) regardless of which one is sent in as a parameter to some external function (such as Collision Volumes).
- IMountRiderInterface - This is how the Rider Component communicates with the Rider Actor itself, but also how other components such as the Mount Component or the Controller Component communicate with the Rider Component.
Click on Class Settings at the top of the Blueprint.
Under the Interface Section in the Details Panel click the “Add” Drop down.
Search and add the ADMUnifiedControllerPawn interface.
Then Add the MountRiderInterface. We will go over what each of these interfaces do in the next section.
Next add the MountRiderComponent to the component’s window of the BP_MountingCharacter class.
For the purposes of this tutorial name it MountRiderComponent. That is all we will do for now, the next sections will cover implementing the interfaces with the components.
3.2 Setup the Mount
Now we wil setup our Mount. This is the thing our Rider will be sitting on. Mounts can be any Actor such as a Chair or a Pawn based object that a Rider can take control over. Mounts Need 3 things to function:
- UMountablePawnComponent - This is the main component mounts need to function as mounts. Despite the name it can be used on Actors as well as Pawns.
- IADMUnifiedControllerPawn Interface - This is the same interface implemented on our Rider and serves the same functions, to allow you to retrieve your (Rider, Mount, or Controller) in external functions such as collision events.
- IMountablePawnInterface - This interface is how the MountablePawnComponent communicates with the Mount Actor itself and how other components such as the Rider and Controller Communicate with it.
Open up the BP_Horse blueprint.
Select “Class Setting” from the Top Menu and then in the “Details” Panel under the Interfaces panel click the “Add” drop down.
Add the ADMUnifiedControllerPawn.
Then Add the MountablePawnInterface.
Next add the MountablePawnComponent to the component’s window of the BP_Horse.
For this tutorial name it MountablePawnComponent.
With the MountablePawnComponent selected sure you check the property "IsMountable" to true on the Details Panel. This can be used to toggle ability to perform mounting on or off for the entire blueprint class.
This is all we will do for now as the next sections cover implementing these interfaces with the components.
3.3 Setup Controller
Finally we setup our Controller. This can be either a PlayerController or an AIController and if you intended to have your player and AI characters perform mounting you will need to implement these components and interfaces on both types of Controllers. As mentioned before the Controller acts as the primary coordinator between your Rider and your Mount, and aids in keeping track of which is which. For the Controller to work properly it needs 3 things:
- URiderControllerComponent - This is the main component Controllers need to function as the coordinator between mounts and riders.
- IADSUnifiedControllerPawn Interface - As mentioned before this interface allows us to retrieve our Rider, Mount, or Controller in external functions such as collision events.
- IRiderControllerInterface - This interface is how the RiderControllerComponent communicates with the Controller Actor itself, it is also how other components such as the MountRiderComponent and the MountablePawnComponent communicate with the RiderControllerComponent.
Open the BP_MountingPlayerController blueprint.
Select “Class Settings” from the top menu and then in the “Details” Panel under “Interfaces” click the “Add” Drop down.
Add the ADMUnifiedControllerPawn interface.
Then add the RiderControllerInterface interface.
Next add the RiderControllerComponent to the component’s window of the BP_MountingPlayerController.
Name it RiderControllerComponent.
The next sections will cover implementing the interfaces with these components
4.0 Implement the PlayerController
Return to Table of Contents
The first Class we will be implementing is the PlayerController. The main reason is simply it is one of the easiest classes to implement. As mentioned in previous sections the Mounting system relies on the Controller as a sort of coordinator between the Rider and the Mount. All mounting operations should begin from the Controller of the Rider.
The mounting system does not implement an interaction system natively and relies on your own expertise and project requirements to do this. However triggering the mounting and dismounting system is easy with a single function you can call for each of these operations.
4.1 General Setup
We will first go through setting up some basic properties and functions that are needed on our PlayerController.
4.1.1 Enable RPCs
First thing to do is Enable the RPC functionality of the RiderControllerComponent. You may be wondering why this is not turned on by default. The reason is backwards compatibilty for older version of the plugin for anyone who is updating from a version of UE such as 4.24 and older. The older verison of the plugin did not come with RPCs and relied upon you to build them out. Future version of this plugin will have the option enabled by default.
In the "Components" Panel select the "RiderControllerComponent" from the list.
Then in the "Details Panel" check true the property "Enable Action Rpcs". This option will allow the RiderControllerComponent to automatically send RPC calls rather than having to define your own on the character. RPC functions on components have a slight network overhead which is why this is an optional setting you can enable or disable, without this turned on you will need to first check if you are running on the Server or the Client and setup your own RPC calls to send the request between the two.
4.1.2 Setup the Owned Pawn
Now we need to tell the mounting system what our Owned Pawn is. The owned pawn is the pawn that our controller should return to when we dismount, for more on the concept of the owned pawn look at the Concepts Article. To do this we are going to override Controllers function APlayerController::Possess()
function and tell it what our owned pawn is.
We setup possession logic that tell the PNMS who our owned pawn is and who our controlled pawn is. You can achieve this with a single funciton call from the URiderControllerComponent::SetControlledPawn()
.
In the Functions section hover over it until you see the “Override” option appear.
Click the “Override” option and search for “OnPossess”.
This function is fired every time the controller takes possession of any pawn.
Get the RiderControllerComponent and call it’s function SetControlledPawn()
.
Pass in the PossessedPawn parameter to the newControlledPawn function parameter.
This is very important for ensuring the mounting system works properly. See Concepts for more information about Controlled vs Owned Pawns.
4.2 Unified Controller Pawn Interface
Return to Table of Contents
In this section we will be implementing the IADMUnifiedControllerPawnInterface. As mentioned before its purpose is to allow us to get our Mount, Rider, or Controller from external functions such as collision events that only return an actor that was hit. It is also heavily used by the components logic to get the appropriate Actor objects to operate on with out having to rely on casts to specific types.
4.2.1 Prepare to Mount
The first function we implement is the PrepareToMount. This function is called at the very start of the mounting process. Its purpose is to allow you to add some additional logic to prepare your controller for mounting, this is also a place where you can have the controller reject the mounting process by returning false. It is important to know that for the mounting system to proceed with mounting, this function must return true. There are many gameplay reasons you may not want a character to start mounting, they may be in a cutscene, the mount may not have room, there may be other factors to prevent mounting. This funciton is executed for all three (Controller, Rider, and Mount) and each can reject the mounting based on some criteria for your game.
Implement PrepareToMount()
by simply returning true.
In the case of more sophisticated logic you may want to put here, you can add custom errors to the Response object that is also returned with this function. This allows you to add custom alerts to your players or your developers to leverage and solve problems. By default the mounting system comes with several error codes that it populates automatically internally if it hits a problem that are also printed to the output log.
4.2.2 Prepare to Dismount
This function operates similar to PrepareToDismount()
. It is called at the very start of the dismounting process to allow you to perform preparations for your controller to dismount. This is also where you can have the controller reject the dismounting process by returning false. You may want to reject a dismount for various reasons. The character maybe in the middle of a cut scene, it maybe on a mount it cannot dismount automatically until it stops moving, etc.
We implement this function by simply returning true. But you can add logic here to determine if the Controller is in a decent state to dismount and even execute a few functions that can put it into a desired state to start dismounting.
4.2.3 Is Mounted
This function is used to test of your character is currently mounted or in the process of mounting. If All the PrepareToMount()
function succeed the IsMounted
flag is set to true even if the character is not physically on the mount yet. The Character has another flag called IsSeated
that can be used with IsMounted to determine if the character is seated on the mount.
Implement Is Mounted by calling the IsMounted
function from the RiderControllerComponent
4.2.4 Get Character Pawn
This function is designed to fetch the Owned Pawn, the pawn that represents the players or NPCs normal character they control. This is useful in many situations where you maybe unsure if you are using the Mount or the Character, particularly in UI and Collisions Events.
Implement GetCharacterPawn
by calling GetOwnedPawn()
from the RiderControllerComponent.
4.2.5 Get Character Mount
This function is designed to fetch the current Mount Actor. This is useful in situations where you maybe unsure if you are using the Mount or Character, particularly in situations such as UI and Collision Events. If the character is not mounted than this function implementation will return None (nullptr
).
Implement GetCharacterMount()
by calling GetMountActor()
from the RiderControllerComponent
4.2.6 Get Character Controller
This function is designed to fetch the Current Controller of the Rider or Mount. In this case if it is called on a PlayerController or an AIController it will always return itself. This is useful for sitations where you may need the controller but have either the Mount or the Rider instead or in the case of Collision Events an Actor you are unsure what it exactly is.
Implement GetCharacterController()
by supplying Self
as the output.
4.3 Implement IRiderControllerInterface
Return to Table of Contents
This section will implement the IRiderControllerInterface. Its purpose is to allow the RiderControllerComponent to communicate with the Controller it is attached to as well as allow other components and actors to communicate with it through the interfaces.
4.3.1 Possess Pawn (PNMS)
The purpose of this function is to allow the RiderControllerComponent to allow the other components such as the RiderComponent and the MountablePawnComponent to call possess without performing a controller cast. The function is prefixed with PNMS as a method of differentiating it from other functions that maybe similar. In C++ code this function looks like URiderControllerComponent::PNMS_PossessPawn()
.
To implement this function, begin by dragging the variable RiderControllerComponent get node onto the graph.
Then draw a line from the RiderControllerComponent node and search for PossessPawn
and add the function node to the graph.
Draw a line from the PawnToPossess
input parameter of PossessPawn (PNMS)
to the PawnToPossess
input pin of the PossessPawn()
function node.
Connect the return value of PossessPawn()
to the return value node.
4.3.2 OnRiderAdded
This function is a signal from the mount to notify it that a new rider has been added to the mount they are currently on. You can leverage this to update your UI in your game.
You implement this function by dragging a get node for RiderControllerComponent onto the graph.
Then draw a line from RiderControllerComponent node and look for RiderAdded
function.
Connect the input parameter NewRider
to the input parameter of the RiderAdded()
funciton of the same name.
Connect the input parameter SeatId
to the input parameter of the RiderAdded()
function of the same name.
Now on the Return Node true.
With this interface implemented you can now use the Callbacks on the RiderControllerComponent
This interface function allows you to use the OnRiderAdded
delegate function of the RiderControllerComponent.
4.3.3 OnRiderRemoved
This function is a signal from the mount to notify the the rider that another rider has been removed from a seat or dismounted. With this function implemented you can update your UI and/or now use the Callbacks on the RiderControllerComponent for OnRiderRemoved
.
You implement this function by dragging a get node for RiderControllerComponent onto the graph.
Then draw a line from RiderControllerComponent node and look for RiderRemoved()
function.
Connect the input parameter RemovedRider
to the input parameter of the RiderRemoved()
funciton of the same name.
Connect the input parameter SeatId
to the input parameter of the RiderRemoved()
function of the same name.
In the return node return true.
4.3.4 OnOtherRiderChangedSeat
This function is a signal from the mount to notify the Rider that another rider has changed seats within the mount they are attached to. With this function you can update your UI and/or bind to the OnOtherRiderChangedSeat
delegate in the RiderControllerComponent.
To implement this function drag a get node for RiderControllerComponent onto the graph.
Then draw a line from RiderControllerComponent node and look for OtherRiderChangedSeat
function and add it to the graph.
Connect the input parameter OtherRider
to the input parameter of the OtherRiderChangedSeat
funciton of the same name.
Connect the input parameter NewSeatId
to the input parameter of the OtherRiderChangedSeat
function of the same name.
Connect the input parameter OldSeatId
to the input parameter of the OtherRiderChangedSeat
function of the same name.
Return true.
4.3.5 OnMountActionFailed
This function is called anytime a mount action has an error and is unable to complete the mounting operation. You can add your own custom error catching / reporting logic here. The Response Object that is returned will contain an System Error Code but can also be set to be a Custom Code you created and assigned to the response object yourself. For this tutorial we will not be adding any sort of error handling for the mounting system but feel free to add your own.
A log will also accompany this failure in the text output. All Prepare and Start interface functions, as well as all 3 basic operations return the response structure so you can customize reasons for why you failed the mounting in your game to send the player messages appropriately.
4.3.6 OnDismountActionFailed
This function is called anytime the dismount action has an error and is unable to complete the dismount operation. You can add your own custom error catching / reporting logic here. The Response Object that is returned will contain an System Error Code but can also be set to be a Custom Code you created and assigned to the response object yourself. For this tutorial we will not be adding any sort of error handeling for the mounting system but feel free to add your own.
A log will also accompany this failure in the text output. All Prepare and Start interface functions, as well as all 3 basic operations return the response structure so you can customize reasons for why you failed the mounting in your game to send the player messages appropriately.
4.3.7 OnChangeSeatActionFailed
This function is called anytime a change seat operation has an error and has failed. You can add your own custom error catching / reporting logic here. The Response Object that is returned will contain an System Error Code but can also be set to be a Custom Code you created and assigned to the response object yourself. For this tutorial we will not be adding any sort of error handeling for the mounting system but feel free to add your own.
A log will also accompany this failure in the text output. All Prepare and Start interface functions, as well as all 3 basic operations return the response structure so you can customize reasons for why you failed the mounting in your game to send the player messages appropriately.
4.3.8 GetRiderControllerComponent
This is a very handy function you can leverage to retrieve the RiderControllerComponent. When combined with the IADMUnifiedPawnController it allows you to easily perform custom logic from anywhere such as collision events and within the UI widgets.
Simply return the RiderControllerComponent. This function allows you to retrieve the RiderControllerComponent without executing a specific cast.
4.3.9 Begin Mounting Actor
This funciton is used to Start the mounting process. This funciton acts as a wrapper for the RiderControllerComponent function PerformMountActor()
that starts position based mounting. This means the character will mount to a seat on the mount that they are closest to.
To implement this funciton drag a get node for RiderControllerComponent onto the graph.
Draw a line from RiderControllerComponent and search for the function PerformMountActor()
and place it onto the graph.
Connect the execution lines from the function start node and the return node.
Draw a line or get the input parameter NewMount
and connect it to the NewMountActor
input parameter for the PerformMountActor()
node.
Draw a line or get the input parameter LinkedActor
and connect it to the NewLinkedActor
input parameter for the PerformMountActor()
node.
Now return the output parameters of PerformMountActor()
to the return node.
4.3.10 Begin Mounting Actor To Seat
This function is used to Start the mounting process to a specific seat. This function acts as a wrapper for the RiderControllerComponent function PerformMountActorToSeat()
that starts seat based mounting. This means the character will mount to a specified seat on the mount rather than having the system figure out a best seat based on its location.
To implement this function drag a get node for RiderControllerComponent onto the graph.
Draw a line from RiderControllerComponent and search for the function PerformMountActorToSeat and place it onto the graph.
Connect the execution lines from the function start node and the return node.
Draw a line or get the input parameter NewMount
and connect it to the NewMountActor
input parameter for the PerformMountActorToSeat()
node.
Draw a line or get the input parameter LinkedActor
and connect it to the NewLinkedActor
input parameter for the PerformMountActorToSeat()
node.
Draw a line or get the input parameter SeatId
and connect it to the SeatId
input parameter for the PerformMountActorToSeat()
node.
Now return the output parameters of PerformMountActorToSeat()
to the return node.
4.3.11 Begin Dismounting Actor
This funciton is used to Start the dismounting process. This funciton acts as a wrapper for the RiderControllerComponent function PerformDismountActor()
that starts the dismounting operation.
To implement this function drag a get node for RiderControllerComponent onto the graph.
Draw a line from RiderControllerComponent and search for the function PerformDismountActor()
and place it onto the graph.
Then Draw a second line from RiderControllerComponent and search for the function GetMountActor()
and place it onto the graph.
Connect the output from GetMountActor()
to the input parameter DismountingActor
of the PerformDismountActor()
node.
Connect the execution lines from the function start node and the return node.
Now return the output parameters of PerformDismountActor()
to the return node.
4.3.12 Begin Changing Seat to Index
This funciton is used to Start the seat changing process. This function acts as a wrapper for the RiderControllerComponent function PerformChangeSeatToIndex()
that starts changing seats to a specified seat index. The seat Index should be a valid index in the seat array of the Mount, but error handling makes this function very forgiving.
To implement this function drag a get node for RiderControllerComponent onto the graph.
Draw a line from RiderControllerComponent and search for the function PeformChangeSeatToIndex()
and place it onto the graph.
Draw a line or get the input parameter SeatIndex
and connect it to the SeatIndex
input parameter for the PeformChangeSeatToIndex()
node.
Connect the execution lines from the function start node and the return node.
Now return the output parameters of PeformChangeSeatToIndex()
to the return node.
4.3.13 Begin Changing Seat By Id
This function is used to Start the seat changing process. This function acts as a wrapper for the RiderControllerComponent function PerformChangeSeatById()
that starts changing seats to a specified seat Id. The seat Id should be a valid seat Id found on a seat in the seat array of the Mount, but error handling makes this function very forgiving.
To implement this function drag a get node for RiderControllerComponent onto the graph.
Draw a line from RiderControllerComponent and search for the function PerformChangeSeatById()
and place it onto the graph.
Draw a line or get the input parameter SeatIndex
and connect it to the SeatIndex
input parameter for the PerformChangeSeatById()
node.
Connect the execution lines from the function start node and the return node.
Now return the output parameters of PerformChangeSeatById()
to the return node.
5.0 Implement the Rider
Return to Table of Contents
In this section we will be implementing the Rider (our Pawn or Character class) that will be attaching to the mount. It is important to know that when a Rider takes a seat, and is the Driver of that mount they are directly controlling the mount itself and not their original Pawn. All Control schemes are based on the control schemes you have setup on the mount. Also any animations you want your original Pawn to perform (such as aimed shooting) need to feed through the mounts control system and into the animation system of the Pawn. Setting this up is beyond the scope of the Plugin as it is very project specific.
5.1 General Setup
Now we will go through setting up some basic properties and functions that are going to be needed by our Character.
5.1.1 Relative Mesh Properties
First we need to establish our characters base mesh properties. These are very important for a Networked Game using this plugin because of the nature of attachments, replication, and network Lag. These three variables (attachment, replication, and lag) can cause lots of issues in Networked games with mounts and it was this very set of problems that was inspiration for creating this plugin.
There are two highly important properties to set here; RelatifveMeshLocation
and RelativeMeshRotation
. These values ensure that when your character dismounts the character mesh is oriented properly. The detachment in networked games has a tendency to move and rotate the character mesh. With these values set they reinforce the original location and orientation of the character mesh component.
To adjust these properties select the MountRiderComponent in the Components panel.
On the Details Panel for the MountRiderComponent look for the two properties RelativeMeshLocation
and RelativeMeshRotation
.
These two values need to match the Mesh Component's location and rotation on your character.
In multiplayer games attachment and detachment can do strange things to the local offset of the characters mesh and capsule. These two properties are used to reset the client side mesh component when it gets off of the mount during dismounting. In general if you are using the standard Mannequin then these values will be simple with a Z value of -90 and a Z rotation (or Yaw) of -90 degrees. In the image above you can see where to find these values
You can use the constructor script to keep the relative locations and rotations of your mount synced up for all subclasses of your character.
5.1.2 Mounting Behavior Settings
The other thing to be aware of are the two properties MountingPossessionBehavior
and DismountingPossessionBehavior
these properties tell the mounting system when to take possession of the mount or the rider during the mounting and dismounting processes. For most cases you will not need to change these values unless you are trying to achieve a specific flow.
They each have a few enum values. For "Mounting Possession Behavior" you have the following options
- Manual - You will handle possession of the mount in code by calling
MountRiderComponent::AllowControllerToPossessMount
in C++ or Blueprints. - RiderFinishedMoving - Possess the mount after the rider has finished moving into position, if no movement logic is in place then it will automatically possess the mount
- RiderFinishedMounting - Possess the mount after the mounting animation is finished playing, if no animaiton logic is in place then it will automatically possess the mount.
The "Dismounting Possession Behavior" has the following options
- Manual - You will handle possession of the Rider in code by calling
MountRiderComponent::AllowControllerToPossessRider
in C++ or Blueprints. - Immedately - possess the rider immedately after triggering the dismount
- RiderFinishedDismounting - possess the rider after the dismount animation finishes playing, if no dismount animaiton plays then the possession of the rider is immedate.
5.1.3 Rider Attachment Behaviors
Next the two rider attachment behaviors tell the mounting system when it should attach the the rider to the mount and detach the rider from the mount during the mounting and dismounting process. The two properties that control this are MountingAttachmentBehavior
and DismountingAttachmentBehavior
. For most cases you will not need to change these values unless you are trying to achieve a specific effect during animation. The values for these enums are identical to the possession values because they often occur nearly at the same time.
For the MountingAttachmentBehavior you have the following options
- Manual - You will handle attachment of the mount in code by calling
MountRiderComponent::AttachRiderToMount()
in C++ or blueprints. - RiderFinishedMoving - Attach to the mount after the rider has finished moving into position, if no movement logic is in place then it will automatically attach to the mount
- RiderFinishedMounting - Attach to the mount after the mounting animation is finished playing, if no animation logic is in place then it will automatically attach to the mount.
The DismountingAttachmentBehavior has the following options
- Manual - You will handle dettachment from the mount in code by calling
MountRiderComponent::DetachRiderFromMount
in C++ or Blueprints - Immedately - detach the rider Immediately after triggering the dismount
- RiderFinishedDismounting - detach the rider after the dismount animation finishes playing, if no dismount animation plays then the detachment of the rider is immediate.
5.2 Character Unified Controller Pawn
Return to Table of Contents
In this section we will be implementing the IADMUnifiedControllerPawn Interface on our Character. As mentioned before its purpose is to allow us to get our Mount, Rider, or Controller from external functions such as collision events that only return an actor that was hit. It is also heavily used by the components logic to get the appropriate Actor objects to operate on with out having to rely on casts to specific types.
5.2.1 Prepare to Mount
The first function we implement is the PrepareToMount()
. This function is called at the very start of the mounting process on all three entities involved (Controller, Rider, Mount). Its purpose is to allow you to add some additional logic to prepare your Rider for mounting, this is also a place where you can have the Rider reject the mounting process by returning false. It is important to know that for the mounting system to proceed with mounting, this function must return true.
Implement PrepareToMount()
by simply returning true.
In the case of more sophisticated logic you may want to put here, you can add custom errors to the Response object that is also returned with this function. This allows you to add custom alerts to your players or your developers to leverage and solve problems. By default the mounting system comes with several error codes that it populates automatically internally if it hits a problem that are also printed to the output log.
5.2.2 Prepare to Dismount
This function operates similar to PrepareToDismount()
. It is called at the very start of the dismounting process to allow you to perform preparations for your Rider to dismount. This is also where you can have the Rider reject the dismounting process by returning false. You may want to reject a dismount for various reasons specirfic to your game.
We implement this function by simply returning true. But you can add logic here to determine if the Rider is in a decent state to dismount and even execute a few functions that can put it into a desired state to start dismounting.
5.2.3 Is Mounted
This function is used to test of your Rider is currently mounted or in the process of mounting. If All the PrepareToMount()
functions succeed the IsMounted
flag is set to true even if the Rider is not physically on the mount yet. The Rider has another flag called IsSeated
that can be used with IsMounted
to determine if the Rider is seated on the mount.
Implement the IsMounted()
function by using a validated get to retrieve the MountRiderComponent.
For the IsValid()
execution line call the IsMounted()
function from the MountRiderComponent and supply it into the return.
For the IsNotValid()
execution line simply return false.
Because this function could be called from the AnimBlueprint of your Rider you want to ensure that the MountRiderComponent has not been destroyed while the Animation Graph is updating. This typically occurs during game shutdown where the AnimBP is still running but the components have been cleaned up and destroyed on the Rider.
5.2.4 Get Character Pawn
This function is designed to fetch the Owned Pawn, the pawn that represents the players or NPCs normal character they control. Similar to the controller this is useful in many situations where you maybe unsure if you are using the Mount or the Character, particularly in UI and Collisions Events. This function being implemented on all three Entities (Controller, Rider, Mount) allows you to get the object you need reguardless of which one of the three you currently have using the same interface call. In this particualr case we want to return our "self".
For GetCharacterPawn()
simply return self.
5.2.5 Get Character Mount
This function is designed to fetch the current Mount Actor. This is useful in situations where you maybe unsure if you are using the Mount or Rider, particularly in situations such as UI and Collision Events. If the Rider is not mounted than this function implementation will return None (nullptr
).
For GetCharacterMount()
use a Validated Get for the MountRiderComponent.
For the IsValid()
execution line call the MountRiderComponent::GetCurrentMount()
function and return it.
For the IsNotValid()
execution line simply return nothing.
Again the Validated Get is useful for functions that will be called by your Animation Blueprints and could be called during the game shutdown when these components may be destroyed.
5.2.6 Get Character Controller
This function is designed to fetch the Current Controller of the Rider or Mount. This is useful for sitations where you may need the Rider but have either the Mount or the Controller instead or in the case of Collision Events an Actor you are unsure what it exactly is.
This is where some of the magic of the IADMUnifiedControllerPawn interface begins to become apparent. Calling the MountRiderComponent::GetRiderController()
will always return the controller that actually owns the Rider. In cases where you are now controlling a horse or vehicle rather than your pawn this will always retrieve the Controller this character is supposed to be controlled by.
Naturally for AI Characters this will be an AI Controller but for Player Characters this will be their Player Controller.
5.3 Implement MountRiderInterface
Return to Table of Contents
This section will implement the IMountRiderInterface. Its purpose is to allow the MountRiderComponent to communicate with the Riderit is attached to as well as allow other components and actors to communicate with it through the interfaces. For instance the RiderControllerComponent will call functions in this interface inorder to trigger something to happen on the MountRiderComponent.
5.3.1 StartPawnMounting
This function is called during the RiderControllerComponent::PerformMountActor()
function. Once all three IADMUnifiedPawnController::PrepareToMount()
functions have returned true
, then the RiderControllerComponent will trigger this function on the Rider to start its mounting sequence.
Implement this function by grabbing the MountRiderComponent then call it’s MountPawn()
function.
Supplying the NewPawnMount
as a parameter and returning the response and bool value.
The NewPawnMount
parameter is the new pawn or actor you wish to mount. Similar to IADMUnifiedControllerPawn::PrepareToMount()
if a linked actor was supplied by the Controller it will pass it along into this function and the MountRiderComponent will skip most of the mounting logic provided the linked actor is not already occupied.
5.3.2 StartPawnDismounting
This function is called during the RiderControllerComponent::PerformDismountActor()
function. Once all three IADMUnifiedPawnController::PrepareToDismount()
functions have returned true
, then the RiderControllerComponent will trigger this function on the Rider to start its Dismounting sequence.
Implement this function by grabbing the MountRiderComponent and calling it’s DismountPawn()
function.
Supply the OldPawnMount
as a parameter to the function and return the output.
Note that this function does not have an optional Linked actor input. The mounting system is designed to treat linked actors as an intrinsic part of the overall mount itself and internally manage the connections between mount and linked actors. That does not, however, prevent you from injecting logic into the various interface functions and obtain the linked pawn from the RiderControllerComponent or the MountRiderComponent which both have a reference to the linked actor.
5.3.3 OnMountingPawnFinished
This function finalizes the mounting process and must be called otherwise the mounting process will remain incomplete. The IsSeated
property will never be set and the final attachments and assumption of control may not be performed.. If you are using a Simple Flow (with no movement and animation see Concepts for more info) or a Mixed Flow with only Movement then this will be called automatically. But if you are using animations then you must call this function some how to finish the mounting. The easiest method is to use the Animation Notify MountingFinished which comes as part of the PNMS plugin.
Implementing this function is straight forward. Drop a get node for MountRiderComponent.
Draw a line from the MountRiderComponent node and look for the function PawnFinishedMounting()
.
Connect the output value of the PawnFinishedMounting()
function to the return node.
This function contains a input parameter that will hold the mount being mounted to, this is optional for you to easily perform checks or other operations on the mount without having to fetch it from the component.
For more on Animations see one of the Animations Tutorial found on the Tutorials Page for the PNMS.
5.3.4 OnDismountPawnFinished
This function finalizes the dismounting process and must be called otherwise the dismounting process will remain incomplete. The IsSeated
property will remain set to true and the final dettachments and relinquishment of control may not be performed. If you are using a Simple Flow (with no movement and animation see Concepts for more info) or a Mixed Flow with only Movement then this will be called automatically. But if you are using animations then you must call this function some how to finish the mounting. The easiest method is to use the Animation Notify DismountFinished which comes as part of the PNMS plugin.
To Implement this function drop a get node for MountRiderComponent.
Draw a line from the MountRiderComponent node and look for the function PawnFinishedDismounting().
Connect the output value of the PawnFinishedDismounting() function to the return node.
This function contains a input parameter that will hold the mount being mounted to, this is optional for you to easily perform checks or other operations on the mount without having to fetch it from the component.
In this we call StopAnimMontage() with no montage supplied to stop all montages. This will cause all currently playing montages to stop running. This may or may not be a desired effect for you, so you may need to make adjustments to store the currently running anim montage for reference later.
For more on Animations see one of the Animations Tutorial found on the Tutorials Page for the PNMS.
5.3.5 OnChangeToNewSeatCompleted
This function finalizes the seat changing process and must be called otherwise the seat changing process will remain incomplete. If not called the seat attachment logic will never be called and the character will remain in their old seat. If you are using a Simple Flow (with no movement and animation see Concepts for more info) or a Mixed Flow with only Movement then this will be called automatically. But if you are using animations then you must call this function some how to finish the mounting. The easiest method is to use the Animation Notify ChangeSeatsFinished which comes as part of the PNMS plugin.
To Implement this function drop a get node for MountRiderComponent.
Draw a line from the MountRiderComponent node and look for the function PawnFinishedChangingSeats()
.
Connect the output value of the PawnFinishedChangingSeats()
function to the return node.
In this we call StopAnimMontage()
with no montage supplied to stop. This will cause all currently playing montages to stop running. This may or may not be a desired effect for you, so you may need to make adjustments to store the currently running anim montage for reference later.
For more on Animations see one of the Animations Tutorial found on the Tutorials Page for the PNMS.
5.3.6 MoveToMountingLocation
This function is called as part of the main mounting process within the MountRiderComponent inorder to allow you to move a Rider into place before it starts to get onto the mount. Currently the PNMS does not come with a native move system, it is up to the developers to implement a system for this and this function provides the space to do so. If this function returns false, it assumes no movement logic is occuring and will immedately proceed to MountRiderComponent::OnMovetoMountingLocationComplete()
which completes the movement action.
For now we will leave the implementation of this function empty, returning false
and skipping the movement logic entirely.
We will cover moving in another tutorial.
5.3.7 OnMoveToMountingLocationCompleted
This function is called when the you have finished moving the character into place or immediately if you are not using movement. If you are using movement then you will need to call this function when you have detected that your potential Rider has reached its goal location. There is no built in supported way to determine if you have reached your destination for the mounting system plugin, so this is something you'll have to create yourself. Ultimately when you have determined your character is close enough call the IADMUnifiedControllerPawn::OnMoveToMountingLocationCompleted()
function.
Implementing this function is simple. Drop a get node for MountRiderComponent onto the graph.
Draw a line from MountRiderComponent and search for MoveToMountingLocationComplete()
.
Connect the output from the MoveToMountingLocationComplete()
node to the return node.
5.3.8 IsSeatedOnMount
This function can be used to determine if the character is currently sitting on the mount. The IsSeated
property of the MountRiderComponent is set to true once PawnFinishedMounting()
is executed.
To implement this function we place a get node for MountRiderComponent and then convert it to a Validated Get.
Connect the IsValid()
execution line to the return.
From MountRiderComponent call the function IsSeated
.
Connect the output of the IsSeated
funciton to the Return node connected to the IsValid
execution line.
Add a second Return node and conect it to the IsNotValid()
execution line.
Make sure the second return node returns false.
We use a Validated Get here because this function will most likely be used by the Animation Graph. When your game shuts down the components are destroyed first on your actor and the Animation Graph continues to update animations. Without the validated git it will cause annoying error messages.
5.3.9 IsDriver
This function is called to easily determine if your Rider is currently the Driver of its current Mount. This is determined by the current seat the driver is sitting on.
Place a get node for the the MountRiderComponent on the graph.
Draw a line from MountRiderComponent get node and look for IsDriver()
function and select it.
Connect the output from IsDriver
the return node.
5.3.10 GetRiderMesh
This function is used by the system to perform relative location and rotation corrections during the mounting and dismounting in networked games. Because of lag, attaching can cause strange offsets to the mesh component which requires that it be reset during game play.
To implement this function drop a get node for Mesh
Component onto the graph and connect it to the return variable.
5.3.11 GetMountRiderComponent
This function allows you retrieve the MountRiderComponent, allowing you to call any function not covered in the interface for any custom logic you may want to add to the system. Using this interface to do so is much more efficient than leveraging the GetComponentByClass()
, GetComponentByInterface()
, or GetComponentByTag()
functions.
To implement place down a get node for MountRiderComponent
Connect the output from the get node to the return value.
5.3.12 .PlayMountingAnimation
This function is called automatically as part of the mounting process. It is meant to allow you to trigger any montage animations to play when the Rider mounts the Mount. This could be opening a Door, swinging their leg over a saddle, or poping the airlock on their ship. This is also an optional part of the system, if you simply return false then the Mounting process automatically calls the MountRiderComponent::PawnFinishedMounting()
. If you are using mounting animations then by returning true, must call MountRiderComponent::PawnFinishedMounting()
manually or use the AnimNotify MountingFinished.
For this demo we will leave this as it is.
In the final project there is a fully working demonstration of how you can supply animations per mount for your character that you are free use for your own games.
5.3.13 Play Dismounting Animation
This function is called automatically as part of the dismounting process. It is meant to allow you to trigger montage animations to play when the Rider dismounts from their mount. This is an optional part of the system, if you return false then the Dismounting Process automatically calls MountRiderComponent::PawnFinishedDismounting()
. If you are using dismounting animations then you must eventually call MountRiderComponent::PawnFinishedDismounting()
.
For this demo we will leave this as it is.
In the final project there is a fully working demonstration of how you can supply animations per mount for your character that you are free to use for your own games.
5.3.14 PlayMoveToSeatAnimation
This function is called automatically as part of teh chagne seat process. It is meant to allow you to play montage animations when the rider changes seats while mounted. This could be as simple as moving forward to take the reigns or hoping from the back seat to the driver seat of a car. Like all the Animation functions they are optional. By returning false on this function your character will simply teleport to the desired seat location. If you are using animations the Advanced Demo includes a simple Blueprint struct it uses to indicate what animations to play when moving between seats. In the case you are using animations for seat changes you will want to call MountRiderComponent::OnChangeTonewSeatCompleted()
to finalize the seat movement. The easiest way to call that function is by using the AnimNotify ChangeSeatFinished on your Animation.
For this demo we will leave this as it is.
In the final project there is a fully working demonstration of how you can supply animations per mount for your character that you are free to use for your own games.
5.3.15 SetRiderCollisionEnabled
This function is called by the MountRiderComponent::PawnFinishedMounting()
and MountRiderComponent::PawnFinishedDismounting()
functions to allow you to disable and enable collision elements of the rider. This is useful to prevent strange physics behavior from while mounted. You can also override this to turn it off or on directly by switching off MountRidercomponent::bAllowCollisionOverride
to false
. Also do not forget that there other methods of handling collisions Unreal such as Custom Collison profiles. Ultimately how you handle collision is up to you, if you are unfamiiar with how unreal handles collisions I suggest looking at the collision documentation in Unreal.
The main thing you probably want to implement is to disable collision on the Character Capsule itself. If you are making a game where characters need to be shot while riding then you need to adjust your collision detection to use the mesh itself instead of the capsule. You can tackle this in other ways too such as having a different collision volume that only interacts with weapons and damage volumes, or fiddling with the Unreal Colliison Settins to setup something custom for your game.
The ShouldEnable
parameter of this function simply tells you if you need to enable collision or disable collision because this is the same function called for dismounting as well as mounting. How and in what way that collision is disabled or enabled is up to you.
I have chosen to Disable to Collision on the CapsuleComponent and use a Select Node with NoCollision
when false and CollisionEnabled (Query and Physics)
when true. In general individual mesh collision does not need to be disabled. I simply return true if successful.
5.3.16 GetSeatId
This function primarily used internally to fetch the current SeatID
of the Rider. It can also be leveraged by any supporting systems you may design for the plugin.
To implement this we get the MountRiderComponent and drop it onto the function graph.
Then right click on the get and convert it to a Validated Get.
From the Validated Get Node return value draw a line and search for GetSeatId()
and select it.
From the IsValid()
execution path connect it to a return node and return the value of GetSeatId()
function node.
Then from the IsNotValid()
execution line connect it to a return node which returns -1
. The value of -1
is an invalid seat number and allows the system to detect if something has gone wrong and abandon the mounting process.
5.3.17 Get All Rider Skeletal Meshes
This function is used to retrieve the skeletal meshes for the Rider that need to continue to animate in a Multiplayer Game. This function is used by the system to ensure that animations continue to play on the server even while movement replication is deactivated. If you have linked the animations of mulitple skeletal components then you should only require the master skeletal mesh component that controls the others.
There is strange issue with Unreal Engine when you disable replicated movement on a character that seems to prevent all skeletal mesh animation updates from occurring on the server, this is fine for a dedicated server but not so fine for a Listen Server where you will see the other characters not animating. This simply facilitates the logic built into the Mounting System that ensures all Skeletal Mesh Components continue to animation after Replicate Movement is turned off on them.
To implement this function we draw a line out from the return value and search for Make Array node.
Then we place a get node for Mesh Component and connect it to the element 0 of the make array node.
5.3.18 UpdateRiderMovementMode
This function is to change the rider movement mode so that it is not trying to perform movement corrections and adjustments while mounted.
To implement this function first place down a CharacterMovement Component reference
From the CharacterMovement componnet draw a string and look for SetMovementMode()
From the SetMovementMode()
node draw a line back from NewMovementMode input parameter and search for a Select Node.
Attach the IsMounted input parameter into the select node's Index pin
For False choose Walking
and for True choose None
.
Then place down a second CharacterMovement component node and draw a line from it to find IgnoreClientMovementErrorChecksAndCorrection
and set it to the same value as the IsMounted input parameter.
return true to tell the Mounting system that this function has handled the movement update change.
6.0 Implement the Mount
Return to Table of Contents
In this section we will be implementing the Mount which our Riders will be Riding. Generally this will be a Pawn
or WheeledVehicle
(which are possessable actors) that a player can control and Drive. However it can also be a basic actor that the character attaches to inorder to ride it or just sit on it. As mentioned before it is important to know that when a Rider takes the Driver seat a mount, they are directly controlling the mount themselves. All Control schemes are based on the control schemes you and your developers have setup on the mount. Also any animations you want your original Pawn to perform (such as aimed shooting) need to feed through the mounts control system and into the animation system of the Pawn. Setting this up is beyond the scope of the Plugin as it is very project specific but it provides methods to getting your original pawn so you can setup animations with the mounted Rider.
6.1 General Setup
We wil be setting up some basic properties and functions for our mount. For C++ Classes or Blueprints that use the AWheeledVehicle
for a base class these next steps are not required. Only C++ or Blueprint classes that use APawn
or ACharacter
require a bit of additional setup in a networked Game to keep them from having some "Strange Behavior" which we will go over as we setup.
6.1.1 AI Controller Reference
One of the major issues discovered about the mounting process is if you dismount while moving your mount will stop moving on the Server, but it will continue moving on local clients. To prevent this we need to have a Controller retake control of our APawn
or ACharacter
based Mounts.
First we need to create a new variable to hold a reference to the original AIController that will take control of our mount.
Create a new variabvle in the Variables list and call it AIControllerRef and make it the type Controller
. We only need to use the Possess
function that all controllers have so we do not care if we are dealing with a PlayerController
or an AIController
.
6.1.2 Override the Possessed Function
We need our Mount to know its original Controller, usually this will be an AIController
. with our AIControllerRef variable we are going to setup the APawn::Possessed() function to check if that variable is valid, if it is None then we will set it to the first controller that takes possession of it.
Go to the Functions section on the right side of your blueprint and hover over until you see the "Override" drop down appear.
Click on the "Override" button and search for "Possessed" to find the Possessed
Event function and click on it to start implementing it.
The Possess function passes in a NewController
variable. We are indeed making an assumption that for this pawn it will always first be possessed by an AIController
first and that will be the that we set to our parameter AIControllerRef.
I've not had any issues with this funciton not having an Authority switch as I do not believe it is executed on the client, but I still put a HasAuthority()
switch just in case.
Then we first test if our AIControllerRef variable is valid by using a validated get. If it is valid we have no need to set our reference to the controller.
That's all for our general setup here. We'll come back to this AIController
later as we setup the MountablePawnInterface.
6.2 Unified Controller Pawn
Return to Table of Contents
In this section we will be implementing the IADMUnifiedControllerPawn
Interface on our Mount. As mentioned before its purpose is to allow us to get our Mount, Rider, or Controller from external functions such as collision events that only return an actor that was hit. It is also heavily used by the components logic to get the appropriate Actor objects to operate on with out having to rely on casts to specific types.
6.2.1 PrepareToMount
The first function we implement is the PrepareToMount()
. This function is called at the very start of the mounting process on all three entities involved (Controller, Rider, Mount). Its purpose is to allow you to add some additional logic to prepare your Mount for mounting, this is also a place where you can have the Mount reject the mounting process by returning false. It is important to know that for the mounting system to proceed with mounting, this function must return true.
You may want to reject the mounting process for various conditions in your game. For example, an animal may reject mounting for Riders that are not their Master, a car or ship may require a key or id card.
Implement PrepareToMount()
by simply returning true.
In the case of more sophisticated logic you may want to put here, you can add custom errors to the Response object that is also returned with this function. This allows you to add custom alerts to your players or your developers to leverage and solve problems. By default the mounting system comes with several error codes that it populates automatically internally if it hits a problem that are also printed to the output log.
6.2.2 PrepareToDismount
This function operates similar to PrepareToDismount()
. It is called at the very start of the dismounting process to allow you to perform preparations for your Rider to dismount. This is also where you can have the Mount reject the dismounting process by returning false. You may want to reject a dismount for various reasons specirfic to your game.
We implement this function by simply returning true. But you can add logic here to determine if the Rider is in a decent state to dismount and even execute a few functions that can put it into a desired state to start dismounting.
The MountOrRider
parameter is always going to be the character attempting to leave the mount in this context.
Returning false for this function will abort the dismounting process and prevent the dismounting from starting. Like mounting, there could be many reasons why you would not want the player to start the dismounting process in your game. It could be that they are in a cutscene or traveling too fast to get off or that the mount has not reached its destination yet.
6.2.3 IsMounted
This function is used to test of your Mount is currently mounted by Riders or in the process of being mounting. If All the PrepareToMount()
functions succeed the IsMounted
flag is set to true even if the Rider is not physically on the mount yet. The Rider has another flag called IsSeated
that can be used with IsMounted
to determine if the Rider is seated on the mount.
To Implement this function, place a get reference to the MountablePawnComponent.
Draw a line from MountablePawnComponent node and search for the function IsMounted()
function.
Return the output of IsMounted()
by connecting it to the return node.
6.2.4 GetCharacterPawn
This function is designed to fetch the Driver Pawn for Mounts, the pawn that represents the player or NPC controlling the mount. Similar to the controller this is useful in many external situations where you maybe unsure if you are using the Mount or the Rider, particularly in UI and Collisions Events. This function being implemented on all three Entities (Controller, Rider, Mount) allows you to get the object you need reguardless of which one of the three you currently have using the same interface call.
To implment this funciton place down a get node for MountablePawnComponent.
Then draw a line from the MountablePawnComponent node and search for GetDriver()
.
return the output of GetDriver()
through the return node.
6.2.5.1 GetCharacterMount
This function is designed to fetch the Mount Actor in situations where you maybe unsure if you have a reference to the Mount or Rider (such as UI and Collision Events). In this situation the return value is always going to be "Self" as this is almost always going to be the Mount. There may be sitautions where you need a bit more logic such as Mounts that can Mount other Mounts. Then you'll need to detect if this is an Acting Mount or Mounted to another Mount.
For characters that are mounts we simply return Self
since it is the mount.
6.2.6 GetCharacterController
This function is designed to fetch the Current Controller of the Rider or Mount. This is useful for sitations where you may need the Rider but have either the Mount or the Controller instead or in the case of Collision Events an Actor you are unsure what it exactly is.
For mounts we always simply return the current Controller that is controlling them. Simply right click and search for GetController()
and return it.
6.3 Implement MountablePawnInterface
Return to Table of Contents
This section covers the implementation of the IMountablePawn Interface.
6.3.1 MustHaveDriver
This function is used to determine if the mount must have a driver fill it's dirver seat first before all other seats. This will force the position mounting system to always put new riders on the driver seat. For implementations where the player can choose a direct seat then it is ignored if the player chooses a seat other than the driver seat.
To implement this function add a get node for MountablePawnComponent.
Draw a line from MountablePawnComponent and search for the function MustHaveDriver()
and select it.
Connect the output of MustHaveDriver()
to the return node.
6.3.1 IsMountableActor
This function returns a flag indicating that the Mount is a mountable object. It is not meant to indicate if the mount is capable of being mounted (such as if it has all its seats, simply that it can server as a mount). This is one method of deactivating the mounting ability of a mount in subclasses.
To implement this function add a get node for the MountablePawnComponent on the graph.
Draw a line from the MountablePawnComponent and search for the function CanMount()
.
Connect the output of CanMount()
to the return node.
6.3.3 Is MountableByPawn
This is one of the functions that can be called from anyway but is best leveraged by the IADMUnifiedControllerPawn::PrepareToMount()
interface function. Here you can add any custom logic for your mount to check if the supplied pawn that wants to mount it is capable of mounting it. For instance if you need a key you can search the supplied pawn's inventory for a key or other key items or check the supplied pawn is the proper owner of the mount.
This function is not called automatically but is supplied as a option for you to use in your system.
In this demonstration we will simply return true to indicate any pawn who can interact with this mount can attempt to mount it.
6.3.4 IsDriverSeat
This function is used as part of the mounting process to determine if a supplied seat data is the same seat data as the driver seat. This is called automatically as part of the mounting process and the dismounting process.
To implement this funciton place down a get node for the MountablePawnComponent.
Draw a line from the MountablePawnComponent and search for the function IsDriverSeat()
.
Connect the input variable named SeatData
to the input variable SeatData
on the IsDriverSeat()
node.
Connect the output of IsDriverSeat()
to the return node.
6.3.5 HasDriver
This function can be used to determine if the mount currently has a driver.
To implement add a get node for the MountablePawnComponent.
Draw a line from the MountablePawnComponent node and search for HasDriver()
.
Connect the output of HasDriver()
to the return node.
6.3.6 GetRelativeMountDirection
This function is mostly used by the Position based mounting to detect the relative direction between the player. The PNMS plugin supplies several functions capable of performing a rather fast calculation to determine where the player is standing relative to the mount. Then you also have the ability to dicate the what valid directions the mount is capable of mounting to. For instance a horse can be mounted form a single seat on the left or right side, but a dune buggy can only be mounted from one side on each seat, and a space ship may be mountable from any direciton when outside of it, or any way you want to setup your system.
For this demonstration we will simply make the the mount return the value AnySide.
AnySide is a special case that tells the Seat Manager it has no preference so long as the seat is available.
6.3.7 GetRelativeDismountDirection
This function is meant to determine what direction character should dismount from. For many vehicles there will be only a single direction for each seat, but for something like a horse you can setup functions to determine what direction the player is looking to get off of the mount. There is a demonstration of this working in the final project.
For demonstration purposes we will simply return the default or first dismounting direction for the seat.
To implement this function draw a line from the input variable Rider
and look for the interface function GetSeatId (Message) which is the Unreal representation of IMountRiderInterface::GetSeatID()
function.
Then place a get node for the MountablePawnComponent on the graph.
Draw a line from MountablePawnComponent and search for GetDefaultPositionForSeatId()
.
Connect the output of GetSeatId()
to the input variable SeatId
on the GetDefaultPositionForSeatId()
function node.
Connect the output of GetDefaultPositionForSeatId()
to the return node.
For a more comprehensive way to set this up using the direction the player is currently looking, see the Finished Project.
6.3.8 GetMountBody
This function is used by the mounting system to get the Mount Body that the player will be attached to. This can be either a Skeletal Mesh or a Static Mesh. This function supplies a SeatID
parameter to allow you to perform special logic based on the seat, such is the case for linked Actors where the seat will need the mesh of the linked actor instead of the main mount body.
To implement this function place a get node for the Mesh component on the graph.
Return the output of the Mesh component.
6.3.9 GetMountablePawnComponent
This function simply allows you to get the MountablePawnComponent from any mount. Allowing you to call functions directly on it from other supporting actors or systems such as collision events or UI elements.
To implement add a get node for the MountablePawnComponent on the graph.
Connect the output of the MountablePawnComponent to the return value.
6.3.10 GetMaxRiders
Get the max number of riders this mount can support, usually this is the same number of seats available to the mount.
To implement this function add a get node for "MountablePawnComponent" to the graph.
From the output of "MountablePawnComponent" draw a line and search for "GetNumSeats" function.
Connect the output of "GetNumSeats" to the return value of the node.
6.3.11 GetDriver
This function is used to get the current driver of the mount.
To implement this function add a get node for MountablePawnComponent to the graph.
Then draw a line from the MountablePawnComponent node and search for the function GetDriver()
.
Connect the output of the GetDriver()
function to the return node.
6.3.12 GetCurrentRiderCount
This funciton is used to get the current number of riders on the mount.
Add a get node for the MountablePawnComponent to the graph.
Draw a line from the MountablePawnComponent and search for the function GetNumRiders()
.
return the output of the GetNumRiders()
node to the return node.
6.3.13 FindAvailableMountingPosition
This function is primarily used when mounting using a relative position instead of a direct mount to a seat. This funciton supplies a current location of the rider as well as their relative direction retrieved the function IMountablePawnInterface::GetRelativeMountDirection()
. The component uses these two values to find a seat with the closest location for the player to attach to and start mounting. If IMountablePawnInterface::MustHaveDriver()
is true and the mount currenlty has no driver than this function will alway return the driver seat position if it is available.
Place a get node for MountablePawnComponent onto the graph.
Draw a line from the MountablePawnComponent node and search for the function FindAvailableMountingPosition()
.
Get the MountablePawnComponent and call FindAvailableMountingPosition()
.
Pass in the Position enum retrieved from the IMountablePawnInterface::GetRelativeMountDirection()
and the RiderLocation parameters.
Then return the outputs.
This function's main purpose is to find the best seat available according to your current distance from the mount and relative direction from the mount. It also performs a distance validation check so you cannot mount too far way from the mount itself.
6.3.14 IsSeatOccupiedById
This function allows you to query if a specific seat is occupied by the seats ID.
To Implement this function we place down a get node for the MountablePawnComponent.
Draw a line from the MountablePawnComponent and search for the funtion IsSeatOccupiedById()
.
Connect the input variable SeatId to the function IsSeatOccupiedById()
input variable called SeatId.
Connect the output of IsSeatOccupiedById()
to the return node.
Get the MountablePawnComponent and call it’s IsSeatOccupiedById()
function. Pass the SeatId parameter into it and return it’s value.
6.3.15 IsSeatOccupiedAtIndex
This function allows you to retrieve a flag indicating that the seat at the specified index is occupied by a rider. This index is the seats index value within the MountablePawnComponent::SeatManager
property
To Implement this function place a get node for MountablePawnComponent.
Draw a line from MountablePawnComponent and search for the function IsSeatOccupiedAtIndex()
.
Connect the function input variable SeatIndex to the IsSeatOccupiedAtIndex()
function node's input parameter SeatIndex.
Connect the output from IsSeatOccupiedAtIndex()
to the return node.
6.3.16 SetSeatOccupiedById
This function allows you to set a specific set as occupied by the unique ID of the seat. Generally you will not need to directly call this function as it is part of the mounting, dismounting, and change seats process.
To implement this function place a get node for the MountablePawnComponent onto the graph.
Draw a line from the MountablePawnComponent and search for the funtion SetSeatOccupiedById()
.
Connect the input parameter SeatId to the input property SeatId of the SetSeatOccupiedById()
function node.
Connect the input parameter Rider to the input property Rider of the SetSeatOccupiedById()
function node.
Connect the output of SetSeatOccupiedById()
to the return node.
6.3.17 SetSeatOccupiedAtIndex
This function allows you to set a specific seat as occupied according to it's index in the MountablePawnComponent::SeatManager
property. Generally you will not need to directly call this function.
To implement this function place a get node for the MountablePawnComponent onto the graph.
Draw a line from the MountablePawnComponent and search for the function SetSeatOccupiedAtIndex()
.
Connect the input parameter SeatIndex to the input property SeatIndex of the SetSeatOccupiedAtIndex()
function node
Connect the input parameter Rider to the input property Rider of the SetSeatOccupiedAtIndex()
function node.
Connect the output of SetSeatOccupiedAtIndex()
to the return node.
6.3.18 ClearSeatById
This function allows you to clear the reference to a rider and mark a seat as unoccupied by using the seats Id. In general there are very few instances where you will need to call this function directly.
To Implement this function place a get node for the MountablePawnComponent on the graph.
Draw a line from the MountablePawnComponent node and search for the function ClearSeatById()
.
Connect the input variable SeatId to the input property SeatId of the ClearSeatById()
function.
Connect the output value of ClearSeatById()
to the return node.
6.3.19 ClearSeatByIndex
This function allows you to clear the reference to a rider and mark a seat as unoccupied by iby the seats index within the MountablePawnComponent::SeatManager
property. In general there are very few instances where you will need to call this function directly.
To Implement this function place a get node for the MountablePawnComponent on the graph.
Draw a line from the MountablePawnComponent node and search for the function ClearSeatById()
.
Connect the input variable SeatId to the input property SeatId of the ClearSeatById()
function.
Connect the output value of ClearSeatById()
to the return node.
6.3.20 GetSeatDataById
Get a copy of a seats data by using it's id. This copy will include current occupying actor as well as the seats mounting and dismounting data and socket attachment. This funciton has two return values, one containing the seat data and the other indicating a successful retrieval of the desired seat data that you can use to test if the seat data was found.
To implement this function place a get node for MountablePawnComponent on the grpah.
Draw a line from MountablePawnComponent and search for the function GetSeatDDataById()
.
Connect the input variable SeatId to the input variable SeatId from the GetSeatDDataById()
function node.
Connect the output variable SeatData to the return node output SeatData.
Connect the boolean output variable ReturnValue to the Return Nodes ReturnValue.
6.3.21 GetSeatDataAtIndex
Get a copy of a seats data by using its index in the seat array of the mount. This copy will include current occupying actor as well as the seats mounting and dismounting data and socket attachment. This funciton has two return values, one containing the seat data and the other indicating a successful retrieval of the desired seat data that you can use to test if the seat data was found.
To implement this function place a get node for MountablePawnComponent on the grpah.
Draw a line from MountablePawnComponent and search for the function GetSeatDDataAtIndex()
.
Connect the input variable Index to the input variable Index from the GetSeatDDataAtIndex()
function node.
Connect the output variable SeatData to the return node output SeatData.
Connect the output variable ReturnValue to the return node output ReturnValue.
6.3.22 CanMountActor
This function primarily servers as a method to determine if the mount is in a mountable state. Here you can add rules to check various proeprties of the mount. The New Rider parameter is supplied to allow you to check if the new rider trying to mount is allowed to mount by calling the function IMountablePawnInterface::IsMountableByPawn()
. This function must return true in order to mounting to proceed successfully.
To implement this function we will reuse a few interface functions we have already setup.
Click anywhere on the graph and search for the function GetMaxRiders()
.
Again click anywhere and search for the funvtion GetCurrentRiderCount()
.
Draw a line from GetMaxRiders()
output and look for the ">" (greater then symbol) and be sure to select the "integer > integer".
Connect the output from GetCurrentRiderCount()
to the second pin of the ">" node.
This initial greater then tests allows us to determine of the mount has available seats.
Again click anywhere and look for the IsMountableActor()
function.
Draw a line from the ">" node and search for "AND" and be sure to choose "AND Boolean"
Connect the output from the IsMountableActor()
function to the second input pin of the "AND" node.
Finally connect the output of the "AND" node to the return value of the function.
6.3.23 CanMountAtPosition
This function can be used to test if the character is able to mount at a specified location and direction.
To implement this function we will reuise a previous interface funciton IMountablePawnInterface::FindAvaialbleMountingPosition()
.
Click on the graph and look for the function FindAvaialbleMountingPosition()
and add it to the graph.
Connect the RiderLocation input parameter to the FindAvaialbleMountingPosition()
functions input parameter RiderLocation.
Connect the DesiredMountingPosition input parameter to the FindAvaialbleMountingPosition()
function input parameter Position.
Connect the ouput node ReturnValue to the return node.
6.3.24 OnRiderFinishedMounting
This function is called on the mount when ever a Rider finishes mounting. This occurs specifically when the RiderComponent calls IMountRiderInterface::PawnFinishedMounting()
. This allows you to add additional logic to to the mount when ever a new rider finishes mounting such as updating the UI or calling a Dispatcher function. When implemented properly this function also sends the signal that a pawn finished mounting to all other riders on the mount allowing them to update UI elements as well.
To implement this function place a get node for the MountablePawnComponent on the graph.
Draw a line from the MountablePawnComponent and search for the function RiderFinishedMounting()
.
Connect the MountedActor input parameter to the RiderFinishedMounting()
functions input parameter Actor
Connect the SeatId input parameter to the RiderFinishedMounting()
functions input parameter SeatId.
On the return node check it's return value to true.
6.3.25 OnRiderFinishedDismounting
This function is called on the mount whenever a Rider finishes dismounting. This occurs specifically when the RiderComponent calls IMountRiderInterface::PawnFinishedDismounting()
. This allows you to add additional logic to to the mount whenever a new rider finishes disounting such as updating the UI or calling a Dispatcher function. When implemented properly this function also sends the signal that a pawn finished disounting to all other riders on the mount allowing them to update UI elements as well.
To implement this function place a get node for the MountablePawnComponent on the graph.
Draw a line from the MountablePawnComponent and search for the function RiderFinishedDismounting()
.
Connect the DismountedActor input parameter to the RiderFinishedDismounting()
functions input parameter DismountedActor
Connect the SeatId input parameter to the RiderFinishedDismounting()
functions input parameter SeatId
.
You can place a get node on the graph for the input parameter SeatId which may make it cleaner to look at the graph.
The next part of this function implementation is to faciliate an issue I have discussed about possessing characters where they can keep moving forward on the client while standing still on the server. To fix this we have the previous AI Controller retake control of the mount if the driver has left.
Draw a line from the MountablePawnComponent and search for the function IsDriverSeatId().
Connect the SeatId input parameter to the IsDriverSeatId()
functions input parameter SeatId
.
Place down a branch node and connect the execution line from RiderFinishedDismounting()
to it.
Connect the output of IsDriverSeatId()
to the Condition input of the Branch node.
The second half of this function we will return control to the AIController
we setup earlier. If the seat we just dismounted from is the driver seat than we want to return control of the mount back over to the AIController
to prevent “funny client side movement”
If we are dealing with a driver seat then check if we are on the authority.
If we are on the authority check if the AIControllerRef variable we setup is valid.
Now we have the AIController
possess the mount.
Return true for all paths.
6.3.26 OnRiderFinishedChangingSeats
This function is called on the mount whenever a Rider finishes changing to a new seat. This occurs specifically when RiderComponent::OnCharacterChangedSeats()
is called. This allows you to add additional logic to to the mount whenever a rider changes seats such as updating the UI or calling a Dispatcher function. When implemented properly this function also sends the signal that a pawn finished changing seats to all other riders on the mount allowing them to update UI elements as well.
To implement this function place down a get node for MountablePawnComponent
Draw a line from MountablePawnComponent and look for the function RiderFinishedChangingSeats()
.
Connect the Rider input parameter to the RiderFinishedChangingSeats()
functions input parameter Rider
Connect the NewSeatId input parameter to the RiderFinishedChangingSeats()
functions input parameter NewSeatId.
Connect the OldSeatId input parameter to the RiderFinishedChangingSeats()
functions input parameter OldSeatId.
The next part of this function implementation is to faciliate an issue I have discussed about possessing characters where they can keep moving forward on the client while standing still on the server. To fix this we have the previous AIController
retake control of the mount if the driver has left.
Draw a line from the MountablePawnComponent and search for the function IsDriverSeatId()
.
Connect the OldSeatId input parameter to the IsDriverSeatId()
functions input parameter SeatId.
Place down a branch node and connect the execution line from RiderFinishedChangingSeats()
to it.
Connect the output of IsDriverSeatId()
to the Condition input of the Branch node.
The second half of this function we will return control to the AIController
we setup earlier. If the seat we just dismounted from is the driver seat than we want to return control of the mount back over to the AIController
to prevent “funny client side movement”
If we are dealing with a driver seat then check if we are on the authority.
If we are on the authority check if the AIControllerRef variable we setup is valid.
Now we have the AIController
possess the mount.
Return true for all paths.
6.3.27 IsDriverReady
This function can be used to determine of the driver is ready to control the mount. The most use of this function is determine if the player can start using input controls for the mount or not. Another use maybe within the mounts animation blueprint in case the mount is meant to have different animations when mounted versus when unmounted.
Call the IMountablePawnInterface::GetDriver()
interface function directly and check it using an IsValid()
macro.
From the IsValid()
execution line call the IMountRiderInterface::IsSeatedOnMount()
Message with the return value of GetDriver()
as it’s target.
Return the value of IMountRiderInterface::IsSeatedOnMount()
.
From the IsNotValue
execution line simply return true, since there is no driver, everything is ready to go.
6.3.28 HasPassengers
This is a utility function to allow you to determine if a mount has passengers, this usually includes the driver.
Place down a get node for the MountablePawnComponent.
Draw a line from the MountablePawnComponent and search for the function GetNumRiders()
.
Draw a line from the GetNumRiders()
function and look for the ">" (Greater then symbol).
Keep the second parameter as zero. If we have more than 0 riders then this mount has passengers.
Connect the output of the ">" node to the return value.
7.0 Setup Mount Seats
Return to Table of Contents
Now it is time to set up the Seats to your mount. The first thing we will do is make sure we have an appropriate socket that will act as the attachment point to our mount.
7.1 Setup Sockets
Open up the mesh that will represent your vehicle or mount. For both Skeletal Meshes or Static Meshes you will want to create a socket.
For the robot horse I have defined two sockets s_DriverSeat and s_PassangerSeat I have configured their rotation and position so that if I attach a character to them they will automatically be facing in the correct location.
The s_DriverSeat is attached to the b_spline_03 bone. It has a Relative location of (X=-0.0,Y=-9.0,Z=13.0)
and a relative rotation of (Pitch=-34.0,Yaw=-90.0,Roll=0.0)
.
The s_PassangerSeat is attached to the b_spine_02 bone. It has a relative location of (X=0.0,Y=2.0,Z=18.0)
and a relative rotation of (Pitch=0.0,Yaw=-90.0,Roll=0.0)
.
Be aware that attempting to use the mannequin Mesh itself to orient your rider will not work for any mount because the rotation of the mannequin is -90 degrees around the Z axis (or Yaw). So while you can set up the socket preview using the mannequin Mesh you’ll need to rotate it -90 Degrees around the Z axis (Yaw) for it to be correct.
Honestly the socket locations and rotations I have setup were all 100% trial and Error. Because the Skeleton is an Asset type in UE4 you can actually change the socket positions and rotations and save the skeleton. Then get off and back on the mount and it will have updated the socket attachment so you can see the changes live without continuously starting and stopping Play in Editor (PIE).
7.2 Setup Seats
With the sockets defined we will now setup the available seats on the mount. Open up the mount you want to define seats for. Go to the Viewport so you can see the mount you are working on and click on the MountablePawnComponent.
Select the MountablePawnComponent under the Component’s window. This should show you the details panel of the component in your editor.
Look for the “Mounting” category and expand the SeatManager
property.
Within the Seat Manager this is a property array called Seats
. Add two seats by pressing the plus sign. We will be designating seat 0 as the driver seat and seat 1 as the passenger seat.
In the seat manager we want to ensure a few values are set for our Mount. The ForceDriverSeat
should be checked. This value enforces that the first seat taken in positional mounting should always be the driver seat. You can disable this if you want the closest seat to the player to take priority.
Then make sure IsPossessableMount
is enabled. This must be set to true if you want your players to be able to drive the mount or vehicle. You can disable this if you just want your players to take a specific seat on the mount.
MaxValidMountingDistanceSquared
represents the maximum distance that this mount can be from the mount for it to be considered valid for attempting to start the mounting process. Remember that this is the Squared distance. So by default it is set to 90000 which is 300 cm or 3 meters from the mount in any direction.
Now ensure that the DriverSeatId
property to 0. You should see the Driver Seat Index update as well.
7.2.1.Setup Driver Seat
Now that we have our two seats for a horse, we will setup the values needed for a seat for the mounting system to work. First we will setup our Driver Seat, because this is a horse, it can hold at most two riders and each "seat" can be mounted from the left side or the right side (at least for this demo). So we'll setup the Driver seat to handle this.
Expand element 0 of the Seats Array. It should have a SeatId
of 0
. Set the SeatSocketName
to s_DriverSeat. The MountingData property is an array that describes how the seat can be attached to. Add Two elements to the Array and Expand it.
Now we can setup the Driver Seat Mounting Points. As stated previously we have two mounting points, one on the left and one on teh right of the mount. These are the locations our character will be moved to during the mounting process and their positions are relative to the Actor origin of the mount.
You can manually set these values or you can use the position editor to select a mounting point and moving it in the editor. In the component list select the MountablePawnComponent and it should bring up all the mounting points you have setup.
For the first mount point set the MountPosition
to LeftSide
. Then set the RelativeMountingOffset
to (X=30, Y=-50, Z=0.0)
. This should translate to 30 units of the mounts forward direction and 50 units to the left side of the mount.
By enabling UseSeparateOffsetforDismounting
you can set a different offset for the position to detach the character to and place the capsule at when dismounting. Like RelativeMountingOffset
the RelativeDismountingOffset
is a relative location from the center of the mount actor. Unfortunatly for now, there is no editor setup for the RelativeDismountingOffset
yet.
On the horse we are allowing mounting to occur on both the right and left side of each seat. So now we need to setup the Right Side mounting too. Expand the index 1 of the Mounting Data. Set the MountPosition
to RightSide
and set the RelativeMountingOffset
to (X=30.0, Y=50.0, Z=0.0)
. This should translate to 30 minutes toward the front of the mount and 50 units to the right of the mount.
Notice the AnySide
value for the options. This is sort of an Omni mounting position, if there are no specific mounting points within a seat with one of the 6 relative directions than the system will attempt to retrieve an “Any” point as a last resort. You can use the “any” point for quick debugging if you don’t care about the relative direction the player is at to move toward a seat.
7.2.2 Setup the Passenger Seat
Next we will setup our Passenger Seat.
The seat at index 1 should have the SeatId
of 1
automatically. Set the SeatSocketName
to s_PassangerSeat.
Now we can setup the Passenger Seat Mounting Points.
As before, we can manually set these values in the Details Panel or you can use the position editor to select a mounting point and moving it in the editor.
[Image: MountPointEditor]
Add two elements to the mounting data and expand them. The first element will be the left side so set the MountPosition
to LeftSide
. Enter (X=-30.0, Y=-50.0, Z=0.0)
for the RelativeMountingOffset
. This should translate to 30 units behind the mount origin, and 50 units to the left of the mount origin.
The Second element should be the right side so set the MountPosition
to RightSide
. Enter (X=-30.0, Y=50.0, Z=0.0)
which should translate to 30 units behind the mount origin and 50 units to the right of the mount origin.
8.0 Setup the Animation System
Return to Table of Contents
The final step we need is to setup our Animation System to support mounting our horse. Since this tutorial is not using any Anim Montages for getting on or off we only need to worry about the actual mounted pose. For a much more robust animation system where each mount has support for different mounting and dismounting animations for each seat as well as different mounted animations depending on the vehicle see the Final Project.
8.1 Event Graph
First thing I am going to do is duplicate the ThirdPerson_AnimBP and call it ABP_MountingCharacter.
Now Open up the APB_MountingCharacter. And double click on the EventGraph.
Create a new Boolean
variable and call it IsSeated.
For the most part we will simply be adding to the event animation graph. From the TryGetPawnOwner()
Drag a line off of it.
Search for the IMountRiderInterface::IsSeatedOnMount()
interface function which will appear as IsSeatedOnMount(Message) in the editor.
Connect the execution line from the last node of the event graph to the IsSeatedOnMount()
function. Then Grab the IsSeated variable and set it to the value returned from the interface function.
That is all we have to do for the Event Graph
8.2 Anim Graph
Next we’ll setup the AnimGraph while this tutorial does not leverage the AnimMontages I thought It would be nice to at least prepare our AnimBP for the use of them.
Next you will need to go to the AnimGraph. I have renamed the State Machine to LocomotionStates. I suggest you do the same as I reference it later on.
Drag a line from the outline of the figure and in the search box select “New Saved Cached Pose…”
Name it Locomotion
Next right click and choose Use cached pose ‘Locomotion’
Now Drag the pose line out and search for the Slot ‘DefaultSlot’ node.
This is the node you will always select in a search regardless of the actual animation slot you are using.
Then Drag the Defaut slot pose line to the OutputPose node.
That is all for setting up the Anim Graph.
8.3 Locomotion States
Now we will setup the Animation States for our Mount. I have a particular method I like to leverage but feel free to setup the Animation States however you see fit.
First thing to do is go into the Locomotion States State Machine graph. And move the Four nodes already in there up, we will keep these for later.
From the “Entry” node draw out the execution line and create a new State called OnGroundStates Then Create a second transition and State called OnMountedStates. Finally create a transition from the OnMountedStates to the OnGroundStates states.
This should have the original 4 states and transitions disconnected from the entry. You can leave them here for now or “Cut” or “Copy” them into your computer’s copy buffer.
Inside the OnGroundStates State right click and create a new State Machine and call it GroundStates. Connect the GroundStates pose pin to the Output Animation Pose node.
First go back to the top level of LocomotionStates and Cut the four disconnected states of “Idle/Run”, “JumpStart”, “JumpLoop”, and “JumpEnd”.
Then Go back to OnGroundStates and into the GroundStates State Machine.
Paste the Four copied States into this state machine.
Now our basic movement state machine should work perfectly fine while we are on the ground.
Now to back to the LocomotionStates and select the OnMountedStates State. We will simply play the A_Mounted state while mounted.
Now our States are finished and we need to setup the transitions.
Double click on the Transition that is pointing from the OnGroundStates toward the OnMountedSeats States. We want our animation system to transition to this state if we are seated on a mount.
Grab the IsSeated variable we created and connect it directly into the CanEnterTransiton
node.
Now go back to the LocomotionStates and double click on the transition that is pointing from the OnMountedSeats toward the OnGroundStates states. We want our animation system to transition back to the default ground movement when our character is no longer seated.
Grab the IsSeated variable we created and then drag it out. Search for NOT
boolean to get the “NOT” node. Then drag that into the CanEnterTransition
node.
Congratulations our Aniation System is setup and ready to go.
8.4 Assign the Animation Graph
Now we need to assign the animation graph.
Go to the BP_MountingCharacter blueprint and open it up.
Click on the Mesh Component.
In the Animation Class
use the drop down and select ABP_MountingCharacter.
9.0 Setup Completed
Return to Table of Contents
And that completes the basic setup for the seats and the entire mounting system. At this point you are finished with the setup. But running your game right now without an interaction system to start the mounting, dismounting, and seat changing procedures will not get you very far. If you already have your own interaction system then you can simply hook up the mounting system into that and be sure to call the appropriate functions.
The next steps will be setting up your interaction system.
If you already have an interaction system in place then you should simply need to know what functions you need to call to start the mounting process.
If you do not have an interaction system then you can follow the tutorial on how to setup a very basic interaction system