How to replicate character position in ragdoll mode?

I’m trying to have the character who goes ragdoll when killed, then can be revived by another player. Everything works fine, but sometimes different players see the character in different places after he went ragdoll. The problem is that the physics simulation ends up with different location of the mesh on different clients. I don’t want to replicate the exact pose of the mesh, but at least have the root bone in the same place, so when the character is revived, he appears in the same place for everybody.

I’ve also noticed that - as also mentioned in another post - during the ragdoll the CapsuleComponent is detached from the Mesh and I’ve to attach it back manually.

Here my code:


void AMyCharacterPlayer::OnDeath(float KillingDamage, struct FDamageEvent const& DamageEvent, class APawn* PawnInstigator, class AActor* DamageCauser)
{
	if (bIsDying)
	{
		return;
	}

	bIsDying = true;
        APlayerController* PC = Cast<APlayerController>(Controller);

	if (Role == ROLE_Authority)
	{
		if (PC)
		{
			PC->SetIgnoreMoveInput(true);
		}
		// disable collisions on capsule
		ReviveCollision = CapsuleComponent->GetCollisionEnabled();
		ReviveCollisionResponse = CapsuleComponent->GetCollisionResponseToChannels();
		CapsuleComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
		CapsuleComponent->SetCollisionResponseToAllChannels(ECR_Ignore);

		CharacterMovement->StopMovementImmediately();
		CharacterMovement->SetMovementMode(EMovementMode::MOVE_None);
		RepDeath();
	}
}

void AMyCharacterPlayer::RepDeath_Implementation()
{
	ReviveActorCollision = GetActorEnableCollision();
	SetActorEnableCollision(true);

	if (Mesh)
	{

	  ReviveCollisionProfileName = Mesh->GetCollisionProfileName();
	  static FName CollisionProfileName(TEXT("Ragdoll"));
	  Mesh->SetCollisionProfileName(CollisionProfileName);
	  ReviveTransform = Mesh->GetRelativeTransform();

		
          Mesh->SetAllBodiesBelowSimulatePhysics(Mesh->GetBoneName(1), true);
	  Mesh->SetSimulatePhysics(true);
	
	  Mesh->SetEnablePhysicsBlending(true);
	  Mesh->WakeAllRigidBodies();
		
	
	}
}

void AMyCharacterPlayer::Revive()
{
	if (!bIsDying)
	{
		return;
	}

	bIsDying = false;

	APlayerController* PC = Cast<APlayerController>(Controller);
	
	if (PC)
	{
		PC->SetIgnoreMoveInput(false);
	}

	

	CapsuleComponent->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
	CapsuleComponent->SetCollisionProfileName(TEXT("Pawn"));

	CharacterMovement->SetMovementMode(EMovementMode::MOVE_Falling);

	
	SetActorLocation(Mesh->GetSocketLocation(Mesh->GetBoneName(0)) + FVector(0.0f, 0.0f, CapsuleComponent->GetScaledCapsuleHalfHeight()));
	RepRevive();
}

void AMyCharacterPlayer::RepRevive_Implementation()
{
	if (Mesh)
	{
		Mesh->SetSimulatePhysics(false);
		Mesh->PutRigidBodyToSleep();
		Mesh->AttachTo(CapsuleComponent);
		Mesh->SetRelativeTransform(ReviveTransform);
	}
}

I have the same problem. I noticed that when you use SkeletalMeshActor with physics and replication enabled it works correctly. I believe it works because the SkeletalMesh component is the root component of the SkeletalMeshActor. In the case of Character the root component is the Capsule, so the location of the Mesh is not correctly replicated. I tried to replicate the relative location of the Mesh by hand on every tick but for some reason it didn’t work, i don’t know why. This is my failed attempt to sync ragdolls location from server to clients by hand, as a Blueprint:

What I notice here is that Mesh’s relative location is already marked as replicated, so in theory it already should work correctly, but …

Did anyone find a solution to this?

Not entirely certain you’re dealing with the same issue I was having with this. My solution was to simply store the attachment hierarchy AND the relative transforms before enabling ragdoll (SetAllBodiesSimulatePhysics, SetSimulatePhysics, WakeAllRigidBodies). So just iterate over all SceneComponents attached to the Character/Pawn using TInlineComponentArray or some other means. Then for each of these SceneComponents store the AttachParent, AttachSocketName, the SceneComponent itself and finally SceneComponent->GetRelativeTransform() for example in an array.

Then when the Character/Pawn is revived you reverse the process (PutAllRigidBodiesToSleep, SetSimulatePhysics, SetAllBodiesSimulatePhysics) and finally you reapply the stored attachment hierarchy and relative transforms. So again you just iterate over the array of stored data and call StoredComponent->AttachTo(StoredAttachParent, StoredAttachSocketName); after that you just need to restore the relative transform StoredComponent->SetRelativeTransform(StoredRelativeTransform, false, nullptr, ETeleportType::None);

Of course this is just a simple setup and you probably want to play some sort of animation or blend between physics and a different pose.