bool ACharacter::CanUseRootMotionRepMove(const FSimulatedRootMotionReplicatedMove& RootMotionRepMove, const FAnimMontageInstance& ClientMontageInstance) const { // Ignore outdated moves. if( GetWorld()->TimeSince(RootMotionRepMove.Time) <= 0.5f ) { // Make sure montage being played matched between client and server. if( RootMotionRepMove.RootMotion.AnimMontage && (RootMotionRepMove.RootMotion.AnimMontage == ClientMontageInstance.Montage) ) { UAnimMontage * AnimMontage = ClientMontageInstance.Montage; const float ServerPosition = RootMotionRepMove.RootMotion.Position; const float ClientPosition = ClientMontageInstance.GetPosition(); const float DeltaPosition = (ClientPosition - ServerPosition); const int32 CurrentSectionIndex = AnimMontage->GetSectionIndexFromPosition(ClientPosition); if( CurrentSectionIndex != INDEX_NONE ) { const int32 NextSectionIndex = (CurrentSectionIndex < ClientMontageInstance.NextSections.Num()) ? ClientMontageInstance.NextSections[CurrentSectionIndex] : INDEX_NONE; // We can only extract root motion if we are within the same section. // It's not trivial to jump through sections in a deterministic manner, but that is luckily not frequent. const bool bSameSections = (AnimMontage->GetSectionIndexFromPosition(ServerPosition) == CurrentSectionIndex); // if we are looping and just wrapped over, skip. That's also not easy to handle and not frequent. const bool bHasLooped = (NextSectionIndex == CurrentSectionIndex) && (FMath::Abs(DeltaPosition) > (AnimMontage->GetSectionLength(CurrentSectionIndex) / 2.f)); // Can only simulate forward in time, so we need to make sure server move is not ahead of the client. const bool bServerAheadOfClient = ((DeltaPosition * ClientMontageInstance.GetPlayRate()) < 0.f); UE_LOG(LogRootMotion, Log, TEXT("\t\tACharacter::CanUseRootMotionRepMove ServerPosition: %.3f, ClientPosition: %.3f, DeltaPosition: %.3f, bSameSections: %d, bHasLooped: %d, bServerAheadOfClient: %d"), ServerPosition, ClientPosition, DeltaPosition, bSameSections, bHasLooped, bServerAheadOfClient); return bSameSections && !bHasLooped && !bServerAheadOfClient; } } } return false; }
float UAnimInstance::PlaySlotAnimation(UAnimSequenceBase* Asset, FName SlotNodeName, float BlendInTime, float BlendOutTime, float InPlayRate) { // create temporary montage and play bool bValidAsset = Asset && !Asset->IsA(UAnimMontage::StaticClass()); if (!bValidAsset) { // user warning UE_LOG(LogAnimation, Warning, TEXT("Invalid Asset. If Montage, use Montage_Play")); return 0.f; } if (SlotNodeName == NAME_None) { // user warning UE_LOG(LogAnimation, Warning, TEXT("SlotNode Name is required. Make sure to add Slot Node in your anim graph and name it.")); return 0.f; } USkeleton * AssetSkeleton = Asset->GetSkeleton(); if (!CurrentSkeleton->IsCompatible(AssetSkeleton)) { UE_LOG(LogAnimation, Warning, TEXT("The Skeleton isn't compatible")); return 0.f; } // now play UAnimMontage * NewMontage = NewObject<UAnimMontage>(); NewMontage->SetSkeleton(AssetSkeleton); // add new track FSlotAnimationTrack NewTrack; NewTrack.SlotName = SlotNodeName; FAnimSegment NewSegment; NewSegment.AnimReference = Asset; NewSegment.AnimStartTime = 0.f; NewSegment.AnimEndTime = Asset->SequenceLength; NewSegment.AnimPlayRate = 1.f; NewSegment.StartPos = 0.f; NewMontage->SequenceLength = Asset->SequenceLength; NewTrack.AnimTrack.AnimSegments.Add(NewSegment); FCompositeSection NewSection; NewSection.SectionName = TEXT("Default"); NewSection.StartTime = 0.f; // add new section NewMontage->CompositeSections.Add(NewSection); NewMontage->BlendInTime = BlendInTime; NewMontage->BlendOutTime = BlendOutTime; NewMontage->SlotAnimTracks.Add(NewTrack); return Montage_Play(NewMontage, InPlayRate); }
bool UAnimInstance::IsPlayingSlotAnimation(UAnimSequenceBase* Asset, FName SlotNodeName ) { // check if this is playing FAnimMontageInstance * CurrentInstance = GetActiveMontageInstance(); // make sure what is active right now is transient that we created by request if ( CurrentInstance && CurrentInstance->Montage && CurrentInstance->Montage->GetOuter() == GetTransientPackage() ) { UAnimMontage * CurMontage = CurrentInstance->Montage; const FAnimTrack * AnimTrack = CurMontage->GetAnimationData(SlotNodeName); if (AnimTrack && AnimTrack->AnimSegments.Num() == 1) { // find if the return (AnimTrack->AnimSegments[0].AnimReference == Asset); } } return false; }
UObject* UAnimMontageFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) { if (TargetSkeleton || SourceAnimation) { UAnimMontage* AnimMontage = ConstructObject<UAnimMontage>(Class,InParent,Name,Flags); if(SourceAnimation) { USkeleton* SourceSkeleton = SourceAnimation->GetSkeleton(); //Make sure we haven't asked to create an AnimComposite with mismatching skeletons check(TargetSkeleton == NULL || TargetSkeleton == SourceSkeleton); TargetSkeleton = SourceSkeleton; FAnimSegment NewSegment; NewSegment.AnimReference = SourceAnimation; NewSegment.AnimStartTime = 0.f; NewSegment.AnimEndTime = SourceAnimation->SequenceLength; NewSegment.AnimPlayRate = 1.f; NewSegment.LoopingCount = 1; NewSegment.StartPos = 0.f; FSlotAnimationTrack NewTrack; NewTrack.AnimTrack.AnimSegments.Add(NewSegment); NewTrack.SlotName = FAnimSlotGroup::DefaultSlotName; AnimMontage->SlotAnimTracks.Add( NewTrack ); AnimMontage->SetSequenceLength(SourceAnimation->SequenceLength); } AnimMontage->SetSkeleton(TargetSkeleton); return AnimMontage; } return NULL; }
PyObject *py_ue_add_anim_composite_section(ue_PyUObject * self, PyObject * args) { ue_py_check(self); char *name; float time; if (!PyArg_ParseTuple(args, "sf:add_anim_composite_section", &name, &time)) return nullptr; UAnimMontage *anim = ue_py_check_type<UAnimMontage>(self); if (!anim) return PyErr_Format(PyExc_Exception, "UObject is not a UAnimMontage."); return PyLong_FromLong(anim->AddAnimCompositeSection(FName(UTF8_TO_TCHAR(name)), time)); }