Enable MotionController from outside Pawn?

I have 2 classes:

  • AVRPlayer derives from APawn
  • AVRHand derives from AActor

In my blueprint of AVRPlayer I include instances of the VRHands for each player’s hand using ChildActorComponents. I figured having a separate class for VRHands would promote better separation of concerns.

Naturally, I wanted to put the MotionControllerComponent directly into AVRHand, but it turns out this doesn’t work (I do have input enabled on the VRHands).
It does work if I put the motion controller directly into the VRPlayer bp though.
I haven’t had a chance to look at the motion controller component code yet, but Is there a restriction for the motion controllers that they must be direct children of a Pawn?

I was able to get it working by manually setting the VRPlayer as the owning net client actor for the VRHands using SetOwner():

VRPlayer.h (for context)

UPROPERTY(BlueprintReadOnly, Category = "Components")
    UChildActorComponent* LeftHandComponent;

UPROPERTY(BlueprintReadOnly, Category = "Components")
    UChildActorComponent* RightHandComponent;

VRPlayer.cpp

void AVRPlayer::BeginPlay()
{
    Super::BeginPlay();

    LeftHand = Cast<AVRHand>(LeftHandComponent->GetChildActor());
    RightHand = Cast<AVRHand>(RightHandComponent->GetChildActor());

    LeftHand->SetOwner(this);
    RightHand->SetOwner(this);
}

Here’s an explanation of how I came to this solution (I provide links to engine source, you can get access here):

I set a breakpoint in UMotionControllerComponent::TickComponent where the location is updated based on tracking:

const bool bNewTrackedState = PollControllerState(Position, Orientation, WorldToMeters);
if (bNewTrackedState)
{
	SetRelativeLocationAndRotation(Position, Orientation);
}

The breakpoint told me that PollControllerState() was returning false. So I stepped into that function to see why.

The bulk of the polling function is guarded by a check to see if the client has net authority:

bHasAuthority = MyOwner->HasLocalNetOwner();

bHasAuthority was evaluating false, so I stepped into HasLocalNetOwner() to see why as well.

This function returns false if the owning actor is neither a Pawn nor Controller (my AVRHand class is neither of those because it only derives from AActor):

// Top owner will normally be a Pawn or a Controller
if (const APawn* Pawn = Cast<APawn>(TopOwner))
{
	return Pawn->IsLocallyControlled();
}

const AController* Controller = Cast<AController>(TopOwner);
return Controller && Controller->IsLocalController();

Fortunately this function has logic to traverse ownership upwards, so by using SetOwner() to make VRPlayer the owner (which is a Pawn), we can tell the motion controller that we do have net authority and to update.

1 Like

I wasn’t sure but I was assuming it was sort of connected to pawn class. I was able to change the ownership directly in blueprint with SetOwner (target is an actor, mine was containing Left and Right hands.)

My motion controller custom actor will be not affiliated with my pawn movement anymore.

Thanks a lot !