void UFaceFXMatineeControl::UpdateTrack( float NewPosition, UInterpTrackInst* TrackInst, bool bJump ) { check(TrackInst); AActor* Actor = TrackInst->GetGroupActor(); if(!Actor) { return; } UFaceFXComponent* FaceFXComp = Actor->FindComponentByClass<UFaceFXComponent>(); if(!FaceFXComp) { return; } UFaceFXMatineeControlInst* TrackInstFaceFX = CastChecked<UFaceFXMatineeControlInst>(TrackInst); if(Keys.Num() == 0) { FaceFXComp->Stop(nullptr, this); } else if(NewPosition <= TrackInstFaceFX->LastUpdatePosition || bJump) { TArray<TPair<int32, const FFaceFXTrackKey*>> TrackKeyPairs; TArray<FFaceFXSkelMeshComponentId> SkelMeshNoTracks; GetTrackKeyForTime(NewPosition, TrackKeyPairs, &SkelMeshNoTracks); for(const TPair<int32, const FFaceFXTrackKey*>& TrackKeyPair : TrackKeyPairs) { const FFaceFXTrackKey* TrackKey = TrackKeyPair.Value; const float NewFaceFXAnimLocation = NewPosition - TrackKey->Time; check(NewFaceFXAnimLocation >= 0.F) USkeletalMeshComponent* SkelMeshTarget = FaceFXComp->GetSkelMeshTarget(TrackKey->SkelMeshComponentId); //Always stop when we switch track keys to prevent playing animations when switching backward after the end of another track animation //Thats because we don't check here how long the animation actually plays and rely on the JumpTo/Play functionality alone to determine that if(TrackKeyPair.Key != TrackInstFaceFX->GetCurrentTrackIndex(SkelMeshTarget)) { FaceFXComp->Stop(SkelMeshTarget); } TrackInstFaceFX->SetCurrentTrackIndex(SkelMeshTarget, TrackKeyPair.Key); bool JumpSucceeded = false; //playing backwards or jumping if(TrackKey->AnimationId.IsValid()) { //play by animation id JumpSucceeded = FaceFXComp->JumpToById(NewFaceFXAnimLocation, bJump, TrackKey->AnimationId.Group, TrackKey->AnimationId.Name, TrackKey->bLoop, SkelMeshTarget, this); } else if(UFaceFXAnim* FaceFXAnim = GetAnimation(*TrackKey, this)) { //play by animation JumpSucceeded = FaceFXComp->JumpTo(NewFaceFXAnimLocation, bJump, FaceFXAnim, TrackKey->bLoop, SkelMeshTarget, this); } if(!JumpSucceeded) { //jump to failed -> i.e. out of range on non looping animation FaceFXComp->Stop(SkelMeshTarget); } } for(const FFaceFXSkelMeshComponentId& SkelMashNoTrack : SkelMeshNoTracks) { //no track key at this position for this skelmesh component -> stop FaceFXComp->Stop(FaceFXComp->GetSkelMeshTarget(SkelMashNoTrack)); } } else { TArray<TPair<int32, const FFaceFXTrackKey*>> TrackKeyPairs; GetTrackKeyForTime(NewPosition, TrackKeyPairs); for(const TPair<int32, const FFaceFXTrackKey*>& TrackKeyPair : TrackKeyPairs) { const FFaceFXTrackKey* TrackKey = TrackKeyPair.Value; USkeletalMeshComponent* SkelMeshTarget = FaceFXComp->GetSkelMeshTarget(TrackKey->SkelMeshComponentId); const bool IsPaused = FaceFXComp->IsPaused(SkelMeshTarget, this); const bool IsPlayingOrPaused = IsPaused || FaceFXComp->IsPlaying(SkelMeshTarget, this); if(!IsPlayingOrPaused || TrackKeyPair.Key != TrackInstFaceFX->GetCurrentTrackIndex(SkelMeshTarget)) { //start playback of a new animation const float PlaybackStartPosition = NewPosition - TrackKey->Time; checkf(PlaybackStartPosition >= 0.F, TEXT("Invalid animation start location")); bool StartSucceeded = false; //play by animation id if(TrackKey->AnimationId.IsValid()) { //play by animation id StartSucceeded = FaceFXComp->JumpToById(PlaybackStartPosition, false, TrackKey->AnimationId.Group, TrackKey->AnimationId.Name, TrackKey->bLoop, SkelMeshTarget, this); } else if(UFaceFXAnim* FaceFXAnim = GetAnimation(*TrackKey, this)) { //play by animation //StartSucceeded = FaceFXComp->Play(FaceFXAnim, nullptr, TrackKey->bLoop, this); StartSucceeded = FaceFXComp->JumpTo(PlaybackStartPosition, false, FaceFXAnim, TrackKey->bLoop, SkelMeshTarget, this); } if(!StartSucceeded) { //start failed -> stop FaceFXComp->Stop(SkelMeshTarget); } TrackInstFaceFX->SetCurrentTrackIndex(SkelMeshTarget, TrackKeyPair.Key); } else if(IsPaused) { //resume paused animation FaceFXComp->Resume(SkelMeshTarget, this); } } } TrackInstFaceFX->LastUpdatePosition = NewPosition; }