void FFaceFXAnimationTrackInstance::Update(EMovieSceneUpdateData& UpdateData, const TArray<TWeakObjectPtr<UObject>>& RuntimeObjects, class IMovieScenePlayer& Player, FMovieSceneSequenceInstance& SequenceInstance) { check(AnimationTrack); //Find the section to update UFaceFXAnimationSection* AnimSection = Cast<UFaceFXAnimationSection>(AnimationTrack->GetSectionAtTime(UpdateData.Position)); if (!AnimSection || !AnimSection->IsActive()) { StopAllPlayback(RuntimeObjects); CurrentActiveSection = nullptr; return; } const float PlaybackLocation = CalcPlaybackLocation(UpdateData.Position, AnimSection); //update the current anim section within this track const bool IsNewAnimSection = CurrentActiveSection != AnimSection; CurrentActiveSection = AnimSection; //update the assigned runtime objects for this track for (int32 i = 0; i < RuntimeObjects.Num(); ++i) { UObject* RuntimeObject = RuntimeObjects[i].Get(); UFaceFXComponent* FaceFXComponent = GetFaceFXComponent(RuntimeObject); if (!FaceFXComponent) { continue; } //may be null if the skel mesh component id is unset and there is no character setup on the component USkeletalMeshComponent* SkelMeshTarget = FaceFXComponent->GetSkelMeshTarget(AnimSection->GetComponent()); //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 (IsNewAnimSection) { FaceFXComponent->Stop(SkelMeshTarget); } const EMovieScenePlayerStatus::Type State = Player.GetPlaybackStatus(); const bool bScrub = State != EMovieScenePlayerStatus::Playing; const bool bPaused = State == EMovieScenePlayerStatus::Stopped && UpdateData.Position == UpdateData.LastPosition; //playing backwards or jumping const FFaceFXAnimId& AnimId = AnimSection->GetAnimationId(); bool UpdateAnimation = true; if (State == EMovieScenePlayerStatus::Playing) { //Playback mode if (!IsNewAnimSection && FaceFXComponent->IsPlaying(SkelMeshTarget, RuntimeObject)) { //No need to updating the FaceFXComponent UpdateAnimation = false; } } if (UpdateAnimation) { bool JumpSucceeded = false; if (bPaused) { FaceFXComponent->Pause(SkelMeshTarget); } else { //jump if not stopping if (AnimId.IsValid()) { //play by animation id JumpSucceeded = FaceFXComponent->JumpToById(PlaybackLocation, bScrub, AnimId.Group, AnimId.Name, false, SkelMeshTarget, RuntimeObject); } else if (UFaceFXAnim* FaceFXAnim = AnimSection->GetAnimation(FaceFXComponent)) { //play by animation JumpSucceeded = FaceFXComponent->JumpTo(PlaybackLocation, bScrub, FaceFXAnim, false, SkelMeshTarget, RuntimeObject); } if (!JumpSucceeded) { //jump to failed -> i.e. out of range on non looping animation FaceFXComponent->Stop(SkelMeshTarget); } } } if (SkelMeshTarget) { //enforce an update on the bones to trigger blend nodes SkelMeshTarget->RefreshBoneTransforms(); } } }
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; }