How to replicate aiming in a 3D Sidescroller in 4-27 or 5-0?

tl;dr - How does one replicate an aim offsets in a 3D side-scroller with a dedicated server?

I am currently working on a 3D side-scroller (constrained to X plane) but I am encountering difficulty with the aim offset. I am just pulling the ControllerRotation Pitch value and using it directly. If I enable ‘Use Controller Rotation Pitch’, it obviously rotates the pawn too, but the aiming is spot-on. This suggests the Pitch value is available on a network level already.

It feels like the correct thing to do is have the client send either the Pitch float or the calculated direction vector (from AimComponent to Muzzle) to the server, then work a tiny bit of magic to solve for the missing piece and broadcast…though I am unsure how to take that property and update the animation on the remote clients (read: PlayerA looks directly into the sun, how does PlayerB see PlayerA looking up?). However, it’s feeling like I am overcomplicating this and that this is likely a stupidly simple process but I’ve missed the obvious answer.

Here is the Animation Blueprint’s Event Graph, specifically Event BlueprintUpdateAnimation.

Here is the same Animation Blueprint’s Animation Graph, relatively basic.

Lastly, here is the character and the locations of the AimComponent (back of the barrel) and the Muzzle (ouchy-causing end of the barrel).

Disregard, I’m a goober. Will post all relevant bits in the morning (EST) for others’ benefit.


(Forgot to link crazy helpful video by Alex Forsythe → " Multiplayer in Unreal Engine: How to Understand Network Replication")

In a nutshell, I overlooked that I never actually USED the replicated AimPitch float. The changes that fixed this were:

  1. Adding the Server RPC and then calling it. (code below)
  2. Updating the Animation Blueprint’s Event Graph to pull that value from the BaseCharacter class (image at bottom)
/// BaseCharacter.h

UPROPERTY(BlueprintReadWrite, EditAnywhere, Replicated, Transient, Category = "Projectile")
float AimPitch = 0.0f;

UFUNCTION(Server, Unreliable)
void Server_SetAimPitch(const float& InPitch);
/// BaseCharacter.cpp

// omitting the other baseline binds, just pointing out the mouse bind
void ABaseCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    // i am using the mouse movement as one would normally do so for a first person shooter, then pulling the Pitch value alone
    PlayerInputComponent->BindAxis("MouseLookUp", this, &ABaseCharacter::LookUpAtRate);
}

// this is automatically created in the third person example project, I've only added the Server RPC call to it
void ABaseCharacter::LookUpAtRate(float Rate)
{
    AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
    Server_SetAimPitch(FMath::ClampAngle(GetControlRotation().Pitch, MinAimPitch, MaxAimPitch)); // <-- pulling the Controller Rotation's Pitch and using the Server RPC
}

void ABaseCharacter::Server_SetAimPitch_Implementation(const float& InPitch)
{
    AimPitch = InPitch;
}