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(); } } }