Skip to content

Instantly share code, notes, and snippets.

@shun126
Last active February 11, 2024 10:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shun126/858a772178768e7075fd648e5863ff71 to your computer and use it in GitHub Desktop.
Save shun126/858a772178768e7075fd648e5863ff71 to your computer and use it in GitHub Desktop.
Unreal Engine用確率指定可能な抽選関数
/*
Unreal Engine用
確率付き抽選に関するヘッダーファイル
https://www.youtube.com/channel/UCkLXe57GpUyaOoj2ycREU1Q
*/
#pragma once
#include <Kismet/BlueprintFunctionLibrary.h>
#include "DrawLots.generated.h"
/*
当選確率と当選アイテム
*/
USTRUCT(BlueprintType)
struct FLotteryItem
{
GENERATED_BODY()
// 抽選確率
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ClampMin = "1"))
uint8 Odds = 1;
// 当選アイテム
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TObjectPtr<UObject> Item;
// コンストラクタ
FLotteryItem()
: Odds(1)
, Item(nullptr)
{}
// コンストラクタ
FLotteryItem(const uint8 odds, UObject* item) noexcept
: Odds(odds)
, Item(item)
{}
};
/*
当選確率が指定可能な抽選データ
LotteryItemsに抽選したいFLotteryItemを追加してDrawLotsObject関数を呼ぶと抽選します
*/
USTRUCT(BlueprintType)
struct FDrawLotsObject
{
GENERATED_BODY()
// 抽選アイテム
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TArray<FLotteryItem> LotteryItems;
// 抽選
UObject* Draw(const bool consecutive) const
{
TArray<FLotteryItem> items;
items.Reserve(LotteryItems.Num());
int32 totalWeight = 0;
for (int32 i = 0; i < LotteryItems.Num(); ++i)
{
if (consecutive || i != mLastLotteryNumber)
{
const auto& lotteryItem = LotteryItems[i];
totalWeight += lotteryItem.Odds;
items.Emplace(totalWeight, lotteryItem.Item);
}
}
if (totalWeight <= 0)
return nullptr;
// TODO: specify random numbers externally.
const int32 rnd = FGenericPlatformMath::Rand() % totalWeight;
for (int32 i = 0; i < items.Num(); ++i)
{
const auto& item = items[i];
if (rnd < item.Odds)
{
mLastLotteryNumber = i;
return item.Item;
}
}
return nullptr;
}
private:
mutable int32 mLastLotteryNumber = -1;
};
/*
当選確率が指定可能な抽選データ
Oddsに確率を定義してDrawLotsIndex関数を呼ぶと抽選します
*/
USTRUCT(BlueprintType)
struct FDrawLotsIndex
{
GENERATED_BODY()
// 確率(1以上の値を入力してください)
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TArray<uint8> Odds;
// 抽選
int32 Draw(const bool consecutive) const
{
TArray<int32> items;
items.Reserve(Odds.Num());
int32 totalWeight = 0;
for (int32 i = 0; i < Odds.Num(); ++i)
{
if (consecutive || i != mLastLotteryNumber)
{
totalWeight += Odds[i];
items.Add(totalWeight);
}
}
if (totalWeight <= 0)
return -1;
// TODO: specify random numbers externally.
const int32 rnd = FGenericPlatformMath::Rand() % totalWeight;
for (int32 i = 0; i < items.Num(); ++i)
{
if (rnd < items[i])
{
mLastLotteryNumber = i;
return i;
}
}
return -1;
}
private:
mutable int32 mLastLotteryNumber = -1;
};
/*
確率指定可能な抽選関数
*/
UCLASS(Blueprintable)
class UDrawLotsBlueprint : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
/*
当選確率が指定可能な抽選
当選したアイテムを返します。
確率をすべて0にした場合など抽選できない場合はnullptrを返します。
consecutiveを無効にすると同じアイテムが連続で当選しません
*/
UFUNCTION(BlueprintCallable)
static UObject* DrawLotsObject(const FDrawLotsObject& drawLotsObject, const bool consecutive = true)
{
return drawLotsObject.Draw(consecutive);
}
/*
当選確率が指定可能な抽選をします
当選した配列番号を返します。
確率をすべて0にした場合など抽選できない場合は-1を返します。
consecutiveを無効にすると同じ番号が連続で当選しません
*/
UFUNCTION(BlueprintCallable)
static int32 DrawLotsIndex(const FDrawLotsIndex& drawLotsIndex, const bool consecutive = true)
{
return drawLotsIndex.Draw(consecutive);
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment