第一次提交
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "GMS_Constants.generated.h"
|
||||
|
||||
UCLASS(Meta = (BlueprintThreadSafe))
|
||||
class GENERICMOVEMENTSYSTEM_API UGMS_Constants : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="GMS|Constants|Animation Slots", Meta = (ReturnDisplayName = "Slot Name"))
|
||||
static const FName& TurnInPlaceSlotName();
|
||||
|
||||
// Other Animation Curves
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="GMS|Constants|Animation Curves", Meta = (ReturnDisplayName = "Curve Name"))
|
||||
static const FName& RotationYawSpeedCurveName();
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="GMS|Constants|Animation Curves", Meta = (ReturnDisplayName = "Curve Name"))
|
||||
static const FName& RotationYawOffsetCurveName();
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="GMS|Constants|Animation Curves", Meta = (ReturnDisplayName = "Curve Name"))
|
||||
static const FName& AllowTurnInPlaceCurveName();
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="GMS|Constants|Animation Curves", Meta = (ReturnDisplayName = "Curve Name"))
|
||||
static const FName& AllowAimingCurveName();
|
||||
};
|
||||
|
||||
inline const FName& UGMS_Constants::TurnInPlaceSlotName()
|
||||
{
|
||||
static const FName Name{TEXTVIEW("TurnInPlace")};
|
||||
return Name;
|
||||
}
|
||||
|
||||
inline const FName& UGMS_Constants::RotationYawSpeedCurveName()
|
||||
{
|
||||
static const FName Name{TEXTVIEW("RotationYawSpeed")};
|
||||
return Name;
|
||||
}
|
||||
|
||||
inline const FName& UGMS_Constants::RotationYawOffsetCurveName()
|
||||
{
|
||||
static const FName Name{TEXTVIEW("RotationYawOffset")};
|
||||
return Name;
|
||||
}
|
||||
|
||||
inline const FName& UGMS_Constants::AllowTurnInPlaceCurveName()
|
||||
{
|
||||
static const FName Name{TEXTVIEW("AllowTurnInPlace")};
|
||||
return Name;
|
||||
}
|
||||
|
||||
inline const FName& UGMS_Constants::AllowAimingCurveName()
|
||||
{
|
||||
static const FName Name{TEXTVIEW("AllowAiming")};
|
||||
return Name;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Logging/LogMacros.h"
|
||||
|
||||
namespace GMSLog
|
||||
{
|
||||
GENERICMOVEMENTSYSTEM_API extern const FName MessageLogName;
|
||||
}
|
||||
|
||||
DECLARE_STATS_GROUP(TEXT("GMS"), STATGROUP_GMS, STATCAT_Advanced)
|
||||
|
||||
FString GetGMSLogContextString(const UObject* ContextObject = nullptr);
|
||||
|
||||
GENERICMOVEMENTSYSTEM_API DECLARE_LOG_CATEGORY_EXTERN(LogGMS, Log, All)
|
||||
|
||||
GENERICMOVEMENTSYSTEM_API DECLARE_LOG_CATEGORY_EXTERN(LogGMS_Animation, Log, All)
|
||||
|
||||
#define GMS_LOG(Verbosity, Format, ...) \
|
||||
{ \
|
||||
UE_LOG(LogGMS, Verbosity, TEXT("%S: %s"),__FUNCTION__, *FString::Printf(TEXT(Format), ##__VA_ARGS__)) \
|
||||
}
|
||||
|
||||
#define GMS_ANIMATION_LOG(Verbosity, Format, ...) \
|
||||
{ \
|
||||
UE_LOG(LogGMS_Animation, Verbosity, TEXT("%S: %s"),__FUNCTION__, *FString::Printf(TEXT(Format), ##__VA_ARGS__)) \
|
||||
}
|
||||
|
||||
#define GMS_CLOG(Verbosity, Format, ...) \
|
||||
{ \
|
||||
UE_LOG(LogGMS, Verbosity, TEXT("%S: ctx(%s) %s"),__FUNCTION__, *GetGMSLogContextString(this), *FString::Printf(TEXT(Format), ##__VA_ARGS__)) \
|
||||
}
|
||||
|
||||
#define GMS_ANIMATION_CLOG(Verbosity, Format, ...) \
|
||||
{ \
|
||||
UE_LOG(LogGMS_Animation, Verbosity, TEXT("%S: ctx(%s) %s"),__FUNCTION__, *GetGMSLogContextString(this), *FString::Printf(TEXT(Format), ##__VA_ARGS__)) \
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "GMS_Math.generated.h"
|
||||
|
||||
UCLASS(Meta = (BlueprintThreadSafe))
|
||||
class GENERICMOVEMENTSYSTEM_API UGMS_Math : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
static constexpr auto Ln2{0.6931471805599453f}; // FMath::Loge(2.0f).
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GMS|Math Utility", Meta = (ReturnDisplayName = "Value"))
|
||||
static float Clamp01(float Value);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GMS|Math Utility", Meta = (ReturnDisplayName = "Value"))
|
||||
static float LerpClamped(float From, float To, float Ratio);
|
||||
|
||||
//Output the frame-rate stable alpha used for smoothly lerp current to target.
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GMS|Math Utility", Meta = (ReturnDisplayName = "Alpha"))
|
||||
static float DamperExactAlpha(float DeltaTime, float HalfLife);
|
||||
|
||||
// HalfLife is the time it takes for the distance to the target to be reduced by half.
|
||||
template <typename ValueType>
|
||||
static ValueType DamperExact(const ValueType& Current, const ValueType& Target, float DeltaTime, float HalfLife);
|
||||
};
|
||||
|
||||
inline float UGMS_Math::Clamp01(const float Value)
|
||||
{
|
||||
return Value > 0.0f
|
||||
? Value < 1.0f
|
||||
? Value
|
||||
: 1.0f
|
||||
: 0.0f;
|
||||
}
|
||||
|
||||
inline float UGMS_Math::LerpClamped(const float From, const float To, const float Ratio)
|
||||
{
|
||||
return From + (To - From) * Clamp01(Ratio);
|
||||
}
|
||||
|
||||
inline float UGMS_Math::DamperExactAlpha(const float DeltaTime, const float HalfLife)
|
||||
{
|
||||
// https://theorangeduck.com/page/spring-roll-call#exactdamper
|
||||
|
||||
return 1.0f - FMath::InvExpApprox(Ln2 / (HalfLife + UE_SMALL_NUMBER) * DeltaTime);
|
||||
}
|
||||
|
||||
template <typename ValueType>
|
||||
ValueType UGMS_Math::DamperExact(const ValueType& Current, const ValueType& Target, const float DeltaTime, const float HalfLife)
|
||||
{
|
||||
return FMath::Lerp(Current, Target, DamperExactAlpha(DeltaTime, HalfLife));
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GMS_Math.h"
|
||||
#include "GMS_Rotation.generated.h"
|
||||
|
||||
UCLASS(Meta = (BlueprintThreadSafe))
|
||||
class GENERICMOVEMENTSYSTEM_API UGMS_Rotation : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
static constexpr auto CounterClockwiseRotationAngleThreshold{5.0f};
|
||||
|
||||
public:
|
||||
template <typename ValueType> requires std::is_floating_point_v<ValueType>
|
||||
static constexpr ValueType RemapAngleForCounterClockwiseRotation(ValueType Angle);
|
||||
|
||||
static VectorRegister4Double RemapRotationForCounterClockwiseRotation(const VectorRegister4Double& Rotation);
|
||||
|
||||
// Remaps the angle from the [175, 180] range to [-185, -180]. Used to
|
||||
// make the character rotate counterclockwise during a 180 degree turn.
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GMS|Rotation Utility", Meta = (ReturnDisplayName = "Angle"))
|
||||
static float RemapAngleForCounterClockwiseRotation(float Angle);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GMS|Rotation Utility", Meta = (ReturnDisplayName = "Angle"))
|
||||
static float LerpAngle(float From, float To, float Ratio);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GMS|Rotation Utility", Meta = (AutoCreateRefTerm = "From, To", ReturnDisplayName = "Rotation"))
|
||||
static FRotator LerpRotation(const FRotator& From, const FRotator& To, float Ratio);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GMS|Rotation Utility", Meta = (ReturnDisplayName = "Angle"))
|
||||
static float InterpolateAngleConstant(float Current, float Target, float DeltaTime, float Speed);
|
||||
|
||||
/**
|
||||
* Smoothly lerp current to target in fame-rate stable way.
|
||||
* 以帧率稳定方式平滑地从Current过渡到Target。
|
||||
* @param HalfLife How fast to reach target.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GMS|Rotation Utility", Meta = (ReturnDisplayName = "Angle"))
|
||||
static float DamperExactAngle(float Current, float Target, float DeltaTime, float HalfLife);
|
||||
|
||||
// HalfLife is the time it takes for the distance to the target to be reduced by half.
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GMS|Rotation Utility",
|
||||
Meta = (AutoCreateRefTerm = "Current, Target", ReturnDisplayName = "Rotation"))
|
||||
static FRotator DamperExactRotation(const FRotator& Current, const FRotator& Target, float DeltaTime, float HalfLife);
|
||||
|
||||
// Same as FMath::QInterpTo(), but uses FQuat::FastLerp() instead of FQuat::Slerp().
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GMS|Rotation Utility", Meta = (ReturnDisplayName = "Quaternion"))
|
||||
static FQuat InterpolateQuaternionFast(const FQuat& Current, const FQuat& Target, float DeltaTime, float Speed);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GMS|Rotation Utility", Meta = (AutoCreateRefTerm = "TwistAxis", ReturnDisplayName = "Twist"))
|
||||
static FQuat GetTwist(const FQuat& Quaternion, const FVector& TwistAxis = FVector::UpVector);
|
||||
};
|
||||
|
||||
template <typename ValueType> requires std::is_floating_point_v<ValueType>
|
||||
constexpr ValueType UGMS_Rotation::RemapAngleForCounterClockwiseRotation(const ValueType Angle)
|
||||
{
|
||||
if (Angle > 180.0f - CounterClockwiseRotationAngleThreshold)
|
||||
{
|
||||
return Angle - 360.0f;
|
||||
}
|
||||
|
||||
return Angle;
|
||||
}
|
||||
|
||||
inline VectorRegister4Double UGMS_Rotation::RemapRotationForCounterClockwiseRotation(const VectorRegister4Double& Rotation)
|
||||
{
|
||||
static constexpr auto RemapThreshold{
|
||||
MakeVectorRegisterDoubleConstant(180.0f - CounterClockwiseRotationAngleThreshold, 180.0f - CounterClockwiseRotationAngleThreshold,
|
||||
180.0f - CounterClockwiseRotationAngleThreshold, 180.0f - CounterClockwiseRotationAngleThreshold)
|
||||
};
|
||||
|
||||
static constexpr auto RemapAngles{MakeVectorRegisterDoubleConstant(360.0f, 360.0f, 360.0f, 0.0f)};
|
||||
|
||||
const auto ReverseRotationMask{VectorCompareGE(Rotation, RemapThreshold)};
|
||||
|
||||
const auto ReversedRotation{VectorSubtract(Rotation, RemapAngles)};
|
||||
|
||||
return VectorSelect(ReverseRotationMask, ReversedRotation, Rotation);
|
||||
}
|
||||
|
||||
inline float UGMS_Rotation::RemapAngleForCounterClockwiseRotation(const float Angle)
|
||||
{
|
||||
return RemapAngleForCounterClockwiseRotation<float>(Angle);
|
||||
}
|
||||
|
||||
inline float UGMS_Rotation::LerpAngle(const float From, const float To, const float Ratio)
|
||||
{
|
||||
auto Delta{FMath::UnwindDegrees(To - From)};
|
||||
Delta = RemapAngleForCounterClockwiseRotation(Delta);
|
||||
|
||||
return FMath::UnwindDegrees(From + Delta * Ratio);
|
||||
}
|
||||
|
||||
inline FRotator UGMS_Rotation::LerpRotation(const FRotator& From, const FRotator& To, const float Ratio)
|
||||
{
|
||||
#if PLATFORM_ENABLE_VECTORINTRINSICS
|
||||
const auto FromRegister{VectorLoadFloat3_W0(&From)};
|
||||
const auto ToRegister{VectorLoadFloat3_W0(&To)};
|
||||
|
||||
auto Delta{VectorSubtract(ToRegister, FromRegister)};
|
||||
Delta = VectorNormalizeRotator(Delta);
|
||||
|
||||
if (!VectorAnyGreaterThan(VectorAbs(Delta), GlobalVectorConstants::DoubleKindaSmallNumber))
|
||||
{
|
||||
return To;
|
||||
}
|
||||
|
||||
Delta = RemapRotationForCounterClockwiseRotation(Delta);
|
||||
|
||||
auto ResultRegister{VectorMultiplyAdd(Delta, VectorLoadFloat1(&Ratio), FromRegister)};
|
||||
ResultRegister = VectorNormalizeRotator(ResultRegister);
|
||||
|
||||
FRotator Result;
|
||||
VectorStoreFloat3(ResultRegister, &Result);
|
||||
|
||||
return Result;
|
||||
#else
|
||||
auto Result{To - From};
|
||||
Result.Normalize();
|
||||
|
||||
Result.Pitch = RemapAngleForCounterClockwiseRotation(Result.Pitch);
|
||||
Result.Yaw = RemapAngleForCounterClockwiseRotation(Result.Yaw);
|
||||
Result.Roll = RemapAngleForCounterClockwiseRotation(Result.Roll);
|
||||
|
||||
Result *= Ratio;
|
||||
Result += From;
|
||||
Result.Normalize();
|
||||
|
||||
return Result;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline float UGMS_Rotation::InterpolateAngleConstant(const float Current, const float Target, const float DeltaTime, const float Speed)
|
||||
{
|
||||
auto Delta{FMath::UnwindDegrees(Target - Current)};
|
||||
const auto MaxDelta{Speed * DeltaTime};
|
||||
|
||||
if (Speed <= 0.0f || FMath::Abs(Delta) <= MaxDelta)
|
||||
{
|
||||
return Target;
|
||||
}
|
||||
|
||||
Delta = RemapAngleForCounterClockwiseRotation(Delta);
|
||||
return FMath::UnwindDegrees(Current + FMath::Sign(Delta) * MaxDelta);
|
||||
}
|
||||
|
||||
inline float UGMS_Rotation::DamperExactAngle(const float Current, const float Target, const float DeltaTime, const float HalfLife)
|
||||
{
|
||||
auto Delta{FMath::UnwindDegrees(Target - Current)};
|
||||
|
||||
if (FMath::IsNearlyZero(Delta, UE_KINDA_SMALL_NUMBER))
|
||||
{
|
||||
return Target;
|
||||
}
|
||||
|
||||
Delta = RemapAngleForCounterClockwiseRotation(Delta);
|
||||
|
||||
const auto Alpha{UGMS_Math::DamperExactAlpha(DeltaTime, HalfLife)};
|
||||
return FMath::UnwindDegrees(Current + Delta * Alpha);
|
||||
}
|
||||
|
||||
inline FRotator UGMS_Rotation::DamperExactRotation(const FRotator& Current, const FRotator& Target,
|
||||
const float DeltaTime, const float HalfLife)
|
||||
{
|
||||
#if PLATFORM_ENABLE_VECTORINTRINSICS
|
||||
const auto CurrentRegister{VectorLoadFloat3_W0(&Current)};
|
||||
const auto TargetRegister{VectorLoadFloat3_W0(&Target)};
|
||||
|
||||
auto Delta{VectorSubtract(TargetRegister, CurrentRegister)};
|
||||
Delta = VectorNormalizeRotator(Delta);
|
||||
|
||||
if (!VectorAnyGreaterThan(VectorAbs(Delta), GlobalVectorConstants::DoubleKindaSmallNumber))
|
||||
{
|
||||
return Target;
|
||||
}
|
||||
|
||||
Delta = RemapRotationForCounterClockwiseRotation(Delta);
|
||||
|
||||
const double Alpha{UGMS_Math::DamperExactAlpha(DeltaTime, HalfLife)};
|
||||
|
||||
auto ResultRegister{VectorMultiplyAdd(Delta, VectorLoadDouble1(&Alpha), CurrentRegister)};
|
||||
ResultRegister = VectorNormalizeRotator(ResultRegister);
|
||||
|
||||
FRotator Result;
|
||||
VectorStoreFloat3(ResultRegister, &Result);
|
||||
|
||||
return Result;
|
||||
#else
|
||||
auto Result{Target - Current};
|
||||
Result.Normalize();
|
||||
|
||||
if (FMath::IsNearlyZero(Result.Pitch, UE_KINDA_SMALL_NUMBER) &&
|
||||
FMath::IsNearlyZero(Result.Yaw, UE_KINDA_SMALL_NUMBER) &&
|
||||
FMath::IsNearlyZero(Result.Roll, UE_KINDA_SMALL_NUMBER))
|
||||
{
|
||||
return Target;
|
||||
}
|
||||
|
||||
Result.Pitch = RemapAngleForCounterClockwiseRotation(Result.Pitch);
|
||||
Result.Yaw = RemapAngleForCounterClockwiseRotation(Result.Yaw);
|
||||
Result.Roll = RemapAngleForCounterClockwiseRotation(Result.Roll);
|
||||
|
||||
const auto Alpha{UGMS_Math::DamperExactAlpha(DeltaTime, HalfLife)};
|
||||
|
||||
Result *= Alpha;
|
||||
Result += Current;
|
||||
Result.Normalize();
|
||||
|
||||
return Result;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline FQuat UGMS_Rotation::InterpolateQuaternionFast(const FQuat& Current, const FQuat& Target, const float DeltaTime, const float Speed)
|
||||
{
|
||||
if (Speed <= 0.0f || Current.Equals(Target))
|
||||
{
|
||||
return Target;
|
||||
}
|
||||
|
||||
return FQuat::FastLerp(Current, Target, UGMS_Math::Clamp01(Speed * DeltaTime)).GetNormalized();
|
||||
}
|
||||
|
||||
inline FQuat UGMS_Rotation::GetTwist(const FQuat& Quaternion, const FVector& TwistAxis)
|
||||
{
|
||||
// Based on TQuat<T>::ToSwingTwist().
|
||||
|
||||
const auto Projection{(TwistAxis | FVector{Quaternion.X, Quaternion.Y, Quaternion.Z}) * TwistAxis};
|
||||
|
||||
return FQuat{Projection.X, Projection.Y, Projection.Z, Quaternion.W}.GetNormalized();
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "NativeGameplayTags.h"
|
||||
|
||||
namespace GMS_MovementModeTags
|
||||
{
|
||||
GENERICMOVEMENTSYSTEM_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(None)
|
||||
GENERICMOVEMENTSYSTEM_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Grounded)
|
||||
GENERICMOVEMENTSYSTEM_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InAir)
|
||||
GENERICMOVEMENTSYSTEM_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Flying)
|
||||
GENERICMOVEMENTSYSTEM_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Swimming)
|
||||
GENERICMOVEMENTSYSTEM_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Zipline)
|
||||
}
|
||||
|
||||
namespace GMS_RotationModeTags
|
||||
{
|
||||
GENERICMOVEMENTSYSTEM_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(VelocityDirection)
|
||||
GENERICMOVEMENTSYSTEM_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(ViewDirection)
|
||||
}
|
||||
|
||||
namespace GMS_MovementStateTags
|
||||
{
|
||||
GENERICMOVEMENTSYSTEM_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Walk)
|
||||
GENERICMOVEMENTSYSTEM_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Jog)
|
||||
GENERICMOVEMENTSYSTEM_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Sprint)
|
||||
}
|
||||
|
||||
namespace GMS_OverlayModeTags
|
||||
{
|
||||
GENERICMOVEMENTSYSTEM_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(None)
|
||||
GENERICMOVEMENTSYSTEM_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Default)
|
||||
}
|
||||
|
||||
namespace GMS_SMTags
|
||||
{
|
||||
GENERICMOVEMENTSYSTEM_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Root)
|
||||
GENERICMOVEMENTSYSTEM_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InAir)
|
||||
GENERICMOVEMENTSYSTEM_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InAir_Jump)
|
||||
GENERICMOVEMENTSYSTEM_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InAir_Fall)
|
||||
GENERICMOVEMENTSYSTEM_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Grounded)
|
||||
GENERICMOVEMENTSYSTEM_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Grounded_Idle)
|
||||
GENERICMOVEMENTSYSTEM_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Grounded_Start)
|
||||
GENERICMOVEMENTSYSTEM_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Grounded_Cycle)
|
||||
GENERICMOVEMENTSYSTEM_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Grounded_Stop)
|
||||
GENERICMOVEMENTSYSTEM_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Grounded_Pivot)
|
||||
GENERICMOVEMENTSYSTEM_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Grounded_Land)
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "Locomotions/GMS_LocomotionStructLibrary.h"
|
||||
#include "Settings/GMS_SettingStructLibrary.h"
|
||||
#include "GMS_Utility.generated.h"
|
||||
|
||||
class UGMS_MovementSetUserSetting;
|
||||
class UGMS_AnimLayer;
|
||||
class UGMS_MainAnimInstance;
|
||||
class UChooserTable;
|
||||
class UPoseSearchDatabase;
|
||||
class UAnimSequenceBase;
|
||||
|
||||
|
||||
UCLASS()
|
||||
class GENERICMOVEMENTSYSTEM_API UGMS_Utility : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
static constexpr auto DrawImpactPointSize{32.0f};
|
||||
static constexpr auto DrawLineThickness{1.0f};
|
||||
static constexpr auto DrawArrowSize{50.0f};
|
||||
static constexpr auto DrawCircleSidesCount{16};
|
||||
|
||||
static constexpr FStringView BoolToString(bool bValue);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="GMS|Utility", Meta = (AutoCreateRefTerm = "Name", ReturnDisplayName = "Display String"))
|
||||
static FString NameToDisplayString(const FName& Name, bool bNameIsBool);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="GMS|Utility",
|
||||
Meta = (DefaultToSelf = "Character", AutoCreateRefTerm = "CurveName", ReturnDisplayName = "Curve Value"))
|
||||
static float GetAnimationCurveValueFromCharacter(const ACharacter* Character, const FName& CurveName);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="GMS|Utility", Meta = (AutoCreateRefTerm = "Tag", ReturnDisplayName = "Child Tags"))
|
||||
static FGameplayTagContainer GetChildTags(const FGameplayTag& Tag);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="GMS|Utility", Meta = (AutoCreateRefTerm = "Tag", ReturnDisplayName = "Tag Name"))
|
||||
static FName GetSimpleTagName(const FGameplayTag& Tag);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="GMS|Utility",
|
||||
Meta = (DefaultToSelf = "Actor", AutoCreateRefTerm = "DisplayName", ReturnDisplayName = "Value"))
|
||||
static bool ShouldDisplayDebugForActor(const AActor* Actor, const FName& DisplayName);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="GMS|Animation", meta=(BlueprintThreadSafe))
|
||||
static float CalculateAnimatedSpeed(const UAnimSequenceBase* AnimSequence);
|
||||
|
||||
/**
|
||||
* @param Animations List of Animations to select.
|
||||
* @param ReferenceValue The animation with distance closest to ReferenceValue will be selected.
|
||||
* @return Selected AnimSequence.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GMS|Animation", meta=(BlueprintThreadSafe))
|
||||
static UAnimSequence* SelectAnimationWithFloat(const TArray<FGMS_AnimationWithDistance>& Animations, const float& ReferenceValue);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="GMS|Animation", meta=(BlueprintThreadSafe))
|
||||
static bool ValidatePoseSearchDatabasesChooser(const UChooserTable* ChooserTable, FText& OutMessage);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="GMS|Animation", meta=(BlueprintThreadSafe))
|
||||
static bool IsValidPoseSearchDatabasesChooser(const UChooserTable* ChooserTable);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure=false, Category="GMS|Animation", meta=(BlueprintThreadSafe))
|
||||
static TArray<UPoseSearchDatabase*> EvaluatePoseSearchDatabasesChooser(const UGMS_MainAnimInstance* MainAnimInstance, const UGMS_AnimLayer* AnimLayerInstance, UChooserTable* ChooserTable);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="GMS|Utility", meta=(BlueprintThreadSafe, DeterminesOutputType=DesiredClass, DynamicOutputParam="ReturnValue"))
|
||||
static const UGMS_MovementSetUserSetting* GetMovementSetUserSetting(const FGMS_MovementSetSetting& MovementSetSetting, TSubclassOf<UGMS_MovementSetUserSetting> DesiredClass);
|
||||
};
|
||||
|
||||
constexpr FStringView UGMS_Utility::BoolToString(const bool bValue)
|
||||
{
|
||||
return bValue ? TEXTVIEW("True") : TEXTVIEW("False");
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "GMS_Vector.generated.h"
|
||||
|
||||
UCLASS(Meta = (BlueprintThreadSafe))
|
||||
class GENERICMOVEMENTSYSTEM_API UGMS_Vector : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GMS|Vector Utility", Meta = (AutoCreateRefTerm = "Vector", ReturnDisplayName = "Vector"))
|
||||
static FVector ClampMagnitude01(const FVector& Vector);
|
||||
|
||||
static FVector3f ClampMagnitude01(const FVector3f& Vector);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GMS|Vector Utility", DisplayName = "Clamp Magnitude 01 2D",
|
||||
Meta = (AutoCreateRefTerm = "Vector", ReturnDisplayName = "Vector"))
|
||||
static FVector2D ClampMagnitude012D(const FVector2D& Vector);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GMS|Vector Utility", Meta = (ReturnDisplayName = "Direction"))
|
||||
static FVector2D RadianToDirection(float Radian);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GMS|Vector Utility", Meta = (ReturnDisplayName = "Direction"))
|
||||
static FVector RadianToDirectionXY(float Radian);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GMS|Vector Utility", Meta = (ReturnDisplayName = "Direction"))
|
||||
static FVector2D AngleToDirection(float Angle);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GMS|Vector Utility", Meta = (ReturnDisplayName = "Direction"))
|
||||
static FVector AngleToDirectionXY(float Angle);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GMS|Vector Utility", Meta = (AutoCreateRefTerm = "Direction", ReturnDisplayName = "Angle"))
|
||||
static double DirectionToAngle(const FVector2D& Direction);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GMS|Vector Utility", Meta = (AutoCreateRefTerm = "Direction", ReturnDisplayName = "Angle"))
|
||||
static double DirectionToAngleXY(const FVector& Direction);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GMS|Vector Utility", Meta = (AutoCreateRefTerm = "Vector", ReturnDisplayName = "Vector"))
|
||||
static FVector PerpendicularClockwiseXY(const FVector& Vector);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GMS|Vector Utility", Meta = (AutoCreateRefTerm = "Vector", ReturnDisplayName = "Vector"))
|
||||
static FVector PerpendicularCounterClockwiseXY(const FVector& Vector);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GMS|Vector Utility", DisplayName = "Angle Between (Skip Normalization)",
|
||||
Meta = (AutoCreateRefTerm = "From, To", ReturnDisplayName = "Angle"))
|
||||
static double AngleBetweenSkipNormalization(const FVector& From, const FVector& To);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GMS|Vector Utility", Meta = (AutoCreateRefTerm = "From, To", ReturnDisplayName = "Angle"))
|
||||
static float AngleBetweenSignedXY(const FVector3f& From, const FVector3f& To);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GMS|Vector Utility", DisplayName = "Slerp (Skip Normalization)",
|
||||
Meta = (AutoCreateRefTerm = "From, To", ReturnDisplayName = "Direction"))
|
||||
static FVector SlerpSkipNormalization(const FVector& From, const FVector& To, float Ratio);
|
||||
};
|
||||
|
||||
inline FVector UGMS_Vector::ClampMagnitude01(const FVector& Vector)
|
||||
{
|
||||
const auto MagnitudeSquared{Vector.SizeSquared()};
|
||||
|
||||
if (MagnitudeSquared <= 1.0f)
|
||||
{
|
||||
return Vector;
|
||||
}
|
||||
|
||||
const auto Scale{FMath::InvSqrt(MagnitudeSquared)};
|
||||
|
||||
return {Vector.X * Scale, Vector.Y * Scale, Vector.Z * Scale};
|
||||
}
|
||||
|
||||
inline FVector3f UGMS_Vector::ClampMagnitude01(const FVector3f& Vector)
|
||||
{
|
||||
const auto MagnitudeSquared{Vector.SizeSquared()};
|
||||
|
||||
if (MagnitudeSquared <= 1.0f)
|
||||
{
|
||||
return Vector;
|
||||
}
|
||||
|
||||
const auto Scale{FMath::InvSqrt(MagnitudeSquared)};
|
||||
|
||||
return {Vector.X * Scale, Vector.Y * Scale, Vector.Z * Scale};
|
||||
}
|
||||
|
||||
inline FVector2D UGMS_Vector::ClampMagnitude012D(const FVector2D& Vector)
|
||||
{
|
||||
const auto MagnitudeSquared{Vector.SizeSquared()};
|
||||
|
||||
if (MagnitudeSquared <= 1.0f)
|
||||
{
|
||||
return Vector;
|
||||
}
|
||||
|
||||
const auto Scale{FMath::InvSqrt(MagnitudeSquared)};
|
||||
|
||||
return {Vector.X * Scale, Vector.Y * Scale};
|
||||
}
|
||||
|
||||
inline FVector2D UGMS_Vector::RadianToDirection(const float Radian)
|
||||
{
|
||||
float Sin, Cos;
|
||||
FMath::SinCos(&Sin, &Cos, Radian);
|
||||
|
||||
return {Cos, Sin};
|
||||
}
|
||||
|
||||
inline FVector UGMS_Vector::RadianToDirectionXY(const float Radian)
|
||||
{
|
||||
float Sin, Cos;
|
||||
FMath::SinCos(&Sin, &Cos, Radian);
|
||||
|
||||
return {Cos, Sin, 0.0f};
|
||||
}
|
||||
|
||||
inline FVector2D UGMS_Vector::AngleToDirection(const float Angle)
|
||||
{
|
||||
return RadianToDirection(FMath::DegreesToRadians(Angle));
|
||||
}
|
||||
|
||||
inline FVector UGMS_Vector::AngleToDirectionXY(const float Angle)
|
||||
{
|
||||
return RadianToDirectionXY(FMath::DegreesToRadians(Angle));
|
||||
}
|
||||
|
||||
inline double UGMS_Vector::DirectionToAngle(const FVector2D& Direction)
|
||||
{
|
||||
return FMath::RadiansToDegrees(FMath::Atan2(Direction.Y, Direction.X));
|
||||
}
|
||||
|
||||
inline double UGMS_Vector::DirectionToAngleXY(const FVector& Direction)
|
||||
{
|
||||
return FMath::RadiansToDegrees(FMath::Atan2(Direction.Y, Direction.X));
|
||||
}
|
||||
|
||||
inline FVector UGMS_Vector::PerpendicularClockwiseXY(const FVector& Vector)
|
||||
{
|
||||
return {Vector.Y, -Vector.X, Vector.Z};
|
||||
}
|
||||
|
||||
inline FVector UGMS_Vector::PerpendicularCounterClockwiseXY(const FVector& Vector)
|
||||
{
|
||||
return {-Vector.Y, Vector.X, Vector.Z};
|
||||
}
|
||||
|
||||
inline double UGMS_Vector::AngleBetweenSkipNormalization(const FVector& From, const FVector& To)
|
||||
{
|
||||
return FMath::RadiansToDegrees(FMath::Acos(From | To));
|
||||
}
|
||||
|
||||
inline float UGMS_Vector::AngleBetweenSignedXY(const FVector3f& From, const FVector3f& To)
|
||||
{
|
||||
FVector2f FromXY{From};
|
||||
FromXY.Normalize();
|
||||
|
||||
FVector2f ToXY{To};
|
||||
ToXY.Normalize();
|
||||
|
||||
// return FMath::RadiansToDegrees(FMath::Atan2(FromXY ^ ToXY, FromXY | ToXY));
|
||||
|
||||
return FMath::RadiansToDegrees(FMath::Acos(FromXY | ToXY)) * FMath::Sign(FromXY ^ ToXY);
|
||||
}
|
||||
Reference in New Issue
Block a user