278 lines
9.7 KiB
C++
278 lines
9.7 KiB
C++
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
|
|
|
|
|
#include "Settings/GMS_SettingObjectLibrary.h"
|
|
#include "Locomotions/GMS_AnimLayer.h"
|
|
#include "Animation/BlendSpace.h"
|
|
#include "Locomotions/GMS_AnimLayer_Additive.h"
|
|
#include "Locomotions/GMS_AnimLayer_Overlay.h"
|
|
#include "Locomotions/GMS_AnimLayer_SkeletalControls.h"
|
|
#include "Locomotions/GMS_AnimLayer_States.h"
|
|
#include "Locomotions/GMS_AnimLayer_View_Default.h"
|
|
#include "Misc/DataValidation.h"
|
|
#include "Utility/GMS_Utility.h"
|
|
|
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_SettingObjectLibrary)
|
|
|
|
#pragma region CommonSettings
|
|
|
|
#if WITH_EDITOR
|
|
#include "UObject/ObjectSaveContext.h"
|
|
|
|
void UGMS_MovementDefinition::PreSave(FObjectPreSaveContext SaveContext)
|
|
{
|
|
Super::PreSave(SaveContext);
|
|
}
|
|
|
|
EDataValidationResult UGMS_MovementDefinition::IsDataValid(class FDataValidationContext& Context) const
|
|
{
|
|
for (const TTuple<FGameplayTag, FGMS_MovementSetSetting>& Pair : MovementSets)
|
|
{
|
|
if (Pair.Value.ControlSetting == nullptr)
|
|
{
|
|
Context.AddError(FText::FromString(FString::Format(TEXT("ControlSetting is required on {0}!!!"), {Pair.Key.GetTagName().ToString()})));
|
|
}
|
|
|
|
if (Pair.Value.bUseInstancedStatesSetting && Pair.Value.AnimLayerSetting_States && Pair.Value.AnimLayerSetting_States->IsDataValid(Context) == EDataValidationResult::Invalid)
|
|
{
|
|
return EDataValidationResult::Invalid;
|
|
}
|
|
|
|
if (Pair.Value.bUseInstancedOverlaySetting && Pair.Value.AnimLayerSetting_Overlay && Pair.Value.AnimLayerSetting_Overlay->IsDataValid(Context) == EDataValidationResult::Invalid)
|
|
{
|
|
return EDataValidationResult::Invalid;
|
|
}
|
|
|
|
if (Pair.Value.AnimLayerSetting_Additive && Pair.Value.AnimLayerSetting_Additive->IsDataValid(Context) == EDataValidationResult::Invalid)
|
|
{
|
|
return EDataValidationResult::Invalid;
|
|
}
|
|
|
|
if (Pair.Value.AnimLayerSetting_View && Pair.Value.AnimLayerSetting_View->IsDataValid(Context) == EDataValidationResult::Invalid)
|
|
{
|
|
return EDataValidationResult::Invalid;
|
|
}
|
|
|
|
if (Pair.Value.AnimLayerSetting_SkeletalControls && Pair.Value.AnimLayerSetting_SkeletalControls->IsDataValid(Context) == EDataValidationResult::Invalid)
|
|
{
|
|
return EDataValidationResult::Invalid;
|
|
}
|
|
}
|
|
return Super::IsDataValid(Context);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
FGameplayTag UGMS_MovementControlSetting_Default::MatchStateTagBySpeed(float Speed, float Threshold) const
|
|
{
|
|
for (const FGMS_MovementStateSetting& MovementState : MovementStates)
|
|
{
|
|
if (MovementState.Speed > 0.0f && MovementState.Speed < Speed + Threshold)
|
|
{
|
|
return MovementState.Tag;
|
|
}
|
|
}
|
|
return FGameplayTag::EmptyTag;
|
|
}
|
|
|
|
bool UGMS_MovementControlSetting_Default::GetStateByIndex(const int32& Index, FGMS_MovementStateSetting& OutSetting) const
|
|
{
|
|
if (MovementStates.IsValidIndex(Index))
|
|
{
|
|
OutSetting = MovementStates[Index];
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool UGMS_MovementControlSetting_Default::GetStateBySpeedLevel(const int32& Level, FGMS_MovementStateSetting& OutSetting) const
|
|
{
|
|
if (SpeedLevelToArrayIndex.Contains(Level))
|
|
{
|
|
OutSetting = MovementStates[SpeedLevelToArrayIndex[Level]];
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool UGMS_MovementControlSetting_Default::GetStateByTag(const FGameplayTag& Tag, FGMS_MovementStateSetting& OutSetting) const
|
|
{
|
|
if (auto Setting = GetMovementStateSetting(Tag))
|
|
{
|
|
OutSetting = *Setting;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const FGMS_MovementStateSetting* UGMS_MovementControlSetting_Default::GetMovementStateSetting(const FGameplayTag& Tag) const
|
|
{
|
|
if (!Tag.IsValid())
|
|
{
|
|
return nullptr;
|
|
}
|
|
return MovementStates.FindByPredicate([Tag](const FGMS_MovementStateSetting& Setting)
|
|
{
|
|
return Setting.Tag.IsValid() && Setting.Tag == Tag;
|
|
});
|
|
}
|
|
|
|
const FGMS_MovementStateSetting* UGMS_MovementControlSetting_Default::GetMovementStateSetting(const FGameplayTag& Tag, bool bHasFallback) const
|
|
{
|
|
if (auto Setting = GetMovementStateSetting(Tag))
|
|
{
|
|
return Setting;
|
|
}
|
|
|
|
if (bHasFallback)
|
|
{
|
|
checkf(!MovementStates.IsEmpty(), TEXT("%s: MovementStates can't be empty!"), *GetNameSafe(this))
|
|
return &MovementStates.Last();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region ControlSettings
|
|
|
|
#if WITH_EDITOR
|
|
|
|
float UGMS_MovementControlSetting_Default::MigrateRotationInterpolationSpeed(float Old)
|
|
{
|
|
if (Old <= 0.0f)
|
|
{
|
|
return 0.0f;
|
|
}
|
|
// larger old value, smaller new value.
|
|
return FMath::GetMappedRangeValueClamped(FVector2f{0.0f, 20.0f}, FVector2f{0.3f, 0.1f}, Old);
|
|
}
|
|
|
|
void UGMS_MovementControlSetting_Default::PreSave(FObjectPreSaveContext SaveContext)
|
|
{
|
|
Super::PreSave(SaveContext);
|
|
|
|
SpeedLevelToArrayIndex.Empty();
|
|
|
|
MovementStates.Sort([](const FGMS_MovementStateSetting& A, const FGMS_MovementStateSetting& B)
|
|
{
|
|
return A.SpeedLevel < B.SpeedLevel;
|
|
});
|
|
|
|
for (int i = 0; i < MovementStates.Num(); ++i)
|
|
{
|
|
FGMS_MovementStateSetting& Setting = MovementStates[i];
|
|
Setting.EditorFriendlyName = FString::Format(TEXT("State({0}) SpeedLevel({1}) Speed({2})"), {UGMS_Utility::GetSimpleTagName(Setting.Tag).ToString(), Setting.SpeedLevel, Setting.Speed});
|
|
SpeedLevelToArrayIndex.Emplace(Setting.SpeedLevel, i);
|
|
}
|
|
|
|
// Migration code for GMS1.5, TODO remove in 1.6
|
|
PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
|
if (MovementStates.Num() > 0)
|
|
{
|
|
const FGMS_MovementStateSetting& LastMovementState = MovementStates[MovementStates.Num() - 1];
|
|
|
|
// migrate velocity direction setting.
|
|
if (!VelocityDirectionSetting.IsValid())
|
|
{
|
|
if (LastMovementState.VelocityDirectionSetting.DirectionMode != EGMS_VelocityDirectionMode_DEPRECATED::TurningCircle)
|
|
{
|
|
FGMS_VelocityDirectionSetting_Default Temp;
|
|
Temp.bEnableRotationWhenNotMoving = LastMovementState.VelocityDirectionSetting.bEnableRotationWhenNotMoving;
|
|
Temp.TargetYawAngleRotationSpeed = LastMovementState.TargetYawAngleRotationSpeed;
|
|
Temp.RotationInterpolationSpeed = MigrateRotationInterpolationSpeed(LastMovementState.RotationInterpolationSpeed);
|
|
Temp.bOrientateToMoveInputIntent = LastMovementState.VelocityDirectionSetting.DirectionMode == EGMS_VelocityDirectionMode_DEPRECATED::OrientToInputDirection;
|
|
|
|
VelocityDirectionSetting.InitializeAs<FGMS_VelocityDirectionSetting_Default>(Temp);
|
|
}
|
|
else if (LastMovementState.VelocityDirectionSetting.DirectionMode == EGMS_VelocityDirectionMode_DEPRECATED::TurningCircle)
|
|
{
|
|
FGMS_VelocityDirectionSetting_RateBased Temp;
|
|
Temp.bEnableRotationWhenNotMoving = LastMovementState.VelocityDirectionSetting.bEnableRotationWhenNotMoving;
|
|
Temp.TurnRate = LastMovementState.VelocityDirectionSetting.TurningRate;
|
|
VelocityDirectionSetting.InitializeAs<FGMS_VelocityDirectionSetting_RateBased>(Temp);
|
|
}
|
|
}
|
|
|
|
// migrate view direction setting.
|
|
if (!ViewDirectionSetting.IsValid())
|
|
{
|
|
if (LastMovementState.ViewDirectionSetting.DirectionMode == EGMS_ViewDirectionMode_DEPRECATED::Aiming)
|
|
{
|
|
FGMS_ViewDirectionSetting_Aiming Temp;
|
|
Temp.bEnableRotationWhenNotMoving = LastMovementState.ViewDirectionSetting.bRotateToViewDirectionWhileNotMoving;
|
|
Temp.TargetYawAngleRotationSpeed = LastMovementState.TargetYawAngleRotationSpeed;
|
|
Temp.RotationInterpolationSpeed = MigrateRotationInterpolationSpeed(LastMovementState.RotationInterpolationSpeed);
|
|
Temp.MinAimingYawAngleLimit = LastMovementState.ViewDirectionSetting.MinAimingYawAngleLimit;
|
|
|
|
ViewDirectionSetting.InitializeAs<FGMS_ViewDirectionSetting_Aiming>(Temp);
|
|
}
|
|
else if (LastMovementState.ViewDirectionSetting.DirectionMode == EGMS_ViewDirectionMode_DEPRECATED::Default)
|
|
{
|
|
FGMS_ViewDirectionSetting_Default Temp;
|
|
Temp.bEnableRotationWhenNotMoving = LastMovementState.ViewDirectionSetting.bRotateToViewDirectionWhileNotMoving;
|
|
Temp.TargetYawAngleRotationSpeed = LastMovementState.TargetYawAngleRotationSpeed;
|
|
Temp.RotationInterpolationSpeed = MigrateRotationInterpolationSpeed(LastMovementState.RotationInterpolationSpeed);
|
|
ViewDirectionSetting.InitializeAs<FGMS_ViewDirectionSetting_Default>(Temp);
|
|
}
|
|
}
|
|
}
|
|
PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
|
|
|
// Safety guard if still invalid.
|
|
if (!VelocityDirectionSetting.IsValid())
|
|
{
|
|
VelocityDirectionSetting.InitializeAs(FGMS_VelocityDirectionSetting_Default());
|
|
}
|
|
if (!ViewDirectionSetting.IsValid())
|
|
{
|
|
ViewDirectionSetting.InitializeAs(FGMS_ViewDirectionSetting_Default());
|
|
}
|
|
}
|
|
|
|
EDataValidationResult UGMS_MovementControlSetting_Default::IsDataValid(class FDataValidationContext& Context) const
|
|
{
|
|
for (int32 i = 0; i < MovementStates.Num(); i++)
|
|
{
|
|
const FGMS_MovementStateSetting& MRSetting = MovementStates[i];
|
|
if (!MRSetting.Tag.IsValid())
|
|
{
|
|
Context.AddError(FText::FromString(FString::Format(TEXT("Invalid tag at index({0}) of MovementStates"), {i})));
|
|
return EDataValidationResult::Invalid;
|
|
}
|
|
if (MRSetting.AllowedRotationModes.IsEmpty())
|
|
{
|
|
Context.AddError(FText::FromString(
|
|
FString::Format(TEXT("AllowedRotationModes at index({0}) of MovementStates can't be empty!"), {i})));
|
|
return EDataValidationResult::Invalid;
|
|
}
|
|
}
|
|
if (!ViewDirectionSetting.IsValid())
|
|
{
|
|
Context.AddError(FText::FromString(TEXT("Invalid view direction setting")));
|
|
return EDataValidationResult::Invalid;
|
|
}
|
|
if (ViewDirectionSetting.IsValid() && ViewDirectionSetting.GetScriptStruct() == FGMS_ViewDirectionSetting::StaticStruct())
|
|
{
|
|
Context.AddError(FText::FromString(FString::Format(TEXT("View direction setting({0}) was deprecated!"), {FGMS_ViewDirectionSetting::StaticStruct()->GetName()})));
|
|
return EDataValidationResult::Invalid;
|
|
}
|
|
|
|
if (!VelocityDirectionSetting.IsValid())
|
|
{
|
|
Context.AddError(FText::FromString(TEXT("Invalid velocity direction setting")));
|
|
return EDataValidationResult::Invalid;
|
|
}
|
|
if (VelocityDirectionSetting.IsValid() && VelocityDirectionSetting.GetScriptStruct() == FGMS_VelocityDirectionSetting::StaticStruct())
|
|
{
|
|
Context.AddError(FText::FromString(FString::Format(TEXT("Velocity direction setting({0}) was deprecated!"), {FGMS_VelocityDirectionSetting::StaticStruct()->GetName()})));
|
|
return EDataValidationResult::Invalid;
|
|
}
|
|
|
|
return Super::IsDataValid(Context);
|
|
}
|
|
#endif
|
|
|
|
#pragma endregion
|