Skip to main content

Linked mounts

A truck and its trailer. A tank and its turret. A motorcycle and its sidecar. Two actors, two mountable components, one vehicle — and a set of questions that only make sense about the vehicle: who is driving it, who is aboard it, get everybody out of it.

Linking is how you say that. It is four lines of setup and it changes nothing about how a mount is found or mounted: the trailer's door is still the trailer's door, and a rider walking up to it still gets the trailer's seat. What it adds is the whole-vehicle view.

Coupling

// On the server. The child points at the parent.
UEDMountableComponent* TrailerMount = Trailer->FindComponentByClass<UEDMountableComponent>();
TrailerMount->LinkToMount(Truck);

// ...and later
TrailerMount->UnlinkFromMount();

LinkToMount returns false if the parent is not a mount, or if the link would make a cycle. The cycle check is not paranoia: a loop would make GetLinkedRoot walk until its depth cap and quietly give the wrong answer to every query below, which is the kind of bug that only appears the first time somebody builds a road train.

LinkedParent is also editable in the details panel, for a vehicle whose parts are placed together in the level and never come apart.

Asking about the vehicle

UEDMountableComponent* Root = TrailerMount->GetLinkedRoot();   // the truck; itself when unlinked

TArray<UEDMountableComponent*> Set;
TrailerMount->GetLinkedMountables(Set, /*bIncludeSelf*/ true); // root first, then the rest

bool bSameVehicle = TrailerMount->IsLinkedTo(Truck); // true from either end

APawn* Driver = TrailerMount->GetLinkedDriver(); // whoever is steering the whole thing
TArray<APawn*> Aboard;
TrailerMount->GetLinkedRiders(Aboard);

TrailerMount->EjectAllRidersInLinkedSet(); // authority only

The link is stored in one direction, child to parent, because a link stored at both ends is a link that can disagree with itself. The children are found by asking the subsystem which registered mounts share a root — the registry is the one list that is always current, so there is no second structure to keep in step and nothing to repair when an actor is destroyed.

OnLinkChanged fires on every machine when a mount is coupled or uncoupled, which is where a game re-weighs the vehicle or redraws its HUD.

Getting in "the truck"

By default a request that names an actor puts you in that actor. Set bAllowLinkedMounts and it will fall through to the rest of the set when the named mount has no seat for you:

FEDMountRequest Request;
Request.MountActor = Truck;
Request.bAllowLinkedMounts = true; // the truck, or its trailer, whichever has room
Rider->RequestMountWithOptions(Request);

The fallback is resolved once, in RequestMountWithOptions, and the resolved request is what gets predicted locally and sent to the server. Resolving it separately on each machine would be two answers to "which trailer has room", and a client would predict its way into a seat the server had already given to somebody else.

The named mount is always tried first, and a failure still names the actor the caller asked about — so "the truck is full" does not become "the trailer is full" in your UI.

It is not attachment. Linking does not parent one actor to the other, move anything, or make the trailer follow the truck; do that with the engine's attachment or a physics constraint, whichever your vehicle needs. A link is a statement about ownership of riders, nothing else, which is why coupling and uncoupling are cheap and safe to do at runtime while people are aboard.

It is also not a driving relationship. The trailer receives no driver input because it has no driver's seat; if you give it one, it gets its own driver and GetLinkedDriver returns whichever it finds first, root outwards. A vehicle with two drivers is a design decision, not something the framework will make for you.