Migration
This page is written against the family of mounting systems built around three components — one on the rider, one on the mount, one on the controller — with seats described as arrays of relative offsets. If yours is shaped differently, the concept table is still the useful part.
Concepts
| There | Here | Note |
|---|---|---|
| Mount rider component (on the pawn) | UEDRiderComponent | Same place, much smaller. |
| Mountable actor component | UEDMountableComponent | Same place. |
| Rider controller component | (usually none) | See The third component. |
| A seat struct in an array on the mountable | UEDMountPointComponent, one per seat | A scene component you drag. |
| A side plus an offset | FEDEntryPoint — a transform, a tag and a clearance capsule | Validated against the world. |
| Seat index / seat id / seat name | FEDSeatHandle | One way, not three. |
| A relative-direction enum | Mount.Entry.* gameplay tags | Extensible; games can add sides. |
| An action-state enum plus six booleans | EEDMountPhase, one enum | Cannot contradict itself. |
bool return plus an out-parameter response struct | FEDMountResult with a named failure | Every refusal names itself. |
| Five AnimNotify classes | One notify with a tag | Plus a windowed variant for doors. |
| (nothing) | UEDMountAnimationSet | Which animation plays is data, not code. |
| (nothing) | Obstruction checks on entry and exit | The framework will not put you in a wall. |
| (nothing) | The procedural transition | It works before you have animations. |
Functions
| There | Here |
|---|---|
MountPawn(Actor, LinkedActor, Response) | RequestMount(Actor) |
MountPawnToSeat(Actor, LinkedActor, SeatId, Response) | RequestMountWithOptions({Actor, DesiredSeat}) |
DismountPawn(Actor, Response) | RequestDismount() |
ChangeSeatToIndex / ChangeSeatById | RequestChangeSeat(Handle) |
IsMounted / IsSeated / IsAttachedToMount | GetMountPhase(), or IsMounted() / IsSeated() |
IsMounting / IsDismounting / IsChangingSeats | IsTransitioning(), or compare the phase |
GetCurrentMount() | GetMountActor() |
GetSeatId() | GetSeatHandle() |
GetRiderBySeatId / AtIndex / ByName | GetOccupant(Handle) |
SetSeatOccupiedById / AtIndex / ByName | RequestMount — see the note below |
FindAvailableMountingPosition(...) | FindBestSeatFor(Rider, Request, OutFailure) |
HasAvailableSeats() | HasFreeSeat() |
GetNumSeats() / GetNumRiders() | GetNumSeats() / GetNumOccupiedSeats() |
PawnFinishedMounting() and friends, from notifies | HandleMountEvent(Mount.Event.Attach) and friends |
OccupySeat, ReserveSeat and ReleaseSeat exist and are public C++, but they are how the rider
component talks to the mountable — not an API for gameplay. Where the old system expected you to call
SetSeatOccupied yourself, here you call RequestMount and the framework does the bookkeeping,
including the reservation that stops two players taking one seat.
The third component
The old shape needs a component on the controller because when the controller possesses the vehicle, the character's component is no longer on the possessed pawn, so something has to remember the way back.
Here that is a per-seat setting. Keep Possessing the Rider — the default — never possesses the mount
at all: the controller stays on the character and driving input is forwarded. There is nothing to
remember. Possess the Mount gives you the old behaviour, and the rider component itself stores the
controller, so the way back is one object rather than three.
Setting up a seat, before and after
Before: add a seat struct to the array on the mountable; type a socket name; add one mounting-data
entry per side; type an FVector offset for each; run the game to see where they went.
After: add a seat component; drag it. Press Generate Entry Points; drag those. The capsules are green when a rider fits and red when they do not, live, while you drag.
Things this framework deliberately does not do
- No "linked mountable actor" concept. The old system carries a second actor through every call to support a mount whose seats live on a different actor. Here a seat is a component, and a component can be parented to anything — so the case is handled by attachment rather than by a parallel pointer in nineteen signatures.
- No custom-status byte on the result. Failures are a typed enum with localised text. A game that
needs its own reason uses
CanAcceptRiderand its own state. - No root-motion validation toggle. Root motion is the animation's business; the attach moment is a notify, and the watchdog covers a montage that never reaches it.
- No mounting AI.
FindBestEntryPointwill tell you where to stand. Walking there is your movement system's job.
A practical order
- Replace the seat arrays with seat components. This is the bulk of the work and it is mostly
dragging. Do one vehicle, look at it with
ED.Mounting.DrawSeats 1, then do the rest. - Replace the mount/dismount calls with the
Request*equivalents, and handleFEDMountResultwhere you previously ignored abool. - Replace index/id/name lookups with one
FindSeatByNameat setup, held as a handle. - Delete your notify classes and put ED Mount Event on the same frames.
- Build an animation set from whatever branching your old code did to pick animations.
- Decide per seat between
Keep Possessing the RiderandPossess the Mount, and delete the controller component if every seat says the former.