Here’s how i play the AnimMontage:
void UPhosphorCharMvtComponent::TickComponent(float DeltaTime,enum ELevelTick TickType,FActorComponentTickFunction *ThisTickFunction)
{
Super::TickComponent(DeltaTime,TickType,ThisTickFunction);
ACharacter* character = GetCharacterOwner();
if (character == nullptr)
return;
if (WantedStance != Stance)
{
if (character->GetCurrentMontage() != TransitionMontage) //done playing
{
Stance = WantedStance;
TransitionMontage = nullptr;
UpdateMovementSpeeds();
}
return;
}
ECharStance newStance = ECharStance::Stand;
if (MovementMode == EMovementMode::MOVE_Walking)
{
if (IsCrouching())
newStance = ECharStance::Sneak;
}
else if (MovementMode == EMovementMode::MOVE_Custom)
{
newStance = static_cast<ECharStance>(CustomMovementMode);
}
if (newStance != Stance)
{
WantedStance = newStance;
if (Stance == ECharStance::Stand && newStance == ECharStance::Sneak)
TransitionMontage = FindAnimMontageAsset("Stand-Sneak");
else if (Stance == ECharStance::Sneak && newStance == ECharStance::Stand)
TransitionMontage = FindAnimMontageAsset("Sneak-Stand");
else
TransitionMontage = nullptr;
if (TransitionMontage != nullptr)
{
//if (CharacterOwner->Role >= ROLE_AutonomousProxy)
character->PlayAnimMontage(TransitionMontage);
}
else //just transition immediately
{
Stance = WantedStance;
UpdateMovementSpeeds();
}
}
}
Stance,WantedStance,TransitionMontage arent replicated or anything, they’re just member vars.
This is what i get on the server (watching a client move):
This is what i get on the client (watching the server, or another client, move):
And if I uncomment that if (CharacterOwner->Role >= ROLE_AutonomousProxy) check so that AnimMontages dont play on simulated proxies, i get: When Not Playing RootMotion On SimulatedClients, Uncrouching Does Not Happen GIF... | Gfycat
When watching the videos, look at the GREEN debug output, that’s what i added to Character.cpp and CharacterMovementComponent.cpp in the functions specified to debug the issue. There’s an extra UnCrouch(false) call in the faulty case.
I think this AnimMontage business is happening because of CharacterMovementComponent rewinding to before the AnimMontage is played (after its done playing), at which point bWantsToCrouch is false in the rewind PerformMovement and it immediately uncrouches him.
This is further confirmed by, when i was debugging the issue, i set breakpoints to where bIsCrouched is set inside CharacterMovementComponent, as well as OnRep_IsCrouched in Character, and most of the time, the issue didnt happen, probably because the DeltaTime after i resumed the program just skipped over the AnimMontage completely.
Engine 4.6-preview built from source github commit f34ec74177b9cd4628611e7d2318e6fb1c2fa84a
EDIT: A hacky way of ‘fixing’ this was to change ACharacter::OnRep_IsCrouched to this:
void ACharacter::OnRep_IsCrouched()
{
if (CharacterMovement)
{
if (bIsCrouched)
{
CharacterMovement->bWantsToCrouch = true; //added this
CharacterMovement->Crouch(true);
}
else
{
CharacterMovement->bWantsToCrouch = false; //added this
CharacterMovement->UnCrouch(true);
}
}
}
No clue what havok that might wreck with regards to latency tolerant move replaying and such, i only tested in PIE windows.