安徽省建设网站,网站开发动态结构设计,网站设计和建设自考题,广州网站制作功能ue5.5 gameanimationsample
先看动画蓝图 核心两个node
第一个是根据数据选择当前的pose 第二个是缓存一段历史记录#xff0c;为第一个node选择的时候提供数据。 在animinstance的update方法中 每帧都更新这个函数#xff0c;每帧更新trajectory的数据 看看第一个node的…ue5.5 gameanimationsample
先看动画蓝图 核心两个node
第一个是根据数据选择当前的pose 第二个是缓存一段历史记录为第一个node选择的时候提供数据。 在animinstance的update方法中 每帧都更新这个函数每帧更新trajectory的数据 看看第一个node的执行顺序
void FAnimNode_MotionMatching::UpdateAssetPlayer(const FAnimationUpdateContext Context)void UPoseSearchLibrary::UpdateMotionMatchingState(const FAnimationUpdateContext Context,const TArrayTObjectPtrconst UPoseSearchDatabase Databases,float BlendTime,int32 MaxActiveBlends,const FFloatInterval PoseJumpThresholdTime,float PoseReselectHistory,float SearchThrottleTime,const FFloatInterval PlayRate,FMotionMatchingState InOutMotionMatchingState,EPoseSearchInterruptMode InterruptMode,bool bShouldSearch,bool bShouldUseCachedChannelData,bool bDebugDrawQuery,bool bDebugDrawCurResult){...........//这里每帧都监听第二个节点把history trajectory传过来const IPoseHistory* PoseHistory nullptr;if (FPoseHistoryProvider* PoseHistoryProvider Context.GetMessageFPoseHistoryProvider()){PoseHistory PoseHistoryProvider-GetPoseHistory();}FMemMark Mark(FMemStack::Get());const UAnimInstance* AnimInstance Castconst UAnimInstance(Context.AnimInstanceProxy-GetAnimInstanceObject());check(AnimInstance);const UPoseSearchDatabase* CurrentResultDatabase InOutMotionMatchingState.CurrentSearchResult.Database.Get();if (IsInvalidatingContinuingPose(InterruptMode, CurrentResultDatabase, Databases)){InOutMotionMatchingState.CurrentSearchResult.Reset();}FSearchContext SearchContext(0.f, InOutMotionMatchingState.PoseIndicesHistory, InOutMotionMatchingState.CurrentSearchResult, PoseJumpThresholdTime);//addSearchContext.AddRole(DefaultRole, AnimInstance, PoseHistory);.........const FSearchResult NewSearchResult Database-Search(SearchContext);.........
}UE::PoseSearch::FSearchResult UPoseSearchDatabase::Search(UE::PoseSearch::FSearchContext SearchContext) const{.........Result SearchPCAKDTree(SearchContext);.........
}UE::PoseSearch::FSearchResult UPoseSearchDatabase::SearchPCAKDTree(UE::PoseSearch::FSearchContext SearchContext) const
{.........//channelTConstArrayViewfloat QueryValues SearchContext.GetOrBuildQuery(Schema);.........
}TConstArrayViewfloat FSearchContext::GetOrBuildQuery(const UPoseSearchSchema* Schema)
{QUICK_SCOPE_CYCLE_COUNTER(STAT_PoseSearch_GetOrBuildQuery);check(Schema);if (const FCachedQuery* FoundCachedQuery CachedQueries.FindByPredicate([Schema](const FCachedQuery CachedQuery){return CachedQuery.GetSchema() Schema;})){return FoundCachedQuery-GetValues();}return Schema-BuildQuery(*this);
}TConstArrayViewfloat UPoseSearchSchema::BuildQuery(UE::PoseSearch::FSearchContext SearchContext) const
{QUICK_SCOPE_CYCLE_COUNTER(STAT_PoseSearch_BuildQuery);SearchContext.AddNewFeatureVectorBuilder(this);for (const TObjectPtrUPoseSearchFeatureChannel ChannelPtr : GetChannels()){ChannelPtr-BuildQuery(SearchContext);}return SearchContext.EditFeatureVector();
}void UPoseSearchFeatureChannel_GroupBase::BuildQuery(UE::PoseSearch::FSearchContext SearchContext) const
{for (const TObjectPtrUPoseSearchFeatureChannel SubChannelPtr : GetSubChannels()){if (const UPoseSearchFeatureChannel* SubChannel SubChannelPtr.Get()){SubChannel-BuildQuery(SearchContext);}}
}//subchannel就是各种channel 的数组 比如position channelvelocity channelvoid UPoseSearchFeatureChannel_Position::BuildQuery(UE::PoseSearch::FSearchContext SearchContext) const
{..............const FVector BonePosition SearchContext.GetSamplePosition(SampleTimeOffset, OriginTimeOffset, SchemaBoneIdx, SchemaOriginBoneIdx, SampleRole, OriginRole, PermutationTimeType, BonePositionWorld);...........}FVector FSearchContext::GetSamplePosition(float SampleTimeOffset, float OriginTimeOffset, int8 SchemaSampleBoneIdx, int8 SchemaOriginBoneIdx, const FRole SampleRole, const FRole OriginRole, EPermutationTimeType PermutationTimeType, const FVector* SampleBonePositionWorldOverride)
{float PermutationSampleTimeOffset 0.f;float PermutationOriginTimeOffset 0.f;UPoseSearchFeatureChannel::GetPermutationTimeOffsets(PermutationTimeType, DesiredPermutationTimeOffset, PermutationSampleTimeOffset, PermutationOriginTimeOffset);const float SampleTime SampleTimeOffset PermutationSampleTimeOffset;const float OriginTime OriginTimeOffset PermutationOriginTimeOffset;return GetSamplePositionInternal(SampleTime, OriginTime, SchemaSampleBoneIdx, SchemaOriginBoneIdx, SampleRole, OriginRole, SampleBonePositionWorldOverride);
}FVector FSearchContext::GetSamplePositionInternal(float SampleTime, float OriginTime, int8 SchemaSampleBoneIdx, int8 SchemaOriginBoneIdx, const FRole SampleRole, const FRole OriginRole, const FVector* SampleBonePositionWorldOverride)
{if (SampleBonePositionWorldOverride){const FTransform RootBoneTransform GetWorldBoneTransformAtTime(OriginTime, OriginRole, RootSchemaBoneIdx);if (SchemaOriginBoneIdx RootSchemaBoneIdx){return RootBoneTransform.InverseTransformPosition(*SampleBonePositionWorldOverride);}// todo: validate this still works for when root bone is not Identityconst FTransform OriginBoneTransform GetWorldBoneTransformAtTime(OriginTime, OriginRole, SchemaOriginBoneIdx);const FVector DeltaBoneTranslation *SampleBonePositionWorldOverride - OriginBoneTransform.GetTranslation();return RootBoneTransform.InverseTransformVector(DeltaBoneTranslation);}const FTransform RootBoneTransform GetWorldBoneTransformAtTime(OriginTime, OriginRole, RootSchemaBoneIdx);const FTransform SampleBoneTransform GetWorldBoneTransformAtTime(SampleTime, SampleRole, SchemaSampleBoneIdx);if (SchemaOriginBoneIdx RootSchemaBoneIdx){return RootBoneTransform.InverseTransformPosition(SampleBoneTransform.GetTranslation());}const FTransform OriginBoneTransform GetWorldBoneTransformAtTime(OriginTime, OriginRole, SchemaOriginBoneIdx);const FVector DeltaBoneTranslation SampleBoneTransform.GetTranslation() - OriginBoneTransform.GetTranslation();return RootBoneTransform.InverseTransformVector(DeltaBoneTranslation);
}//获得过程终于用到了const IPoseHistory* PoseHistory GetPoseHistory(SampleRole);
FTransform FSearchContext::GetWorldBoneTransformAtTime(float SampleTime, const FRole SampleRole, int8 SchemaBoneIdx)
{// CachedQueries.Last is the query were building check(!CachedQueries.IsEmpty());const UPoseSearchSchema* Schema CachedQueries.Last().GetSchema();check(Schema);TConstArrayViewFBoneReference BoneReferences Schema-GetBoneReferences(SampleRole);check(BoneReferences[SchemaBoneIdx].HasValidSetup());const FBoneIndexType BoneIndexType BoneReferences[SchemaBoneIdx].BoneIndex;const uint32 SampleTimeHash GetTypeHash(SampleTime);const uint32 SampleRoleHash GetTypeHash(SampleRole);const uint32 SampleTimeAndRoleHash HashCombineFast(SampleTimeHash, SampleRoleHash);const uint32 BoneIndexTypeHash GetTypeHash(BoneIndexType);const uint32 BoneCachedTransformKey HashCombineFast(SampleTimeAndRoleHash, BoneIndexTypeHash);if (const FTransform* CachedTransform CachedTransforms.Find(BoneCachedTransformKey)){return *CachedTransform;}FTransform WorldBoneTransform;if (BoneIndexType RootBoneIndexType){// we already tried querying the CachedTransforms so, lets search in TrajectoryWorldBoneTransform GetWorldRootBoneTransformAtTime(SampleTime, SampleRole);}else // if (BoneIndexType ! RootBoneIndexType){// searching for RootBoneIndexType in CachedTransformsstatic const uint32 RootBoneIndexTypeHash GetTypeHash(RootBoneIndexType); // Note: static const, since RootBoneIndexType is a constantconst uint32 RootBoneCachedTransformKey HashCombineFast(SampleTimeAndRoleHash, RootBoneIndexTypeHash);if (const FTransform* CachedTransform CachedTransforms.Find(RootBoneCachedTransformKey)){WorldBoneTransform *CachedTransform;}else{WorldBoneTransform GetWorldRootBoneTransformAtTime(SampleTime, SampleRole);}// collecting the local bone transforms from the IPoseHistoryconst IPoseHistory* PoseHistory GetPoseHistory(SampleRole);#if WITH_EDITORif (!PoseHistory){UE_LOG(LogPoseSearch, Error, TEXT(FSearchContext::GetWorldBoneTransformAtTime - Couldnt search for bones requested by %s, because no IPoseHistory has been found!), *Schema-GetName());}else#endif // WITH_EDITOR{check(PoseHistory);const USkeleton* Skeleton Schema-GetSkeleton(SampleRole);FTransform LocalBoneTransform;if (!PoseHistory-GetTransformAtTime(SampleTime, LocalBoneTransform, Skeleton, BoneIndexType, RootBoneIndexType)){if (Skeleton){if (!PoseHistory-IsEmpty()){UE_LOG(LogPoseSearch, Warning, TEXT(FSearchContext::GetWorldBoneTransformAtTime - Couldnt find BoneIndexType %d (%s) requested by %s), BoneIndexType, *Skeleton-GetReferenceSkeleton().GetBoneName(BoneIndexType).ToString(), *Schema-GetName());}}else{UE_LOG(LogPoseSearch, Warning, TEXT(FSearchContext::GetWorldBoneTransformAtTime - Schema %s Skeleton is not properly set), *Schema-GetName());}}WorldBoneTransform LocalBoneTransform * WorldBoneTransform;}}CachedTransforms.Add(BoneCachedTransformKey) WorldBoneTransform;return WorldBoneTransform;
}//这里就是第二个节点pose history 里面有 trajectory
bool FPoseHistory::GetTransformAtTime(float Time, FTransform OutBoneTransform, const USkeleton* BoneIndexSkeleton, FBoneIndexType BoneIndexType, FBoneIndexType ReferenceBoneIndexType, bool bExtrapolate) const
{CheckThreadSafetyRead(ReadPoseDataThreadSafeCounter);static_assert(RootBoneIndexType 0 ComponentSpaceIndexType FBoneIndexType(-1) WorldSpaceIndexType FBoneIndexType(-2)); // some assumptionscheck(BoneIndexType ! ComponentSpaceIndexType BoneIndexType ! WorldSpaceIndexType);bool bSuccess false;const bool bApplyComponentToWorld ReferenceBoneIndexType WorldSpaceIndexType;FTransform ComponentToWorld FTransform::Identity;if (bApplyComponentToWorld){//就是这个和trajectory 联系上了ComponentToWorld Trajectory.GetSampleAtTime(Time, bExtrapolate).GetTransform();ReferenceBoneIndexType ComponentSpaceIndexType;}const FPoseData ReadPoseData GetReadPoseData();const int32 NumEntries ReadPoseData.Entries.Num();if (NumEntries 0){int32 NextIdx 0;int32 PrevIdx 0;if (NumEntries 1){const int32 LowerBoundIdx LowerBound(ReadPoseData.Entries.begin(), ReadPoseData.Entries.end(), Time, [](const FPoseHistoryEntry Entry, float Value) { return Value Entry.AccumulatedSeconds; });NextIdx FMath::Clamp(LowerBoundIdx, 1, NumEntries - 1);PrevIdx NextIdx - 1;}const FPoseHistoryEntry PrevEntry ReadPoseData.Entries[PrevIdx];const FPoseHistoryEntry NextEntry ReadPoseData.Entries[NextIdx];bSuccess LerpEntries(Time, bExtrapolate, PrevEntry, NextEntry, BoneIndexSkeleton, ReadPoseData.LastUpdateSkeleton.Get(), ReadPoseData.BoneToTransformMap, BoneIndexType, ReferenceBoneIndexType, OutBoneTransform);if (bApplyComponentToWorld){OutBoneTransform * ComponentToWorld;}}else{OutBoneTransform ComponentToWorld;}return bSuccess;
} 关于pose history FPoseHistory 里面有 FPoseSearchQueryTrajectory Trajectory;
就是 void FAnimNode_PoseSearchHistoryCollector::Evaluate_AnyThread(FPoseContext Output)
{DECLARE_SCOPE_HIERARCHICAL_COUNTER_ANIMNODE(Evaluate_AnyThread);ANIM_MT_SCOPE_CYCLE_COUNTER_VERBOSE(PoseSearchHistoryCollector, !IsInGameThread());check(Output.AnimInstanceProxy);Super::Evaluate_AnyThread(Output);Source.Evaluate(Output);const bool bNeedsReset bResetOnBecomingRelevant UpdateCounter.HasEverBeenUpdated() !UpdateCounter.WasSynchronizedCounter(Output.AnimInstanceProxy-GetUpdateCounter());FCSPoseFCompactPose ComponentSpacePose;ComponentSpacePose.InitPose(Output.Pose);TArrayFBoneIndexType RequiredBones;if (bCacheBones){RequiredBones GetRequiredBones(Output.AnimInstanceProxy);}PoseHistory.EvaluateComponentSpace_AnyThread(Output.AnimInstanceProxy-GetDeltaSeconds(), ComponentSpacePose, bStoreScales,RootBoneRecoveryTime, RootBoneTranslationRecoveryRatio, RootBoneRotationRecoveryRatio, bNeedsReset, bCacheBones, RequiredBones, Output.Curve, MakeConstArrayView(CollectedCurves));bCacheBones false;#if ENABLE_DRAW_DEBUG ENABLE_ANIM_DEBUGFColor Color;
#if WITH_EDITORONLY_DATAColor DebugColor.ToFColor(true);
#else // WITH_EDITORONLY_DATAColor FLinearColor::Red.ToFColor(true);
#endif // WITH_EDITORONLY_DATAPoseHistory.DebugDraw(*Output.AnimInstanceProxy, Color);
#endif // ENABLE_DRAW_DEBUG ENABLE_ANIM_DEBUG
}FPoseHistory中Trajectory能确定控件的世界空间下的 pos和rotation
再配合CollectedBones确定的骨骼的历史记录
就能确定某个骨骼的历史 的世界坐标
来作为选择某个pose的支撑数据 在FSearchContext中
const IPoseHistory* GetPoseHistory(const FRole Role) const { return PoseHistories[RoleToIndex[Role]]; } 资产方面 最重要的是要建立 pose search database schema最重要的是定义channel从不同的维度去解析查找符合预期的pose
各种channel的定义