467 lines
12 KiB
C++
467 lines
12 KiB
C++
|
|
// Copyright (c) 2026 Unreal Directive. Licensed under the MIT License.
|
|||
|
|
|
|||
|
|
|
|||
|
|
#include "Libraries/DirectiveUtilMathFunctionLibrary.h"
|
|||
|
|
|
|||
|
|
namespace
|
|||
|
|
{
|
|||
|
|
double EaseBackIn(double t)
|
|||
|
|
{
|
|||
|
|
const double s = 1.70158;
|
|||
|
|
return t * t * ((s + 1.0) * t - s);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
double EaseBackOut(double t)
|
|||
|
|
{
|
|||
|
|
const double s = 1.70158;
|
|||
|
|
t -= 1.0;
|
|||
|
|
return t * t * ((s + 1.0) * t + s) + 1.0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
double EaseBackInOut(double t)
|
|||
|
|
{
|
|||
|
|
const double s = 1.70158 * 1.525;
|
|||
|
|
t *= 2.0;
|
|||
|
|
if (t < 1.0)
|
|||
|
|
{
|
|||
|
|
return 0.5 * (t * t * ((s + 1.0) * t - s));
|
|||
|
|
}
|
|||
|
|
t -= 2.0;
|
|||
|
|
return 0.5 * (t * t * ((s + 1.0) * t + s) + 2.0);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
double EaseElasticIn(double t)
|
|||
|
|
{
|
|||
|
|
if (t <= 0.0) { return 0.0; }
|
|||
|
|
if (t >= 1.0) { return 1.0; }
|
|||
|
|
const double p = 0.3;
|
|||
|
|
const double s = p / 4.0;
|
|||
|
|
t -= 1.0;
|
|||
|
|
return -(FMath::Pow(2.0, 10.0 * t) * FMath::Sin((t - s) * (2.0 * PI) / p));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
double EaseElasticOut(double t)
|
|||
|
|
{
|
|||
|
|
if (t <= 0.0) { return 0.0; }
|
|||
|
|
if (t >= 1.0) { return 1.0; }
|
|||
|
|
const double p = 0.3;
|
|||
|
|
const double s = p / 4.0;
|
|||
|
|
return FMath::Pow(2.0, -10.0 * t) * FMath::Sin((t - s) * (2.0 * PI) / p) + 1.0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
double EaseElasticInOut(double t)
|
|||
|
|
{
|
|||
|
|
if (t <= 0.0) { return 0.0; }
|
|||
|
|
if (t >= 1.0) { return 1.0; }
|
|||
|
|
const double p = 0.3 * 1.5;
|
|||
|
|
const double s = p / 4.0;
|
|||
|
|
t *= 2.0;
|
|||
|
|
if (t < 1.0)
|
|||
|
|
{
|
|||
|
|
t -= 1.0;
|
|||
|
|
return -0.5 * (FMath::Pow(2.0, 10.0 * t) * FMath::Sin((t - s) * (2.0 * PI) / p));
|
|||
|
|
}
|
|||
|
|
t -= 1.0;
|
|||
|
|
return FMath::Pow(2.0, -10.0 * t) * FMath::Sin((t - s) * (2.0 * PI) / p) * 0.5 + 1.0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
double EaseBounceOut(double t)
|
|||
|
|
{
|
|||
|
|
const double n1 = 7.5625;
|
|||
|
|
const double d1 = 2.75;
|
|||
|
|
if (t < 1.0 / d1)
|
|||
|
|
{
|
|||
|
|
return n1 * t * t;
|
|||
|
|
}
|
|||
|
|
if (t < 2.0 / d1)
|
|||
|
|
{
|
|||
|
|
t -= 1.5 / d1;
|
|||
|
|
return n1 * t * t + 0.75;
|
|||
|
|
}
|
|||
|
|
if (t < 2.5 / d1)
|
|||
|
|
{
|
|||
|
|
t -= 2.25 / d1;
|
|||
|
|
return n1 * t * t + 0.9375;
|
|||
|
|
}
|
|||
|
|
t -= 2.625 / d1;
|
|||
|
|
return n1 * t * t + 0.984375;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
double EaseBounceIn(double t)
|
|||
|
|
{
|
|||
|
|
return 1.0 - EaseBounceOut(1.0 - t);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
double EaseBounceInOut(double t)
|
|||
|
|
{
|
|||
|
|
return t < 0.5
|
|||
|
|
? (1.0 - EaseBounceOut(1.0 - 2.0 * t)) * 0.5
|
|||
|
|
: (1.0 + EaseBounceOut(2.0 * t - 1.0)) * 0.5;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
float UDirectiveUtilMathFunctionLibrary::PerlinNoise2D(const FVector2D Position)
|
|||
|
|
{
|
|||
|
|
return FMath::PerlinNoise2D(Position);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
float UDirectiveUtilMathFunctionLibrary::PerlinNoise3D(const FVector& Position)
|
|||
|
|
{
|
|||
|
|
return FMath::PerlinNoise3D(Position);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
float UDirectiveUtilMathFunctionLibrary::AngleBetweenVectors(const FVector& A, const FVector& B)
|
|||
|
|
{
|
|||
|
|
return FMath::RadiansToDegrees(FMath::Acos(FMath::Clamp(FVector::DotProduct(A.GetSafeNormal(), B.GetSafeNormal()), -1.0, 1.0)));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
float UDirectiveUtilMathFunctionLibrary::EaseAlpha(const float Alpha, const EDirectiveUtilEaseType EaseType)
|
|||
|
|
{
|
|||
|
|
const double t = static_cast<double>(FMath::Clamp(Alpha, 0.0f, 1.0f));
|
|||
|
|
double Result = t;
|
|||
|
|
switch (EaseType)
|
|||
|
|
{
|
|||
|
|
case EDirectiveUtilEaseType::BackIn: Result = EaseBackIn(t); break;
|
|||
|
|
case EDirectiveUtilEaseType::BackOut: Result = EaseBackOut(t); break;
|
|||
|
|
case EDirectiveUtilEaseType::BackInOut: Result = EaseBackInOut(t); break;
|
|||
|
|
case EDirectiveUtilEaseType::ElasticIn: Result = EaseElasticIn(t); break;
|
|||
|
|
case EDirectiveUtilEaseType::ElasticOut: Result = EaseElasticOut(t); break;
|
|||
|
|
case EDirectiveUtilEaseType::ElasticInOut: Result = EaseElasticInOut(t); break;
|
|||
|
|
case EDirectiveUtilEaseType::BounceIn: Result = EaseBounceIn(t); break;
|
|||
|
|
case EDirectiveUtilEaseType::BounceOut: Result = EaseBounceOut(t); break;
|
|||
|
|
case EDirectiveUtilEaseType::BounceInOut: Result = EaseBounceInOut(t); break;
|
|||
|
|
}
|
|||
|
|
return static_cast<float>(Result);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
float UDirectiveUtilMathFunctionLibrary::EaseFloat(const float A, const float B, const float Alpha, const EDirectiveUtilEaseType EaseType)
|
|||
|
|
{
|
|||
|
|
return FMath::Lerp(A, B, EaseAlpha(Alpha, EaseType));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
FVector UDirectiveUtilMathFunctionLibrary::EaseVector(const FVector& A, const FVector& B, const float Alpha, const EDirectiveUtilEaseType EaseType)
|
|||
|
|
{
|
|||
|
|
return FMath::Lerp(A, B, static_cast<double>(EaseAlpha(Alpha, EaseType)));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
FRotator UDirectiveUtilMathFunctionLibrary::EaseRotator(const FRotator& A, const FRotator& B, const float Alpha, const EDirectiveUtilEaseType EaseType)
|
|||
|
|
{
|
|||
|
|
return FQuat::Slerp(A.Quaternion(), B.Quaternion(), EaseAlpha(Alpha, EaseType)).Rotator();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
FLinearColor UDirectiveUtilMathFunctionLibrary::EaseColor(const FLinearColor& A, const FLinearColor& B, const float Alpha, const EDirectiveUtilEaseType EaseType)
|
|||
|
|
{
|
|||
|
|
return FMath::Lerp(A, B, EaseAlpha(Alpha, EaseType));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
float UDirectiveUtilMathFunctionLibrary::RoundToDecimals(const float Value, int32 Decimals)
|
|||
|
|
{
|
|||
|
|
Decimals = FMath::Clamp(Decimals, 0, 10);
|
|||
|
|
if (Decimals == 0)
|
|||
|
|
{
|
|||
|
|
return FMath::RoundHalfFromZero(Value);
|
|||
|
|
}
|
|||
|
|
const double Factor = FMath::Pow(10.0, static_cast<double>(Decimals));
|
|||
|
|
return static_cast<float>(FMath::RoundHalfFromZero(static_cast<double>(Value) * Factor) / Factor);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
FText UDirectiveUtilMathFunctionLibrary::RoundToDecimalsAsText(const float Value, int32 Decimals)
|
|||
|
|
{
|
|||
|
|
Decimals = FMath::Clamp(Decimals, 0, 10);
|
|||
|
|
FNumberFormattingOptions Options;
|
|||
|
|
Options.MinimumFractionalDigits = 0;
|
|||
|
|
Options.MaximumFractionalDigits = Decimals;
|
|||
|
|
Options.RoundingMode = ERoundingMode::HalfFromZero;
|
|||
|
|
return FText::AsNumber(Value, &Options);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
FText UDirectiveUtilMathFunctionLibrary::FormatBytes(const int64 Bytes, int32 Decimals)
|
|||
|
|
{
|
|||
|
|
Decimals = FMath::Clamp(Decimals, 0, 3);
|
|||
|
|
|
|||
|
|
static const TCHAR* Suffixes[] = { TEXT("B"), TEXT("KB"), TEXT("MB"), TEXT("GB"), TEXT("TB"), TEXT("PB") };
|
|||
|
|
const bool bNegative = Bytes < 0;
|
|||
|
|
double Value = FMath::Abs(static_cast<double>(Bytes));
|
|||
|
|
int32 SuffixIndex = 0;
|
|||
|
|
while (Value >= 1024.0 && SuffixIndex < UE_ARRAY_COUNT(Suffixes) - 1)
|
|||
|
|
{
|
|||
|
|
Value /= 1024.0;
|
|||
|
|
++SuffixIndex;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return FText::FromString(FString::Printf(TEXT("%s%.*f %s"),
|
|||
|
|
bNegative ? TEXT("-") : TEXT(""), SuffixIndex == 0 ? 0 : Decimals, Value, Suffixes[SuffixIndex]));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
FText UDirectiveUtilMathFunctionLibrary::FormatDuration(const float Seconds, const bool bIncludeSeconds)
|
|||
|
|
{
|
|||
|
|
if (!FMath::IsFinite(Seconds))
|
|||
|
|
{
|
|||
|
|
return FText::FromString(TEXT("0s"));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const int64 TotalSeconds = static_cast<int64>(FMath::Abs(static_cast<double>(Seconds)));
|
|||
|
|
const bool bNegative = Seconds < 0.0f && TotalSeconds > 0;
|
|||
|
|
|
|||
|
|
const int64 UnitValues[] = { TotalSeconds / 86400, (TotalSeconds / 3600) % 24, (TotalSeconds / 60) % 60, TotalSeconds % 60 };
|
|||
|
|
static const TCHAR* UnitSuffixes[] = { TEXT("d"), TEXT("h"), TEXT("m"), TEXT("s") };
|
|||
|
|
const int32 NumUnits = bIncludeSeconds ? 4 : 3;
|
|||
|
|
|
|||
|
|
int32 FirstUnit = NumUnits - 1;
|
|||
|
|
for (int32 Index = 0; Index < NumUnits; ++Index)
|
|||
|
|
{
|
|||
|
|
if (UnitValues[Index] != 0)
|
|||
|
|
{
|
|||
|
|
FirstUnit = Index;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
int32 LastUnit = FirstUnit;
|
|||
|
|
for (int32 Index = NumUnits - 1; Index >= FirstUnit; --Index)
|
|||
|
|
{
|
|||
|
|
if (UnitValues[Index] != 0)
|
|||
|
|
{
|
|||
|
|
LastUnit = Index;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
FString Result = bNegative ? TEXT("-") : TEXT("");
|
|||
|
|
for (int32 Index = FirstUnit; Index <= LastUnit; ++Index)
|
|||
|
|
{
|
|||
|
|
if (Index == FirstUnit)
|
|||
|
|
{
|
|||
|
|
Result += FString::Printf(TEXT("%lld%s"), UnitValues[Index], UnitSuffixes[Index]);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
Result += FString::Printf(TEXT(" %02lld%s"), UnitValues[Index], UnitSuffixes[Index]);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return FText::FromString(Result);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
FText UDirectiveUtilMathFunctionLibrary::FormatRelativeTime(const FDateTime& Timestamp)
|
|||
|
|
{
|
|||
|
|
const FTimespan Delta = FDateTime::Now() - Timestamp;
|
|||
|
|
const bool bFuture = Delta.GetTicks() < 0;
|
|||
|
|
// Round to whole seconds so clock-adjacent inputs (e.g. Now() + 2 hours) land in the intended bucket.
|
|||
|
|
const int64 SecondsAbs = static_cast<int64>(FMath::RoundToDouble(FMath::Abs(Delta.GetTotalSeconds())));
|
|||
|
|
|
|||
|
|
if (SecondsAbs < 60)
|
|||
|
|
{
|
|||
|
|
return FText::FromString(TEXT("just now"));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int64 Count;
|
|||
|
|
const TCHAR* Unit;
|
|||
|
|
if (SecondsAbs < 3600)
|
|||
|
|
{
|
|||
|
|
Count = SecondsAbs / 60;
|
|||
|
|
Unit = TEXT("minute");
|
|||
|
|
}
|
|||
|
|
else if (SecondsAbs < 86400)
|
|||
|
|
{
|
|||
|
|
Count = SecondsAbs / 3600;
|
|||
|
|
Unit = TEXT("hour");
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
Count = SecondsAbs / 86400;
|
|||
|
|
Unit = TEXT("day");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const FString Quantity = FString::Printf(TEXT("%lld %s%s"), Count, Unit, Count == 1 ? TEXT("") : TEXT("s"));
|
|||
|
|
return FText::FromString(bFuture
|
|||
|
|
? FString::Printf(TEXT("in %s"), *Quantity)
|
|||
|
|
: FString::Printf(TEXT("%s ago"), *Quantity));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int64 UDirectiveUtilMathFunctionLibrary::GetIntArraySum(const TArray<int32>& Values)
|
|||
|
|
{
|
|||
|
|
int64 Sum = 0;
|
|||
|
|
for (const int32 Value : Values)
|
|||
|
|
{
|
|||
|
|
Sum += Value;
|
|||
|
|
}
|
|||
|
|
return Sum;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
float UDirectiveUtilMathFunctionLibrary::GetIntArrayAverage(const TArray<int32>& Values)
|
|||
|
|
{
|
|||
|
|
if (Values.IsEmpty())
|
|||
|
|
{
|
|||
|
|
return 0.0f;
|
|||
|
|
}
|
|||
|
|
return static_cast<float>(static_cast<double>(GetIntArraySum(Values)) / Values.Num());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
float UDirectiveUtilMathFunctionLibrary::GetIntArrayMedian(const TArray<int32>& Values)
|
|||
|
|
{
|
|||
|
|
if (Values.IsEmpty())
|
|||
|
|
{
|
|||
|
|
return 0.0f;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TArray<int32> Sorted = Values;
|
|||
|
|
Sorted.Sort();
|
|||
|
|
|
|||
|
|
const int32 Middle = Sorted.Num() / 2;
|
|||
|
|
if (Sorted.Num() % 2 == 0)
|
|||
|
|
{
|
|||
|
|
return static_cast<float>((static_cast<double>(Sorted[Middle - 1]) + static_cast<double>(Sorted[Middle])) * 0.5);
|
|||
|
|
}
|
|||
|
|
return static_cast<float>(Sorted[Middle]);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
float UDirectiveUtilMathFunctionLibrary::GetIntArrayStandardDeviation(const TArray<int32>& Values)
|
|||
|
|
{
|
|||
|
|
if (Values.IsEmpty())
|
|||
|
|
{
|
|||
|
|
return 0.0f;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const double Mean = static_cast<double>(GetIntArraySum(Values)) / Values.Num();
|
|||
|
|
double SquaredDeltaSum = 0.0;
|
|||
|
|
for (const int32 Value : Values)
|
|||
|
|
{
|
|||
|
|
const double Delta = static_cast<double>(Value) - Mean;
|
|||
|
|
SquaredDeltaSum += Delta * Delta;
|
|||
|
|
}
|
|||
|
|
return static_cast<float>(FMath::Sqrt(SquaredDeltaSum / Values.Num()));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
float UDirectiveUtilMathFunctionLibrary::GetFloatArraySum(const TArray<float>& Values)
|
|||
|
|
{
|
|||
|
|
double Sum = 0.0;
|
|||
|
|
for (const float Value : Values)
|
|||
|
|
{
|
|||
|
|
Sum += static_cast<double>(Value);
|
|||
|
|
}
|
|||
|
|
return static_cast<float>(Sum);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
float UDirectiveUtilMathFunctionLibrary::GetFloatArrayAverage(const TArray<float>& Values)
|
|||
|
|
{
|
|||
|
|
if (Values.IsEmpty())
|
|||
|
|
{
|
|||
|
|
return 0.0f;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
double Sum = 0.0;
|
|||
|
|
for (const float Value : Values)
|
|||
|
|
{
|
|||
|
|
Sum += static_cast<double>(Value);
|
|||
|
|
}
|
|||
|
|
return static_cast<float>(Sum / Values.Num());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
float UDirectiveUtilMathFunctionLibrary::GetFloatArrayMedian(const TArray<float>& Values)
|
|||
|
|
{
|
|||
|
|
if (Values.IsEmpty())
|
|||
|
|
{
|
|||
|
|
return 0.0f;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TArray<float> Sorted = Values;
|
|||
|
|
Sorted.Sort();
|
|||
|
|
|
|||
|
|
const int32 Middle = Sorted.Num() / 2;
|
|||
|
|
if (Sorted.Num() % 2 == 0)
|
|||
|
|
{
|
|||
|
|
return static_cast<float>((static_cast<double>(Sorted[Middle - 1]) + static_cast<double>(Sorted[Middle])) * 0.5);
|
|||
|
|
}
|
|||
|
|
return Sorted[Middle];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
float UDirectiveUtilMathFunctionLibrary::GetFloatArrayStandardDeviation(const TArray<float>& Values)
|
|||
|
|
{
|
|||
|
|
if (Values.IsEmpty())
|
|||
|
|
{
|
|||
|
|
return 0.0f;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
double Sum = 0.0;
|
|||
|
|
for (const float Value : Values)
|
|||
|
|
{
|
|||
|
|
Sum += static_cast<double>(Value);
|
|||
|
|
}
|
|||
|
|
const double Mean = Sum / Values.Num();
|
|||
|
|
|
|||
|
|
double SquaredDeltaSum = 0.0;
|
|||
|
|
for (const float Value : Values)
|
|||
|
|
{
|
|||
|
|
const double Delta = static_cast<double>(Value) - Mean;
|
|||
|
|
SquaredDeltaSum += Delta * Delta;
|
|||
|
|
}
|
|||
|
|
return static_cast<float>(FMath::Sqrt(SquaredDeltaSum / Values.Num()));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int32 UDirectiveUtilMathFunctionLibrary::GetRandomIndexFromWeights(const TArray<float>& Weights)
|
|||
|
|
{
|
|||
|
|
float Total = 0.0f;
|
|||
|
|
for (const float Weight : Weights)
|
|||
|
|
{
|
|||
|
|
Total += FMath::Max(0.0f, Weight);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (Total <= 0.0f)
|
|||
|
|
{
|
|||
|
|
return INDEX_NONE;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const float Roll = FMath::FRand() * Total;
|
|||
|
|
float Accumulated = 0.0f;
|
|||
|
|
int32 LastPositiveIndex = INDEX_NONE;
|
|||
|
|
for (int32 Index = 0; Index < Weights.Num(); ++Index)
|
|||
|
|
{
|
|||
|
|
const float Weight = FMath::Max(0.0f, Weights[Index]);
|
|||
|
|
if (Weight <= 0.0f)
|
|||
|
|
{
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
LastPositiveIndex = Index;
|
|||
|
|
Accumulated += Weight;
|
|||
|
|
if (Roll < Accumulated)
|
|||
|
|
{
|
|||
|
|
return Index;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return LastPositiveIndex;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int32 UDirectiveUtilMathFunctionLibrary::GetRandomIndexFromWeightsFromStream(FRandomStream& Stream, const TArray<float>& Weights)
|
|||
|
|
{
|
|||
|
|
float Total = 0.0f;
|
|||
|
|
for (const float Weight : Weights)
|
|||
|
|
{
|
|||
|
|
Total += FMath::Max(0.0f, Weight);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (Total <= 0.0f)
|
|||
|
|
{
|
|||
|
|
return INDEX_NONE;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const float Roll = Stream.FRand() * Total;
|
|||
|
|
float Accumulated = 0.0f;
|
|||
|
|
int32 LastPositiveIndex = INDEX_NONE;
|
|||
|
|
for (int32 Index = 0; Index < Weights.Num(); ++Index)
|
|||
|
|
{
|
|||
|
|
const float Weight = FMath::Max(0.0f, Weights[Index]);
|
|||
|
|
if (Weight <= 0.0f)
|
|||
|
|
{
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
LastPositiveIndex = Index;
|
|||
|
|
Accumulated += Weight;
|
|||
|
|
if (Roll < Accumulated)
|
|||
|
|
{
|
|||
|
|
return Index;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return LastPositiveIndex;
|
|||
|
|
}
|