Hi Matej,
Ok, here’s what I found out:
Function UNavMoverComponent::GetFeetLocationBased() gets its “MovementBaseInfo” data from key “CommonBlackboard::LastFoundDynamicMovementBase” on its simulation blackboard (member TObjectPtr<UMoverBlackboard> SimBlackboard). This blackboard key is only set by WalkingMode and FallingMode (not by NavWalkingMode), and only if the controlled actor is based on a floor marked as Movable (not Static). Here’s the relevant call chain:
UWalkingMode::SimulationTick_Implementation()
UFloorQueryUtils::FindFloor()
UWalkingMode::CaptureFinalState()
UWalkingMode::UpdateFloorAndBaseInfo()
FRelativeBaseInfo::SetFromFloorResult()
On the call chain above, UFloorQueryUtils::FindFloor() looks for the floor below the controlled actor by sweeping a collision shape downward (using UWorld::SweepSingleByChannel()). Its results are passed on to FRelativeBaseInfo::SetFromFloorResult(), which fills out the “ContactLocalPosition” member by converting the floor result’s “HitResult.ImpactPoint” from world space to a space relative to “HitResult.Component” (using UBasedMovementUtils::TransformWorldLocationToBased()).
Now, when UNavMoverComponent::GetFeetLocationBased() attempts to convert an FRelativeBaseInfo to an FBasedPosition, it seems to be mixing things up. It sets CachedBaseLocation (which should be the world space position of the base actor) to ContactLocalPosition (the controlled actor’s position relative to a component in the base actor). It also sets Position (which should be the controlled actor’s position relative to the base actor) to “Location” (the world space position of a component in the base actor). As a result, FBasedPosition’s operator* will recalculate the world-space position of the controlled actor, and will do it using the wrong base location. The most useful information, ContactLocalPosition, is lost, resulting in the behavior you described well inside UPathFollowingComponent.
Here’s how I believe UNavMoverComponent::GetFeetLocationBased() should have been implemented:
FBasedPosition UNavMoverComponent::GetFeetLocationBased() const
{
FBasedPosition BasedPosition(NULL, GetFeetLocation());
if (MoverComponent.IsValid())
{
if (const UMoverBlackboard* Blackboard = MoverComponent->GetSimBlackboard())
{
FRelativeBaseInfo MovementBaseInfo;
if (Blackboard->TryGet(CommonBlackboard::LastFoundDynamicMovementBase, MovementBaseInfo))
{
AActor* Base = MovementBaseInfo.MovementBase->GetOwner();
FVector WorldPosition;
UBasedMovementUtils::TransformBasedLocationToWorld(
MovementBaseInfo.MovementBase.Get(),
MovementBaseInfo.BoneName,
MovementBaseInfo.ContactLocalPosition,
WorldPosition);
BasedPosition.Set(Base, WorldPosition);
}
}
}
return BasedPosition;
}
In the implementation above, I transformed MovementBaseInfo.ContactLocalPosition from its base-component-relative space back to world space, and allowed FBasedPosition::Set() to re-convert that world space position to base-actor-relative space, as well as update all internal cached variables. This not only “un-swaps” Position and CachedBaseLocation, but it also converts the base from a primitive component (as used in FRelativeBaseInfo) to its owning actor (as used in FBasedPosition). This back-and-forth sequence of conversions does not feel very polished, but it seems to resolve the issue in my tests, so please let me know if it also works for you.
I will now file an internal bug report with all this information for the devs responsible for the Mover plugin, and then provide you a tracking number for it. Note that, since this is still an experimental plugin, it is possible that it still undergoes significant changes instead of merely getting this fixed.
Best regards,
Vitor