Hey all,
In my current project, I’m finding the need to generate weighted random integers is surprisingly high. I was also kind of surprised to see this function not be native to UE or to have not been tackled as part of the low end extended library. This is especially true as, on searching around on the topic, a solution for this was asked for quite frequently…
So I coded my own solution. I tried to include the add pins functionality, but couldn’t figure out how to get the cpp to account for the variability in values outside of using a variadic template (which I then discovered BPs don’t support). I then toyed with a duplicating (kind of) the Select Integer node, but I couldn’t get that ■■■■ to compile for all the money in China - and it was also now getting unnecessarily complex for what should have been a pretty simple operation.
Sooo… the compromise:
She’ll take in up to 20 weights (which can add up to anything) which will proportionally bias the selection of one of the (up to) 20 values. The function will ignore any value with zero weighting, and the blueprint node is collapsible to hide unplugged nodes.
To use it, just create a new C++ class. As set up it will live in files named Custom Functions and be an extension of the BluePrint Function Library. The code:
.h
#pragma once
#include "CoreMinimal.h"
#include "CustomFunctions.generated.h"
UCLASS()
class URandomIntegerWithWeight : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintPure, meta = (AdvancedDisplay = "v3,v4,v5,v6,v7,v8,v9,v10,v11,v12,v13,v14,v15,v16,v17,v18,v19,v20"), Category = "Math|Random")
static int32 RandomIntegerWithWeight(int32 v1, int32 v2, int32 v3 = 0, int32 v4 = 0, int32 v5 = 0, int32 v6 = 0, int32 v7 = 0, int32 v8 = 0, int32 v9 = 0, int32 v10 = 0, int32 v11 = 0, int32 v12 = 0, int32 v13 = 0, int32 v14 = 0, int32 v15 = 0, int32 v16 = 0, int32 v17 = 0, int32 v18 = 0, int32 v19 = 0, int32 v20 = 0);
};
.cpp
#include "CustomFunctions.h"
#include "Kismet/KismetMathLibrary.h"
int32 URandomIntegerWithWeight::RandomIntegerWithWeight(int32 v1, int32 v2, int32 v3, int32 v4, int32 v5, int32 v6, int32 v7, int32 v8, int32 v9, int32 v10, int32 v11, int32 v12, int32 v13, int32 v14, int32 v15, int32 v16, int32 v17, int32 v18, int32 v19, int32 v20)
{
TArray<int32> values = { v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20 };
int32 sum = 0;
for (int i = 0; i < values.Num(); i++)
{
if (values[i] > 0)
{
sum += values[i];
}
}
if (sum == 0)
{
return 0;
}
int32 randNum = FMath::RandRange(1, sum);
for (int i = 0; i < values.Num(); i++)
{
if (values[i] > 0)
{
randNum -= values[i];
if (randNum <= 0)
{
return i + 1;
}
}
}
return 0;
}
Possible improvements… a bool safety check in case all the values equal zero… and of course, the commutative binary operators for adding pins, rather than using the fixed twenty (which is plenty tbf).
Hope this helps someone, and improve away.
