bool ACharacter::CanUseRootMotionRepMove(const FSimulatedRootMotionReplicatedMove& RootMotionRepMove, const FAnimMontageInstance& ClientMontageInstance) const { // Ignore outdated moves. if( GetWorld()->TimeSince(RootMotionRepMove.Time) <= 0.5f ) { // Make sure montage being played matched between client and server. if( RootMotionRepMove.RootMotion.AnimMontage && (RootMotionRepMove.RootMotion.AnimMontage == ClientMontageInstance.Montage) ) { UAnimMontage * AnimMontage = ClientMontageInstance.Montage; const float ServerPosition = RootMotionRepMove.RootMotion.Position; const float ClientPosition = ClientMontageInstance.GetPosition(); const float DeltaPosition = (ClientPosition - ServerPosition); const int32 CurrentSectionIndex = AnimMontage->GetSectionIndexFromPosition(ClientPosition); if( CurrentSectionIndex != INDEX_NONE ) { const int32 NextSectionIndex = (CurrentSectionIndex < ClientMontageInstance.NextSections.Num()) ? ClientMontageInstance.NextSections[CurrentSectionIndex] : INDEX_NONE; // We can only extract root motion if we are within the same section. // It's not trivial to jump through sections in a deterministic manner, but that is luckily not frequent. const bool bSameSections = (AnimMontage->GetSectionIndexFromPosition(ServerPosition) == CurrentSectionIndex); // if we are looping and just wrapped over, skip. That's also not easy to handle and not frequent. const bool bHasLooped = (NextSectionIndex == CurrentSectionIndex) && (FMath::Abs(DeltaPosition) > (AnimMontage->GetSectionLength(CurrentSectionIndex) / 2.f)); // Can only simulate forward in time, so we need to make sure server move is not ahead of the client. const bool bServerAheadOfClient = ((DeltaPosition * ClientMontageInstance.GetPlayRate()) < 0.f); UE_LOG(LogRootMotion, Log, TEXT("\t\tACharacter::CanUseRootMotionRepMove ServerPosition: %.3f, ClientPosition: %.3f, DeltaPosition: %.3f, bSameSections: %d, bHasLooped: %d, bServerAheadOfClient: %d"), ServerPosition, ClientPosition, DeltaPosition, bSameSections, bHasLooped, bServerAheadOfClient); return bSameSections && !bHasLooped && !bServerAheadOfClient; } } } return false; }
void UAnimSingleNodeInstance::Montage_Advance(float DeltaTime) { Super::Montage_Advance(DeltaTime); FAnimMontageInstance * CurMontageInstance = GetActiveMontageInstance(); if ( CurMontageInstance ) { CurrentTime = CurMontageInstance->GetPosition(); } }
void UAnimSingleNodeInstance::NativeUpdateAnimation(float DeltaTimeX) { float NewPlayRate = PlayRate; UAnimSequence* PreviewBasePose = NULL; if (bPlaying == false) { // we still have to tick animation when bPlaying is false because NewPlayRate = 0.f; } if(CurrentAsset != NULL) { FAnimGroupInstance* SyncGroup; if (UBlendSpaceBase* BlendSpace = Cast<UBlendSpaceBase>(CurrentAsset)) { FAnimTickRecord& TickRecord = CreateUninitializedTickRecord(INDEX_NONE, /*out*/ SyncGroup); MakeBlendSpaceTickRecord(TickRecord, BlendSpace, BlendSpaceInput, BlendSampleData, BlendFilter, bLooping, NewPlayRate, 1.f, /*inout*/ CurrentTime); #if WITH_EDITORONLY_DATA PreviewBasePose = BlendSpace->PreviewBasePose; #endif } else if (UAnimSequence* Sequence = Cast<UAnimSequence>(CurrentAsset)) { FAnimTickRecord& TickRecord = CreateUninitializedTickRecord(INDEX_NONE, /*out*/ SyncGroup); MakeSequenceTickRecord(TickRecord, Sequence, bLooping, NewPlayRate, 1.f, /*inout*/ CurrentTime); // if it's not looping, just set play to be false when reached to end if (!bLooping) { if ((NewPlayRate < 0.f && CurrentTime <= 0.f) || (NewPlayRate > 0.f && CurrentTime >= Sequence->SequenceLength)) { SetPlaying(false); } } } else if(UAnimComposite* Composite = Cast<UAnimComposite>(CurrentAsset)) { FAnimTickRecord& TickRecord = CreateUninitializedTickRecord(INDEX_NONE, /*out*/ SyncGroup); MakeSequenceTickRecord(TickRecord, Composite, bLooping, NewPlayRate, 1.f, /*inout*/ CurrentTime); // if it's not looping, just set play to be false when reached to end if (!bLooping) { if ((NewPlayRate < 0.f && CurrentTime <= 0.f) || (NewPlayRate > 0.f && CurrentTime >= Composite->SequenceLength)) { SetPlaying(false); } } } else if (UAnimMontage * Montage = Cast<UAnimMontage>(CurrentAsset)) { // Full weight , if you don't have slot track, you won't be able to see animation playing if ( Montage->SlotAnimTracks.Num() > 0 ) { UpdateSlotNodeWeight(Montage->SlotAnimTracks[0].SlotName, 1.f); } // get the montage position // @todo anim: temporarily just choose first slot and show the location FAnimMontageInstance * ActiveMontageInstance = GetActiveMontageInstance(); if (ActiveMontageInstance) { CurrentTime = ActiveMontageInstance->GetPosition(); } else if (bPlaying) { SetPlaying(false); } #if WITH_EDITORONLY_DATA PreviewBasePose = Montage->PreviewBasePose; #endif } } else if(CurrentVertexAnim != NULL) { float MoveDelta = DeltaTimeX * NewPlayRate; FAnimationRuntime::AdvanceTime(bLooping, MoveDelta, CurrentTime, CurrentVertexAnim->GetAnimLength()); } #if WITH_EDITORONLY_DATA if(PreviewBasePose) { float MoveDelta = DeltaTimeX * NewPlayRate; const bool bIsPreviewPoseLooping = true; FAnimationRuntime::AdvanceTime(bIsPreviewPoseLooping, MoveDelta, PreviewPoseCurrentTime, PreviewBasePose->SequenceLength); } #endif }