第一次提交

This commit is contained in:
不明不惑
2026-03-03 01:23:02 +08:00
commit 3e434877e8
1053 changed files with 102411 additions and 0 deletions

View File

@@ -0,0 +1,237 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "Utility/GMS_Utility.h"
#include "Chooser.h"
#include "ChooserPropertyAccess.h"
#include "PoseSearch/PoseSearchDatabase.h"
#include "GameplayTagsManager.h"
#include "IObjectChooser.h"
#include "Animation/AnimInstance.h"
#include "Components/SkeletalMeshComponent.h"
#include "Engine/World.h"
#include "GameFramework/Character.h"
#include "GameFramework/HUD.h"
#include "Animation/AnimSequenceBase.h"
#include "Locomotions/GMS_AnimLayer.h"
#include "Locomotions/GMS_MainAnimInstance.h"
#include "Settings/GMS_SettingObjectLibrary.h"
#include "Utility/GMS_Log.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_Utility)
FString UGMS_Utility::NameToDisplayString(const FName& Name, const bool bNameIsBool)
{
return FName::NameToDisplayString(Name.ToString(), bNameIsBool);
}
float UGMS_Utility::GetAnimationCurveValueFromCharacter(const ACharacter* Character, const FName& CurveName)
{
const auto* Mesh{IsValid(Character) ? Character->GetMesh() : nullptr};
const auto* AnimationInstance{IsValid(Mesh) ? Mesh->GetAnimInstance() : nullptr};
return IsValid(AnimationInstance) ? AnimationInstance->GetCurveValue(CurveName) : 0.0f;
}
FGameplayTagContainer UGMS_Utility::GetChildTags(const FGameplayTag& Tag)
{
return UGameplayTagsManager::Get().RequestGameplayTagChildren(Tag);
}
FName UGMS_Utility::GetSimpleTagName(const FGameplayTag& Tag)
{
const auto TagNode{UGameplayTagsManager::Get().FindTagNode(Tag)};
return TagNode.IsValid() ? TagNode->GetSimpleTagName() : NAME_None;
}
bool UGMS_Utility::ShouldDisplayDebugForActor(const AActor* Actor, const FName& DisplayName)
{
const auto* World{IsValid(Actor) ? Actor->GetWorld() : nullptr};
const auto* PlayerController{IsValid(World) ? World->GetFirstPlayerController() : nullptr};
auto* Hud{IsValid(PlayerController) ? PlayerController->GetHUD() : nullptr};
return IsValid(Hud) && Hud->ShouldDisplayDebug(DisplayName) && Hud->GetCurrentDebugTargetActor() == Actor;
}
float UGMS_Utility::CalculateAnimatedSpeed(const UAnimSequenceBase* AnimSequence)
{
if (AnimSequence == nullptr)
{
UE_LOG(LogGMS, Warning, TEXT("Passed invalid anim sequence"));
return 0.0f;
}
const float AnimLength = AnimSequence->GetPlayLength();
// Calculate the speed as: (distance traveled by the animation) / (length of the animation)
const FVector RootMotionTranslation = AnimSequence->ExtractRootMotionFromRange(0.0f, AnimLength).GetTranslation();
const float RootMotionDistance = RootMotionTranslation.Size2D();
if (!FMath::IsNearlyZero(RootMotionDistance))
{
const float AnimationSpeed = RootMotionDistance / AnimLength;
return AnimationSpeed;
}
UE_LOG(LogGMS, Warning, TEXT("Unable to Calculate animation speed for animation with no root motion delta (%s)."), *GetNameSafe(AnimSequence));
return 0.0f;
}
UAnimSequence* UGMS_Utility::SelectAnimationWithFloat(const TArray<FGMS_AnimationWithDistance>& Animations, const float& ReferenceValue)
{
if (Animations.IsEmpty())
{
return nullptr;
}
float Delta = FLT_MAX; // 使用FLT_MAX来初始化Delta
int32 Found = INDEX_NONE;
for (int32 i = 0; i < Animations.Num(); i++)
{
// 计算与传入Float的绝对差值
float TempDelta = FMath::Abs(ReferenceValue - Animations[i].Distance);
// 如果当前的差值更小则更新Delta和Found
if (TempDelta < Delta)
{
Delta = TempDelta;
Found = i;
}
}
// 返回找到的动画或最后一个动画作为备选
return (Found != INDEX_NONE) ? Animations[Found].Animation : Animations.Last().Animation;
}
bool UGMS_Utility::ValidatePoseSearchDatabasesChooser(const UChooserTable* ChooserTable, FText& OutMessage)
{
if (!IsValid(ChooserTable))
{
OutMessage = FText::FromName("Invalid ChooserTable");
return false;
}
if (ChooserTable->GetContextData().Num() != 2)
{
OutMessage = FText::FromString(FString::Format(TEXT("ChooserTable({0}):Context is empty, and only allow 2 element!"), {*ChooserTable->GetName()}));
return false;
}
if (ChooserTable->ResultType != EObjectChooserResultType::ObjectResult || ChooserTable->OutputObjectType != UPoseSearchDatabase::StaticClass())
{
OutMessage = FText::FromString(FString::Format(TEXT("ChooserTable({0}):Result type must be ObjectResult and the OutputObjectType must be PoseSearchDatabase."), {*ChooserTable->GetName()}));
return false;
}
const FContextObjectTypeClass* Ctx1 = ChooserTable->GetContextData()[0].GetPtr<FContextObjectTypeClass>();
bool bValidCtx1 = Ctx1 != nullptr && Ctx1->Class != nullptr && Ctx1->Class->IsChildOf(UGMS_MainAnimInstance::StaticClass());
if (!bValidCtx1)
{
OutMessage = FText::FromString(FString::Format(
TEXT("ChooserTable({0}): First context must be ContextObjectTypeClass and the class must be Subclass of UGMS_MainAnimInstance."), {*ChooserTable->GetName()}));
return false;
}
const FContextObjectTypeClass* Ctx2 = ChooserTable->GetContextData()[1].GetPtr<FContextObjectTypeClass>();
bool bValidCtx2 = Ctx2 != nullptr && Ctx2->Class != nullptr && Ctx2->Class->IsChildOf(UGMS_AnimLayer::StaticClass());
if (!bValidCtx2)
{
OutMessage = FText::FromString(
FString::Format(TEXT("ChooserTable({0}): Secondary context must be ContextObjectTypeClass and the class must be Subclass of UGMS_AnimLayer."), {*ChooserTable->GetName()}));
return false;
}
return true;
}
bool UGMS_Utility::IsValidPoseSearchDatabasesChooser(const UChooserTable* ChooserTable)
{
if (!IsValid(ChooserTable))
{
return false;
}
if (ChooserTable->GetContextData().Num() != 2)
{
UE_LOG(LogGMS, Warning, TEXT("ChooserTable(%s):Context is empty, and only allow 2 element!"), *ChooserTable->GetName());
return false;
}
if (ChooserTable->ResultType != EObjectChooserResultType::ObjectResult || ChooserTable->OutputObjectType != UPoseSearchDatabase::StaticClass())
{
UE_LOG(LogGMS, Warning, TEXT("ChooserTable(%s):Result type must be ObjectResult and the OutputObjectType must be PoseSearchDatabase."), *ChooserTable->GetName());
return false;
}
const FContextObjectTypeClass* Ctx1 = ChooserTable->GetContextData()[0].GetPtr<FContextObjectTypeClass>();
bool bValidCtx1 = Ctx1 != nullptr && Ctx1->Class != nullptr && Ctx1->Class->IsChildOf(UGMS_MainAnimInstance::StaticClass());
if (!bValidCtx1)
{
UE_LOG(LogGMS, Warning, TEXT("ChooserTable(%s): First context must be ContextObjectTypeClass and the class must be Subclass of UGMS_MainAnimInstance."),
*ChooserTable->GetName());
return false;
}
const FContextObjectTypeClass* Ctx2 = ChooserTable->GetContextData()[1].GetPtr<FContextObjectTypeClass>();
bool bValidCtx2 = Ctx2 != nullptr && Ctx2->Class != nullptr && Ctx2->Class->IsChildOf(UGMS_AnimLayer::StaticClass());
if (!bValidCtx2)
{
GMS_LOG(Warning, "ChooserTable(%s): Secondary context must be ContextObjectTypeClass and the class must be Subclass of UGMS_AnimLayer.",
*ChooserTable->GetName())
return false;
}
return true;
}
TArray<UPoseSearchDatabase*> UGMS_Utility::EvaluatePoseSearchDatabasesChooser(const UGMS_MainAnimInstance* MainAnimInstance, const UGMS_AnimLayer* AnimLayerInstance,
UChooserTable* ChooserTable)
{
TArray<UPoseSearchDatabase*> Ret;
if (!IsValid(ChooserTable))
{
return Ret;
}
// Fallback single context object version
FChooserEvaluationContext Context;
Context.AddObjectParam(const_cast<UGMS_MainAnimInstance*>(MainAnimInstance));
Context.AddObjectParam(const_cast<UGMS_AnimLayer*>(AnimLayerInstance));
auto Callback = FObjectChooserBase::FObjectChooserIteratorCallback::CreateLambda([&Ret](UObject* InResult)
{
if (InResult && InResult->IsA(UPoseSearchDatabase::StaticClass()))
{
Ret.Add(Cast<UPoseSearchDatabase>(InResult));
}
return FObjectChooserBase::EIteratorStatus::Continue;
});
UChooserTable::EvaluateChooser(Context, ChooserTable, Callback);
return Ret;
}
const UGMS_MovementSetUserSetting* UGMS_Utility::GetMovementSetUserSetting(const FGMS_MovementSetSetting& MovementSetSetting, TSubclassOf<UGMS_MovementSetUserSetting> DesiredClass)
{
if (!IsValid(DesiredClass))
{
return nullptr;
}
for (TObjectPtr<UGMS_MovementSetUserSetting> UserSetting : MovementSetSetting.UserSettings)
{
if (UserSetting->GetClass() == DesiredClass)
{
return UserSetting;
}
}
return nullptr;
}