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