void FAnimationNode_TwoWayBlend::Update(const FAnimationUpdateContext& Context) { EvaluateGraphExposedInputs.Execute(Context); const float ActualAlpha = AlphaScaleBias.ApplyTo(Alpha); if (ActualAlpha > ZERO_ANIMWEIGHT_THRESH) { if (ActualAlpha < 1.0f - ZERO_ANIMWEIGHT_THRESH) { // Blend A and B together A.Update(Context.FractionalWeight(1.0f - ActualAlpha)); B.Update(Context.FractionalWeight(ActualAlpha)); } else { // Take all of B B.Update(Context); } } else { // Take all of A A.Update(Context); } }
void FAnimNode_AbilityAnimPlayer::UpdateAssetPlayer(const FAnimationUpdateContext& Context) { EvaluateGraphExposedInputs.Execute(Context); if (m_SequenceToPlay && m_SequenceToPlay->SequenceLength - InternalTimeAccumulator < KINDA_SMALL_NUMBER) { if (m_NextSequence) { SwapToNextSequence(); } else { m_SequenceToPlay = nullptr; } } if (m_SequenceToPlay && Context.AnimInstanceProxy->IsSkeletonCompatible(m_SequenceToPlay->GetSkeleton())) { if (m_NextSequence && m_NextSequenceBlendIn.IsComplete()) { SwapToNextSequence(); } InternalTimeAccumulator = FMath::Clamp(InternalTimeAccumulator, 0.f, m_SequenceToPlay->SequenceLength); if (m_NextSequence) { m_NextSequenceBlendIn.Update(Context.GetDeltaTime()); m_NextSequenceInternalTimeAccumulator += Context.GetDeltaTime(); m_NextSequenceInternalTimeAccumulator = FMath::Clamp(m_NextSequenceInternalTimeAccumulator, 0.0f, m_NextSequence->SequenceLength); } if (FAnimInstanceProxy* Proxy = Context.AnimInstanceProxy) { FAnimGroupInstance* SyncGroup; FAnimTickRecord& SequenceTickRecord = Proxy->CreateUninitializedTickRecord(NAME_None, SyncGroup); float CurrentBlendValue = m_NextSequenceBlendIn.GetBlendedValue(); float SequenceBlendRate = m_NextSequence ? 1.0f - CurrentBlendValue : 1.0f; Proxy->MakeSequenceTickRecord(SequenceTickRecord, const_cast<UAnimSequence*>(m_SequenceToPlay), false, m_PlayRate, SequenceBlendRate, InternalTimeAccumulator, MarkerTickRecord); if (m_NextSequence) { FAnimTickRecord& NextSequenceTickRecord = Proxy->CreateUninitializedTickRecord(NAME_None, SyncGroup); Proxy->MakeSequenceTickRecord(NextSequenceTickRecord, const_cast<UAnimSequence*>(m_NextSequence), false, m_NextPlayRate, CurrentBlendValue, m_NextSequenceInternalTimeAccumulator, m_NextSequenceMarkerTickRecord); } } //CreateTickRecordForNode(Context, const_cast<UAnimSequence*>(m_SequenceToPlay), false, m_PlayRate); } else if (m_CachedOutputSequence && Context.AnimInstanceProxy->IsSkeletonCompatible(m_CachedOutputSequence->GetSkeleton())) { InternalTimeAccumulator = FMath::Clamp(InternalTimeAccumulator, 0.0f, m_CachedOutputSequence->SequenceLength); if (FAnimInstanceProxy* Proxy = Context.AnimInstanceProxy) { FAnimGroupInstance* SyncGroup; FAnimTickRecord& SequenceTickRecord = Proxy->CreateUninitializedTickRecord(NAME_None, SyncGroup); Proxy->MakeSequenceTickRecord(SequenceTickRecord, const_cast<UAnimSequence*>(m_CachedOutputSequence), false, m_PlayRate, 1.0, InternalTimeAccumulator, MarkerTickRecord); } } }
void FAnimationActiveTransitionEntry::Update(const FAnimationUpdateContext& Context, int32 CurrentStateIndex, bool& bOutFinished) { bOutFinished = false; // Advance time if (bActive) { ElapsedTime += Context.GetDeltaTime(); Blend.Update(Context.GetDeltaTime()); float QueryAlpha = 1.0f; // If non-zero, calculate the query alpha if (CrossfadeDuration > 0.0f) { QueryAlpha = ElapsedTime / CrossfadeDuration; } Alpha = FAlphaBlend::AlphaToBlendOption(QueryAlpha, Blend.GetBlendOption(), Blend.GetCustomCurve()); if(Blend.IsComplete()) { bActive = false; bOutFinished = true; } // Update state blend data (only when we're using per-bone) if(BlendProfile) { for(int32 Idx = 0 ; Idx < 2 ; ++Idx) { bool bForwards = Idx == 0; FBlendSampleData& CurrentData = StateBlendData[Idx]; CurrentData.TotalWeight = (bForwards) ? Alpha : 1.0f - Alpha; for(int32 PerBoneIndex = 0 ; PerBoneIndex < CurrentData.PerBoneBlendData.Num() ; ++PerBoneIndex) { float& BoneBlend = CurrentData.PerBoneBlendData[PerBoneIndex]; float WeightScale = BlendProfile->GetEntryBlendScale(PerBoneIndex); if(!bForwards) { WeightScale = 1.0f / WeightScale; } BoneBlend = CurrentData.TotalWeight * WeightScale; } } FBlendSampleData::NormalizeDataWeight(StateBlendData); } } }
void FAnimNode_SequencePlayer::UpdateAssetPlayer(const FAnimationUpdateContext& Context) { EvaluateGraphExposedInputs.Execute(Context); if ((Sequence != NULL) && (Context.AnimInstance->CurrentSkeleton->IsCompatible(Sequence->GetSkeleton()))) { const float FinalBlendWeight = Context.GetFinalBlendWeight(); // Create a tick record and fill it out FAnimGroupInstance* SyncGroup; FAnimTickRecord& TickRecord = Context.AnimInstance->CreateUninitializedTickRecord(GroupIndex, /*out*/ SyncGroup); if (InternalTimeAccumulator > Sequence->SequenceLength) { InternalTimeAccumulator = 0.f; } Context.AnimInstance->MakeSequenceTickRecord(TickRecord, Sequence, bLoopAnimation, PlayRate, FinalBlendWeight, /*inout*/ InternalTimeAccumulator); // Update the sync group if it exists if (SyncGroup != NULL) { SyncGroup->TestTickRecordForLeadership(GroupRole); } } }
void FAnimationActiveTransitionEntry::Update(const FAnimationUpdateContext& Context, int32 CurrentStateIndex, bool& bOutFinished) { bOutFinished = false; // Advance time if (bActive) { ElapsedTime += Context.GetDeltaTime(); if (ElapsedTime >= CrossfadeDuration) { bActive = false; bOutFinished = true; } if(CrossfadeDuration <= 0.0f) { Alpha = 1.0f; } else { Alpha = CalculateAlpha(ElapsedTime / CrossfadeDuration); } } }
void FAnimNode_SpringBone::Update(const FAnimationUpdateContext& Context) { FAnimNode_SkeletalControlBase::Update(Context); RemainingTime += Context.GetDeltaTime(); const USkeletalMeshComponent* SkelComp = Context.AnimInstance->GetSkelMeshComponent(); const UWorld* World = SkelComp->GetWorld(); check(World->GetWorldSettings()); // Fixed step simulation at 120hz FixedTimeStep = (1.f / 120.f) * World->GetWorldSettings()->GetEffectiveTimeDilation(); }
void FAnimNode_StateMachine::UpdateTransitionStates(const FAnimationUpdateContext& Context, FAnimationActiveTransitionEntry& Transition) { if (Transition.bActive) { switch (Transition.LogicType) { case ETransitionLogicType::TLT_StandardBlend: { // update both states UpdateState(Transition.PreviousState, Context.FractionalWeight(1.0f - Transition.Alpha)); UpdateState(Transition.NextState, Context.FractionalWeight(Transition.Alpha)); } break; case ETransitionLogicType::TLT_Custom: { if (Transition.CustomTransitionGraph.LinkID != INDEX_NONE) { Transition.CustomTransitionGraph.Update(Context); for (TArray<FAnimNode_TransitionPoseEvaluator*>::TIterator PoseEvaluatorListIt = Transition.PoseEvaluators.CreateIterator(); PoseEvaluatorListIt; ++PoseEvaluatorListIt) { FAnimNode_TransitionPoseEvaluator* Evaluator = *PoseEvaluatorListIt; if (Evaluator->InputNodeNeedsUpdate()) { const bool bUsePreviousState = (Evaluator->DataSource == EEvaluatorDataSource::EDS_SourcePose); const int32 EffectiveStateIndex = bUsePreviousState ? Transition.PreviousState : Transition.NextState; UpdateState(EffectiveStateIndex, Context); } } } } break; default: break; } } }
void FAnimationActiveTransitionEntry::InitializeCustomGraphLinks(const FAnimationUpdateContext& Context, const FBakedStateExitTransition& TransitionRule) { if (TransitionRule.CustomResultNodeIndex != INDEX_NONE) { const UAnimBlueprintGeneratedClass* AnimBlueprintClass = Context.GetAnimBlueprintClass(); CustomTransitionGraph.LinkID = AnimBlueprintClass->AnimNodeProperties.Num() - 1 - TransitionRule.CustomResultNodeIndex; //@TODO: Crazysauce FAnimationInitializeContext InitContext(Context.AnimInstance); CustomTransitionGraph.Initialize(InitContext); for (int32 Index = 0; Index < TransitionRule.PoseEvaluatorLinks.Num(); ++Index) { FAnimNode_TransitionPoseEvaluator* PoseEvaluator = GetNodeFromPropertyIndex<FAnimNode_TransitionPoseEvaluator>(Context.AnimInstance, AnimBlueprintClass, TransitionRule.PoseEvaluatorLinks[Index]); PoseEvaluators.Add(PoseEvaluator); } } }
void FAnimNode_ApplyMeshSpaceAdditive::Update(const FAnimationUpdateContext& Context) { Base.Update(Context); ActualAlpha = 0.f; if (IsLODEnabled(Context.AnimInstanceProxy, LODThreshold)) { // @note: If you derive this class, and if you have input that you rely on for base // this is not going to work EvaluateGraphExposedInputs.Execute(Context); ActualAlpha = AlphaScaleBias.ApplyTo(Alpha); if (FAnimWeight::IsRelevant(ActualAlpha)) { Additive.Update(Context.FractionalWeight(ActualAlpha)); } } }
void FPoseLinkBase::Update(const FAnimationUpdateContext& Context) { QUICK_SCOPE_CYCLE_COUNTER(STAT_FPoseLinkBase_Update); #if DO_CHECK checkf( !bProcessed, TEXT( "Update already in progress, circular link for AnimInstance [%s] Blueprint [%s]" ), \ *Context.AnimInstanceProxy->GetAnimInstanceName(), *GetFullNameSafe(IAnimClassInterface::GetActualAnimClass(Context.AnimInstanceProxy->GetAnimClassInterface()))); TGuardValue<bool> CircularGuard(bProcessed, true); #endif #if WITH_EDITOR if (GIsEditor) { if (LinkedNode == NULL) { //@TODO: Should only do this when playing back AttemptRelink(Context); } // Record the node line activation if (LinkedNode != NULL) { if (Context.AnimInstanceProxy->IsBeingDebugged()) { Context.AnimInstanceProxy->RecordNodeVisit(LinkID, SourceLinkID, Context.GetFinalBlendWeight()); } } } #endif #if ENABLE_ANIMGRAPH_TRAVERSAL_DEBUG checkf(InitializationCounter.IsSynchronizedWith(Context.AnimInstanceProxy->GetInitializationCounter()), TEXT("Calling Update without initialization!")); UpdateCounter.SynchronizeWith(Context.AnimInstanceProxy->GetUpdateCounter()); #endif if (LinkedNode != NULL) { LinkedNode->Update(Context); } }
void FAnimNode_QueuedSequencePlayer::Update(const FAnimationUpdateContext& Context) { EvaluateGraphExposedInputs.Execute(Context); if (CurrentSequence == nullptr || (NextSequence != nullptr && (InternalTimeAccumulator + SMALL_NUMBER >= CurrentSequence->SequenceLength || LastInternalTimeAccumulator > InternalTimeAccumulator))) { CurrentSequence = NextSequence; InternalTimeAccumulator = 0; } if ((CurrentSequence != NULL) && (Context.AnimInstanceProxy->GetSkeleton()->IsCompatible(CurrentSequence->GetSkeleton()))) { const float fTriggerTime = FMath::Max(0.f, CurrentSequence->SequenceLength - EndOfPlayEventTime); if (LastInternalTimeAccumulator < fTriggerTime && InternalTimeAccumulator >= fTriggerTime) { UAnimInstance* Instance = Context.AnimInstanceProxy->GetSkelMeshComponent()->GetAnimInstance(); UFunction* EOPFunc = Instance->FindFunction(EndOfPlayEvent); if (EOPFunc != nullptr) { Instance->ProcessEvent(EOPFunc, nullptr); } } const float FinalBlendWeight = Context.GetFinalBlendWeight(); // Create a tick record and fill it out UAnimInstance* Instance = Context.AnimInstanceProxy->GetSkelMeshComponent()->GetAnimInstance(); FAnimGroupInstance* SyncGroup; FAnimTickRecord& TickRecord = Context.AnimInstanceProxy->CreateUninitializedTickRecord(INDEX_NONE, /*out*/ SyncGroup); Context.AnimInstanceProxy->MakeSequenceTickRecord(TickRecord, CurrentSequence, false, 1.0, FinalBlendWeight, /*inout*/ InternalTimeAccumulator, *TickRecord.MarkerTickRecord); } LastInternalTimeAccumulator = InternalTimeAccumulator; }
void FAnimationActiveTransitionEntry::InitializeCustomGraphLinks(const FAnimationUpdateContext& Context, const FBakedStateExitTransition& TransitionRule) { if (TransitionRule.CustomResultNodeIndex != INDEX_NONE) { const IAnimClassInterface* AnimBlueprintClass = Context.GetAnimClass(); CustomTransitionGraph.LinkID = AnimBlueprintClass->GetAnimNodeProperties().Num() - 1 - TransitionRule.CustomResultNodeIndex; //@TODO: Crazysauce FAnimationInitializeContext InitContext(Context.AnimInstanceProxy); CustomTransitionGraph.Initialize(InitContext); for (int32 Index = 0; Index < TransitionRule.PoseEvaluatorLinks.Num(); ++Index) { FAnimNode_TransitionPoseEvaluator* PoseEvaluator = GetNodeFromPropertyIndex<FAnimNode_TransitionPoseEvaluator>(Context.AnimInstanceProxy->GetAnimInstanceObject(), AnimBlueprintClass, TransitionRule.PoseEvaluatorLinks[Index]); PoseEvaluators.Add(PoseEvaluator); } } // Initialize blend data if necessary if(BlendProfile) { StateBlendData.AddZeroed(2); StateBlendData[0].PerBoneBlendData.AddZeroed(BlendProfile->GetNumBlendEntries()); StateBlendData[1].PerBoneBlendData.AddZeroed(BlendProfile->GetNumBlendEntries()); } }
void FAnimNode_BlendSpacePlayer::UpdateInternal(const FAnimationUpdateContext& Context) { if ((BlendSpace != NULL) && (Context.AnimInstanceProxy->IsSkeletonCompatible(BlendSpace->GetSkeleton()))) { // Create a tick record and fill it out FAnimGroupInstance* SyncGroup; FAnimTickRecord& TickRecord = Context.AnimInstanceProxy->CreateUninitializedTickRecord(GroupIndex, /*out*/ SyncGroup); const FVector BlendInput(X, Y, Z); if (PreviousBlendSpace != BlendSpace) { Reinitialize(); } Context.AnimInstanceProxy->MakeBlendSpaceTickRecord(TickRecord, BlendSpace, BlendInput, BlendSampleDataCache, BlendFilter, bLoop, PlayRate, Context.GetFinalBlendWeight(), /*inout*/ InternalTimeAccumulator, MarkerTickRecord); // Update the sync group if it exists if (SyncGroup != NULL) { SyncGroup->TestTickRecordForLeadership(GroupRole); } PreviousBlendSpace = BlendSpace; } }
void FAnimNode_Trail::Update(const FAnimationUpdateContext& Context) { FAnimNode_SkeletalControlBase::Update(Context); ThisTimstep = Context.GetDeltaTime(); }
void FAnimNode_LookAt::Update(const FAnimationUpdateContext& Context) { FAnimNode_SkeletalControlBase::Update(Context); AccumulatedInterpoolationTime = FMath::Clamp(AccumulatedInterpoolationTime+Context.GetDeltaTime(), 0.f, InterpolationTime);; }
void FAnimNode_BlendListBase::Update(const FAnimationUpdateContext& Context) { EvaluateGraphExposedInputs.Execute(Context); const int NumPoses = BlendPose.Num(); checkSlow((BlendTime.Num() == NumPoses) && (BlendWeights.Num() == NumPoses)); if (NumPoses > 0) { // Handle a change in the active child index; adjusting the target weights const int32 ChildIndex = GetActiveChildIndex(); if (ChildIndex != LastActiveChildIndex) { bool LastChildIndexIsInvalid = (LastActiveChildIndex == INDEX_NONE); LastActiveChildIndex = ChildIndex; const float CurrentWeight = BlendWeights[ChildIndex]; const float DesiredWeight = 1.0f; const float WeightDifference = FMath::Clamp<float>(FMath::Abs<float>(DesiredWeight - CurrentWeight), 0.0f, 1.0f); // scale by the weight difference since we want always consistency: // - if you're moving from 0 to full weight 1, it will use the normal blend time // - if you're moving from 0.5 to full weight 1, it will get there in half the time const float RemainingBlendTime = LastChildIndexIsInvalid ? 0.0f : ( BlendTime[ChildIndex] * WeightDifference ); for (int32 i = 0; i < RemainingBlendTimes.Num(); ++i) { RemainingBlendTimes[i] = RemainingBlendTime; } } // Advance the weights //@TODO: This means we advance even in a frame where the target weights/times just got modified; is that desirable? float SumWeight = 0.0f; for (int32 i = 0; i < RemainingBlendTimes.Num(); ++i) { float& RemainingBlendTime = RemainingBlendTimes[i]; float& BlendWeight = BlendWeights[i]; const float DesiredWeight = (i == ChildIndex) ? 1.0f : 0.0f; FAnimationRuntime::TickBlendWeight(Context.GetDeltaTime(), DesiredWeight, BlendWeight, RemainingBlendTime); SumWeight += BlendWeight; } // Renormalize the weights if ((SumWeight > ZERO_ANIMWEIGHT_THRESH) && (FMath::Abs<float>(SumWeight - 1.0f) > ZERO_ANIMWEIGHT_THRESH)) { float ReciprocalSum = 1.0f / SumWeight; for (int32 i = 0; i < BlendWeights.Num(); ++i) { BlendWeights[i] *= ReciprocalSum; } } // Update our active children for (int32 i = 0; i < BlendPose.Num(); ++i) { const float BlendWeight = BlendWeights[i]; if (BlendWeight > ZERO_ANIMWEIGHT_THRESH) { BlendPose[i].Update(Context.FractionalWeight(BlendWeight)); } } } }
bool FAnimNode_StateMachine::FindValidTransition(const FAnimationUpdateContext& Context, const FBakedAnimationState& StateInfo, /*out*/ FAnimationPotentialTransition& OutPotentialTransition, /*out*/ TArray<int32, TInlineAllocator<4>>& OutVisitedStateIndices) { // There is a possibility we'll revisit states connected through conduits, // so we can avoid doing unnecessary work (and infinite loops) by caching off states we have already checked const int32 CheckingStateIndex = GetStateIndex(StateInfo); if (OutVisitedStateIndices.Contains(CheckingStateIndex)) { return false; } OutVisitedStateIndices.Add(CheckingStateIndex); const UAnimBlueprintGeneratedClass* AnimBlueprintClass = Context.GetAnimBlueprintClass(); // Conduit 'states' have an additional entry rule which must be true to consider taking any transitions via the conduit //@TODO: It would add flexibility to be able to define this on normal state nodes as well, assuming the dual-graph editing is sorted out if (FAnimNode_TransitionResult* StateEntryRuleNode = GetNodeFromPropertyIndex<FAnimNode_TransitionResult>(Context.AnimInstance, AnimBlueprintClass, StateInfo.EntryRuleNodeIndex)) { if (StateEntryRuleNode->NativeTransitionDelegate.IsBound()) { // attempt to evaluate native rule StateEntryRuleNode->bCanEnterTransition = StateEntryRuleNode->NativeTransitionDelegate.Execute(); } else { // Execute it and see if we can take this rule StateEntryRuleNode->EvaluateGraphExposedInputs.Execute(Context); } // not ok, back out if (!StateEntryRuleNode->bCanEnterTransition) { return false; } } const int32 NumTransitions = StateInfo.Transitions.Num(); for (int32 TransitionIndex = 0; TransitionIndex < NumTransitions; ++TransitionIndex) { const FBakedStateExitTransition& TransitionRule = StateInfo.Transitions[TransitionIndex]; if (TransitionRule.CanTakeDelegateIndex == INDEX_NONE) { continue; } FAnimNode_TransitionResult* ResultNode = GetNodeFromPropertyIndex<FAnimNode_TransitionResult>(Context.AnimInstance, AnimBlueprintClass, TransitionRule.CanTakeDelegateIndex); if (ResultNode->NativeTransitionDelegate.IsBound()) { // attempt to evaluate native rule ResultNode->bCanEnterTransition = ResultNode->NativeTransitionDelegate.Execute(); } else { bool bStillCallEvaluate = true; if (TransitionRule.StateSequencePlayerToQueryIndex != INDEX_NONE) { // Simple automatic rule FAnimNode_SequencePlayer* SequencePlayer = GetNodeFromPropertyIndex<FAnimNode_SequencePlayer>(Context.AnimInstance, AnimBlueprintClass, TransitionRule.StateSequencePlayerToQueryIndex); if ((SequencePlayer != nullptr) && (SequencePlayer->Sequence != nullptr)) { const float SequenceLength = SequencePlayer->Sequence->GetMaxCurrentTime(); const float PlayerTime = SequencePlayer->InternalTimeAccumulator; const float PlayerTimeLeft = SequenceLength - PlayerTime; const FAnimationTransitionBetweenStates& TransitionInfo = GetTransitionInfo(TransitionRule.TransitionIndex); ResultNode->bCanEnterTransition = (PlayerTimeLeft <= TransitionInfo.CrossfadeDuration); bStillCallEvaluate = false; } } if (bStillCallEvaluate) { // Execute it and see if we can take this rule ResultNode->EvaluateGraphExposedInputs.Execute(Context); } } if (ResultNode->bCanEnterTransition == TransitionRule.bDesiredTransitionReturnValue) { int32 NextState = GetTransitionInfo(TransitionRule.TransitionIndex).NextState; const FBakedAnimationState& NextStateInfo = GetStateInfo(NextState); // if next state is a conduit we want to check for transitions using that state as the root if (NextStateInfo.bIsAConduit) { if (FindValidTransition(Context, NextStateInfo, /*out*/ OutPotentialTransition, /*out*/ OutVisitedStateIndices)) { #if WITH_EDITORONLY_DATA OutPotentialTransition.SourceTransitionIndices.Add(TransitionRule.TransitionIndex); #endif return true; } } // otherwise we have found a content state, so we can record our potential transition else { // clear out any potential transition we already have OutPotentialTransition.Clear(); // fill out the potential transition information OutPotentialTransition.TransitionRule = &TransitionRule; OutPotentialTransition.TargetState = NextState; #if WITH_EDITORONLY_DATA OutPotentialTransition.SourceTransitionIndices.Add(TransitionRule.TransitionIndex); #endif return true; } } } return false; }
void FAnimNode_StateMachine::Update(const FAnimationUpdateContext& Context) { if (FBakedAnimationStateMachine* Machine = GetMachineDescription()) { if (Machine->States.Num() == 0) { return; } } else { return; } SCOPE_CYCLE_COUNTER(STAT_AnimStateMachineUpdate); bool bFoundValidTransition = false; int32 TransitionCountThisFrame = 0; int32 TransitionIndex = INDEX_NONE; // Look for legal transitions to take; can move across multiple states in one frame (up to MaxTransitionsPerFrame) do { bFoundValidTransition = false; FAnimationPotentialTransition PotentialTransition; { SCOPE_CYCLE_COUNTER(STAT_AnimStateMachineFindTransition); // Evaluate possible transitions out of this state //@TODO: Evaluate if a set is better than an array for the probably low N encountered here TArray<int32, TInlineAllocator<4>> VisitedStateIndices; FindValidTransition(Context, GetStateInfo(), /*Out*/ PotentialTransition, /*Out*/ VisitedStateIndices); } // If transition is valid and not waiting on other conditions if (PotentialTransition.IsValid()) { bFoundValidTransition = true; // let the latest transition know it has been interrupted if ((ActiveTransitionArray.Num() > 0) && ActiveTransitionArray[ActiveTransitionArray.Num()-1].bActive) { Context.AnimInstance->AddAnimNotifyFromGeneratedClass(ActiveTransitionArray[ActiveTransitionArray.Num()-1].InterruptNotify); } const int32 PreviousState = CurrentState; const int32 NextState = PotentialTransition.TargetState; // Fire off Notifies for state transition if (!bFirstUpdate) { Context.AnimInstance->AddAnimNotifyFromGeneratedClass(GetStateInfo(PreviousState).EndNotify); Context.AnimInstance->AddAnimNotifyFromGeneratedClass(GetStateInfo(NextState).StartNotify); } // Get the current weight of the next state, which may be non-zero const float ExistingWeightOfNextState = GetStateWeight(NextState); // Push the transition onto the stack const FAnimationTransitionBetweenStates& ReferenceTransition = GetTransitionInfo(PotentialTransition.TransitionRule->TransitionIndex); FAnimationActiveTransitionEntry* NewTransition = new (ActiveTransitionArray) FAnimationActiveTransitionEntry(NextState, ExistingWeightOfNextState, PreviousState, ReferenceTransition); NewTransition->InitializeCustomGraphLinks(Context, *(PotentialTransition.TransitionRule)); #if WITH_EDITORONLY_DATA NewTransition->SourceTransitionIndices = PotentialTransition.SourceTransitionIndices; #endif if (!bFirstUpdate) { Context.AnimInstance->AddAnimNotifyFromGeneratedClass(NewTransition->StartNotify); } SetState(Context, NextState); TransitionCountThisFrame++; } } while (bFoundValidTransition && (TransitionCountThisFrame < MaxTransitionsPerFrame)); if (bFirstUpdate) { //Handle enter notify for "first" (after initial transitions) state Context.AnimInstance->AddAnimNotifyFromGeneratedClass(GetStateInfo().StartNotify); // in the first update, we don't like to transition from entry state // so we throw out any transition data at the first update ActiveTransitionArray.Reset(); bFirstUpdate = false; } StatesUpdated.Empty(StatesUpdated.Num()); // Tick the individual state/states that are active if (ActiveTransitionArray.Num() > 0) { for (int32 Index = 0; Index < ActiveTransitionArray.Num(); ++Index) { // The custom graph will tick the needed states bool bFinishedTrans = false; // The custom graph will tick the needed states ActiveTransitionArray[Index].Update(Context, CurrentState, /*out*/ bFinishedTrans); if (bFinishedTrans) { // only play these events if it is the last transition (most recent, going to current state) if (Index == (ActiveTransitionArray.Num() - 1)) { Context.AnimInstance->AddAnimNotifyFromGeneratedClass(ActiveTransitionArray[Index].EndNotify); Context.AnimInstance->AddAnimNotifyFromGeneratedClass(GetStateInfo().FullyBlendedNotify); } } else { // transition is still active, so tick the required states UpdateTransitionStates(Context, ActiveTransitionArray[Index]); } } // remove finished transitions here, newer transitions ending means any older ones must complete as well for (int32 Index = (ActiveTransitionArray.Num()-1); Index >= 0; --Index) { // if we find an inactive one, remove all older transitions and break out if (!ActiveTransitionArray[Index].bActive) { ActiveTransitionArray.RemoveAt(0, Index+1); break; } } } //@TODO: StatesUpdated.Contains is a linear search // Update the only active state if there are no transitions still in flight if (ActiveTransitionArray.Num() == 0 && !IsAConduitState(CurrentState) && !StatesUpdated.Contains(CurrentState)) { StatePoseLinks[CurrentState].Update(Context); } ElapsedTime += Context.GetDeltaTime(); }
void FAnimNode_AnimDynamics::UpdateInternal(const FAnimationUpdateContext& Context) { FAnimNode_SkeletalControlBase::UpdateInternal(Context); NextTimeStep = Context.GetDeltaTime(); }
void FAnimNode_BlendSpacePlayer::Update(const FAnimationUpdateContext& Context) { EvaluateGraphExposedInputs.Execute(Context); if ((BlendSpace != NULL) && (Context.AnimInstance->CurrentSkeleton->IsCompatible(BlendSpace->GetSkeleton()))) { // Create a tick record and fill it out FAnimGroupInstance* SyncGroup; FAnimTickRecord& TickRecord = Context.AnimInstance->CreateUninitializedTickRecord(GroupIndex, /*out*/ SyncGroup); const FVector BlendInput(X, Y, Z); Context.AnimInstance->MakeBlendSpaceTickRecord(TickRecord, BlendSpace, BlendInput, BlendSampleDataCache, BlendFilter, bLoop, PlayRate, Context.GetFinalBlendWeight(), /*inout*/ InternalTimeAccumulator); // Update the sync group if it exists if (SyncGroup != NULL) { SyncGroup->TestTickRecordForLeadership(GroupRole); } } }