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

	}
}
示例#6
0
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;
	}
}
示例#14
0
void FAnimNode_Trail::Update(const FAnimationUpdateContext& Context)
{
	FAnimNode_SkeletalControlBase::Update(Context);

	ThisTimstep = Context.GetDeltaTime();
}
示例#15
0
void FAnimNode_LookAt::Update(const FAnimationUpdateContext& Context)
{
	FAnimNode_SkeletalControlBase::Update(Context);

	AccumulatedInterpoolationTime = FMath::Clamp(AccumulatedInterpoolationTime+Context.GetDeltaTime(), 0.f, InterpolationTime);;
}
示例#16
0
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);
		}
	}
}