Skip to content

Instantly share code, notes, and snippets.

@Reputeless
Last active October 24, 2023 12:08
Show Gist options
  • Save Reputeless/e143b30d0926e402ed287df838e43de8 to your computer and use it in GitHub Desktop.
Save Reputeless/e143b30d0926e402ed287df838e43de8 to your computer and use it in GitHub Desktop.
# include <Siv3D.hpp>
/// @brief ガチャを行うクラス
class GachaMachine
{
public:
GachaMachine() = default;
/// @brief ガチャを行うクラスを作成します。
/// @param items アイテムとその出現比率のペアの配列
/// @param cooldownTime ガチャのクールダウン時間(デフォルト: 0.5 秒)
explicit GachaMachine(const Array<std::pair<String, double>>& items, const Duration& cooldownTime = 0.5s)
: m_distribution{ items.map([](const auto& item) { return item.second; }) }
, m_probabilities{ m_distribution.probabilities() }
, m_itemNames{ items.map([](const auto& item) { return item.first; }) }
, m_cooldownTimer{ cooldownTime } {}
/// @brief ガチャのアイテム数を返します。
/// @return ガチャのアイテム数
[[nodiscard]]
size_t num_items() const
{
return m_probabilities.size();
}
/// @brief ガチャのアイテムの出現確率を返します。
/// @param index アイテムのインデックス
/// @return アイテムの出現確率
[[nodiscard]]
double getProbability(size_t index) const
{
return m_probabilities[index];
}
/// @brief ガチャのアイテムの出現確率の一覧を返します。
/// @return アイテムの出現確率の一覧
[[nodiscard]]
const Array<double>& getProbabilities() const
{
return m_probabilities;
}
/// @brief ガチャのアイテムの名前を返します。
/// @param index アイテムのインデックス
/// @return アイテムの名前
[[nodiscard]]
const String& getItemName(size_t index) const
{
return m_itemNames[index];
}
/// @brief ガチャのアイテムの名前の一覧を返します。
/// @return アイテムの名前の一覧
[[nodiscard]]
const Array<String>& getItemNames() const
{
return m_itemNames;
}
/// @brief ガチャを行い、結果を返します。
/// @return ガチャの結果
[[nodiscard]]
int32 getResult() const
{
m_cooldownTimer.restart();
return static_cast<int32>(m_distribution(GetDefaultRNG()));
}
/// @brief ガチャを複数回行い、結果を返します。
/// @param n ガチャを行う回数
/// @return ガチャの結果の配列
[[nodiscard]]
Array<int32> getResults(size_t n) const
{
return Array<int32>::Generate(n, [this]() { return getResult(); });
}
/// @brief ガチャがクールダウン中であるかを返します。
/// @return クールダウン中の場合 true, それ以外の場合は false
[[nodiscard]]
bool isCoolingDown() const
{
return m_cooldownTimer.isRunning();
}
/// @brief クールダウンの進捗を [0.0, 1.0] の範囲で返します。
/// @return クールダウンの進捗
[[nodiscard]]
double getCoolDownProgress() const
{
return m_cooldownTimer.progress0_1();
}
private:
mutable DiscreteDistribution m_distribution;
Array<double> m_probabilities;
Array<String> m_itemNames;
mutable Timer m_cooldownTimer;
};
void Main()
{
Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });
// フォント
const Font font{ FontMethod::MSDF, 48, Typeface::Heavy };
// ガチャのアイテムと出現比率、クールダウン時間を設定する
const GachaMachine gachaMachine{ {
{ U"はずれ", 1000 },
{ U"ブロンズ", 100 },
{ U"シルバー", 10 },
{ U"ゴールド", 1 }},
0.4s };
// アイテムの確率を表示する
for (size_t i = 0; i < gachaMachine.num_items(); ++i)
{
Print << U"「{}」の確率: {:.2f}%"_fmt(gachaMachine.getItemName(i), (gachaMachine.getProbability(i) * 100));
}
// ダイヤの数
int32 diamonds = 35;
// 直前のガチャの結果を格納する配列
Array<int32> lastResults;
while (System::Update())
{
if (SimpleGUI::Button(U"\U000F01C8 {}"_fmt(diamonds), Vec2{ 300, 40 }, 100, (not gachaMachine.isCoolingDown())))
{
// ダイヤを購入する処理など
diamonds += 100;
}
if (SimpleGUI::Button(U"1 回ガチャ \U000F01C8 × 1", Vec2{ 300, 80 }, 200, (lastResults.isEmpty() && (1 <= diamonds))))
{
--diamonds;
lastResults = gachaMachine.getResults(1); // 引いた瞬間に結果を取得する
}
if (SimpleGUI::Button(U"5 回ガチャ \U000F01C8 × 5", Vec2{ 520, 80 }, 200, (lastResults.isEmpty() && (5 <= diamonds))))
{
diamonds -= 5;
lastResults = gachaMachine.getResults(5); // 引いた瞬間に結果を取得する
}
// 各ガチャ結果について
for (int32 i = 0; i < static_cast<int32>(lastResults.size()); ++i)
{
const RectF rect{ 300, (160 + i * 50), (gachaMachine.getCoolDownProgress() * 300), 45 };
// そのアイテムの出現率を取得する
const double probability = gachaMachine.getProbability(lastResults[i]);
// 出現率に応じて背景色を変える
if (probability < 0.1) // 10% 未満
{
rect.draw(ColorF{ 1.0, 0.8, 0.6 });
}
else
{
rect.draw(ColorF{ 0.9 });
}
// クールダウン完了後に
if (not gachaMachine.isCoolingDown())
{
// アイテム名を表示する
font(gachaMachine.getItemName(lastResults[i])).drawAt(30, rect.center(), ColorF{ 0.11 });
}
}
if (lastResults && (not gachaMachine.isCoolingDown()))
{
if (SimpleGUI::Button(U"アイテムを受け取る", Vec2{ 300, 440 }, 300))
{
// ガチャ結果のアイテムを受け取る処理など
lastResults.clear();
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment