void UAnimSingleNodeInstance::SetPosition(float InPosition, bool bFireNotifies) { float PreviousTime = CurrentTime; CurrentTime = FMath::Clamp<float>(InPosition, 0.f, GetLength()); if (FAnimMontageInstance* CurMontageInstance = GetActiveMontageInstance()) { CurMontageInstance->SetPosition(CurrentTime); } // Handle notifies // the way AnimInstance handles notifies doesn't work for single node because this does not tick or anything // this will need to handle manually, emptying, it and collect it, and trigger them at once. if (bFireNotifies) { UAnimSequenceBase * SequenceBase = Cast<UAnimSequenceBase> (CurrentAsset); if (SequenceBase) { AnimNotifies.Empty(); TArray<const FAnimNotifyEvent*> Notifies; SequenceBase->GetAnimNotifiesFromDeltaPositions(PreviousTime, CurrentTime, Notifies); if ( Notifies.Num() > 0 ) { // single node instance only has 1 asset at a time AddAnimNotifies(Notifies, 1.0f); } TriggerAnimNotifies(0.f); } } }
void UAnimSingleNodeInstance::SetPosition(float InPosition, bool bFireNotifies) { float PreviousTime = CurrentTime; CurrentTime = FMath::Clamp<float>(InPosition, 0.f, GetLength()); if (FAnimMontageInstance* CurMontageInstance = GetActiveMontageInstance()) { CurMontageInstance->SetPosition(CurrentTime); } // Handle notifies // the way AnimInstance handles notifies doesn't work for single node because this does not tick or anything // this will need to handle manually, emptying, it and collect it, and trigger them at once. if (bFireNotifies) { UAnimSequenceBase * SequenceBase = Cast<UAnimSequenceBase> (CurrentAsset); if (SequenceBase) { AnimNotifies.Empty(); TArray<const FAnimNotifyEvent*> Notifies; SequenceBase->GetAnimNotifiesFromDeltaPositions(PreviousTime, CurrentTime, Notifies); if ( Notifies.Num() > 0 ) { // single node instance only has 1 asset at a time AddAnimNotifies(Notifies, 1.0f); } TriggerAnimNotifies(0.f); // since this is singlenode instance, if position changes, we can't keep old morphtarget curves // we clear it and evaluate curve here with new asset. MorphTargetCurves.Empty(); MaterialParameterCurves.Empty(); // Evaluate Curve data now - even if time did not move, we still need to return curve if it exists SequenceBase->EvaluateCurveData(this, CurrentTime, 1.0); } } }
void UAnimInstance::UpdateAnimation(float DeltaSeconds) { #if WITH_EDITORONLY_DATA if (GIsEditor) { // Reset the anim graph visualization if (RootNode != NULL) { if (UAnimBlueprintGeneratedClass* AnimBlueprintClass = Cast<UAnimBlueprintGeneratedClass>(GetClass())) { UAnimBlueprint* AnimBP = CastChecked<UAnimBlueprint>(AnimBlueprintClass->ClassGeneratedBy); if (AnimBP->GetObjectBeingDebugged() == this) { AnimBlueprintClass->GetAnimBlueprintDebugData().ResetNodeVisitSites(); } } } // Update the lifetimer and see if we should use the snapshot instead CurrentLifeTimerScrubPosition += DeltaSeconds; LifeTimer = FMath::Max<double>(CurrentLifeTimerScrubPosition, LifeTimer); if (UpdateSnapshotAndSkipRemainingUpdate()) { return; } } #endif AnimNotifies.Empty(); MorphTargetCurves.Empty(); ClearSlotNodeWeights(); //Track material params we set last time round so we can clear them if they aren't set again. MaterialParamatersToClear.Empty(); for( auto Iter = MaterialParameterCurves.CreateConstIterator(); Iter; ++Iter ) { if(Iter.Value() > 0.0f) { MaterialParamatersToClear.Add(Iter.Key()); } } MaterialParameterCurves.Empty(); VertexAnims.Empty(); // Reset the player tick list (but keep it presized) UngroupedActivePlayers.Empty(UngroupedActivePlayers.Num()); for (int32 GroupIndex = 0; GroupIndex < SyncGroups.Num(); ++GroupIndex) { SyncGroups[GroupIndex].Reset(); } NativeUpdateAnimation(DeltaSeconds); BlueprintUpdateAnimation(DeltaSeconds); // update weight before all nodes update comes in Montage_UpdateWeight(DeltaSeconds); // Update the anim graph if (RootNode != NULL) { IncrementContextCounter(); FAnimationUpdateContext UpdateContext(this, DeltaSeconds); RootNode->Update(UpdateContext); } // curve values can be used during update state, so we need to clear the array before ticking each elements // where we collect new items EventCurves.Empty(); // Handle all players inside sync groups for (int32 GroupIndex = 0; GroupIndex < SyncGroups.Num(); ++GroupIndex) { FAnimGroupInstance& SyncGroup = SyncGroups[GroupIndex]; if (SyncGroup.ActivePlayers.Num() > 0) { const int32 GroupLeaderIndex = FMath::Max(SyncGroup.GroupLeaderIndex, 0); // Tick the group leader FAnimAssetTickContext TickContext(DeltaSeconds); FAnimTickRecord& GroupLeader = SyncGroup.ActivePlayers[GroupLeaderIndex]; GroupLeader.SourceAsset->TickAssetPlayerInstance(GroupLeader, this, TickContext); // Update everything else to follow the leader if (SyncGroup.ActivePlayers.Num() > 1) { TickContext.ConvertToFollower(); for (int32 TickIndex = 0; TickIndex < SyncGroup.ActivePlayers.Num(); ++TickIndex) { if (TickIndex != GroupLeaderIndex) { const FAnimTickRecord& AssetPlayer = SyncGroup.ActivePlayers[TickIndex]; AssetPlayer.SourceAsset->TickAssetPlayerInstance(AssetPlayer, this, TickContext); } } } } } // Handle the remaining ungrouped animation players for (int32 TickIndex = 0; TickIndex < UngroupedActivePlayers.Num(); ++TickIndex) { const FAnimTickRecord& AssetPlayerToTick = UngroupedActivePlayers[TickIndex]; FAnimAssetTickContext TickContext(DeltaSeconds); AssetPlayerToTick.SourceAsset->TickAssetPlayerInstance(AssetPlayerToTick, this, TickContext); } // update montage should run in game thread // if we do multi threading, make sure this stays in game thread Montage_Advance(DeltaSeconds); // now trigger Notifies TriggerAnimNotifies(DeltaSeconds); // Add 0.0 curves to clear parameters that we have previously set but didn't set this tick. // - Make a copy of MaterialParametersToClear as it will be modified by AddCurveValue TArray<FName> ParamsToClearCopy = MaterialParamatersToClear; for (int i = 0; i < ParamsToClearCopy.Num(); ++i) { AddCurveValue(ParamsToClearCopy[i], 0.0f, ACF_DrivesMaterial); } #if WITH_EDITOR && 0 { // Take a snapshot if the scrub control is locked to the end, we are playing, and we are the one being debugged if (UAnimBlueprintGeneratedClass* AnimBlueprintClass = Cast<UAnimBlueprintGeneratedClass>(GetClass())) { if (UAnimBlueprint* Blueprint = Cast<UAnimBlueprint>(AnimBlueprintClass->ClassGeneratedBy)) { if (Blueprint->GetObjectBeingDebugged() == this) { if ((CurrentLifeTimerScrubPosition == LifeTimer) && (DeltaSeconds > 0.0f)) { AnimBlueprintClass->GetAnimBlueprintDebugData().TakeSnapshot(this); } } } } } #endif }