第一次提交

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,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;
}

View File

@@ -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__)) \
}

View File

@@ -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));
}

View File

@@ -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();
}

View File

@@ -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)
}

View File

@@ -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");
}

View File

@@ -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);
}