roblox-simulator-economy について
Roblox のシミュレーター、タイクーン、クリッカー、放置ゲーム向けに、Luau で経済を設計・実装するスキルです。進行ペース計算、サーバー管理の購入処理、テスト可能な数式を扱います。
主な機能
- 01アップグレードと転生の進行計算
- 02サーバー管理の購入処理
- 03オフライン収益とセーブデータ修復
- 04大きな数値の表示と確率計算
- 05Luau 経済計算のテスト実行
活用シーン
- 01アップグレード費用と転生間隔の調整
- 02まとめ買いとオフライン報酬の実装
- 03クライアント入力による通貨購入の改ざん防止
- 04大きな残高と有料アイテムの確率表示
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ gamedev-skills/ awesome-gamedev-agent-skills --skill roblox-simulator-economyインストール後、Claude Code で/roblox-simulator-economyを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
agents/openai.yamlreferences/formulas.mdserver-patterns.mdscripts/balance.luaueconomy_math.luautest_economy_math.luau詳細
Roblox シミュレーターの経済設計
ゲームプレイのコードを書く前にペース配分表を使って経済の数値を決め、すべての金額をサーバーが管理するように実装します。 コードの対象は、ローリング方式で更新される Roblox のプラットフォーム API 上の Luau です。同梱スクリプトは純粋な Luau で、Studio の外で Lune 0.11.0 を使って実行しています。
使用する場面
- シミュレーター、タイクーン、クリッカー、放置ゲームの
baseCost、costGrowth、転生の必要条件と倍率、またはオフラインの上限時間と効率を選定または見直すとき。 - Luau でアップグレード、最大数購入、通貨、転生、オフライン収益のコードを書くとき。
- 大きな数値や、精度が失われる約
1e15を超える値を表示するとき。 - Robux、または Robux で購入できる通貨で買う卵、クレート、スピンを追加するとき。
使用しない場面: DataStore の読み書き、セッションロック、再試行 → roblox-datastores。
リモートプロトコルの設計と詳細なレート制限 → roblox-networking。ショップと HUD のレイアウト →
roblox-ui + game-ui-ux。ウェーブディフェンスの経済設計 → tower-defense。エンジンに依存しないセーブ形式に関する質問 →
save-systems。
ワークフロー
- 調査します。 現在、通貨、アップグレード、転生の値がどこにあり、どのスクリプトが変更しているかを確認します。通貨に関わる RemoteEvent をすべて列挙し、それぞれが何を送信するか記録します。
- 数値を1つの設定テーブルにまとめます。 基本収益、各アップグレードの
baseCost、costGrowth、効果、maxLevel、転生のbaseRequirement、requirementGrowth、multiplierPerRebirth、オフラインのmaxHours、efficiencyを記述します。サーバーとクライアントの両方がこの設定を読み込みます。 - スキルのディレクトリからペース配分計算ツールを実行します(パスはこのディレクトリからの相対パスです)。
lune run scripts/balance.luau --print-defaults > economy.jsonを実行してファイルを編集し、その後lune run scripts/balance.luau economy.jsonを実行します。wait列とvs prev列を確認し、下記の目標に合うまで調整します。--helpでは各フィールドと許容範囲を確認できます。ファイルの欠落、無効な JSON、または許容範囲外のフィールドがあると、エラーを出力して終了コード 1 で終了します。 - 下記のパターンと
references/server-patterns.md を使ってサーバー側に実装します。
scripts/economy_math.luauは変更せずに ModuleScript にコピーし、テストした計算式がそのまま実行されるようにします。 - 検証します。 計算式のテストには
lune run scripts/test_economy_math.luauを実行します(失敗時は終了コード 1)。購入、転生、退出と再参加、無効なリモート引数については、Studio で Server & Clients を使ってプレイテストします。
計算式
| 項目 | 計算式 | 備考 |
|---|---|---|
| 次のレベルの価格 | floor(baseCost * costGrowth ^ owned) | 何度も購入するアップグレードでは 1.07~1.15、まれに買う高額なものでは 1.5~3 |
| レベル a から b までのコスト | baseCost * g^a * (g^(b-a) - 1) / (g - 1) | プレイヤーが支払うのはレベルごとの価格を切り捨てた合計で、わずかに少なくなります |
| 最大数購入 | 次の価格が所持金または maxLevel を超えるまで、実際の価格を順に確認します | 価格を切り捨てる場合、対数を使った式より安全です |
| 転生 k の必要条件 | baseRequirement * requirementGrowth ^ k | |
| 転生倍率 | 1 + k * multiplierPerRebirth | 加算式はゆるやかに増加し、乗算式(q ^ k)は指数関数的に増加します |
| オフライン収益 | floor(min(away, maxHours * 3600) * rate * efficiency) | 経過時間が 0 以下(時計が戻った場合)は、収益を支払いません |
導出、転生の待ち時間比率の計算式、具体例については、 references/formulas.md を参照してください。
ペース配分の目安(開始点であり、ルールではありません)
- 序盤のアップグレードには数秒かかります。 デフォルト設定では、最初の10レベルはそれぞれ3~10秒です。
- リバースにかかる時間は、前回より少し長くなる程度にします。たとえばx1.2~x1.5です。計算ツールの
vs prev列がその比率です。加算型の倍率では、時間の経過とともにrequirementGrowthに近づきます。そのため、requirementGrowth = 3の場合、倍率はx2.0からx2.7まで上昇し、デフォルトの収入では8回目のリバースに137日かかります。requirementGrowth = 1.8の場合、倍率はx1.2からx1.6まで上昇します。 - オフラインの収入はオンラインより少なくします。 効率を0.25~0.5にし、上限を8~12時間にすると、ログアウト中のプレイヤーの収入がゼロにならず、放置するよりプレイする価値を高く保てます。
サーバー側で管理する購入
RemoteEventで送るのは、プレイヤーが何を買いたいか(「アップグレードIncomeを購入」)であり、数量や価格ではありません。サーバーが価格を確認し、残高をチェックして、途中でyieldせずに支払いを処理して付与します。
-- ServerScriptService/Economy.server.luau (excerpt; full version in references/server-patterns.md)
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local EconomyMath = require(ReplicatedStorage.Shared.EconomyMath)
local Config = require(ReplicatedStorage.Shared.EconomyConfig)
ReplicatedStorage.Remotes.BuyUpgrade.OnServerEvent:Connect(function(player: Player, upgradeId: unknown)
local session = sessions[player]
if not session or type(upgradeId) ~= "string" then return end
local upgrade = Config.upgrades[upgradeId]
if not upgrade then return end -- unknown id
local owned = session.levels[upgradeId] or 0
if owned >= upgrade.maxLevel then return end
local price = EconomyMath.levelCost(upgrade.baseCost, upgrade.costGrowth, owned)
if EconomyMath.trySpend(session.wallet, { [upgrade.currency] = price }) then
session.levels[upgradeId] = owned + 1
end
end)
trySpendは、複数通貨でのコストを差し引く前にすべての通貨をチェックし、負のコストや有限でないコストを拒否します。- サーバー側でリバースします:サーバー側の残高を必要額と比較してから、ウォレットとレベルをリセットし、回数を増やします。「リバースした」という申告やクライアントからの残高を決して受け入れないでください。
roblox-networkingを使い、トークンバケットでプレイヤーごとに各リモートのレートを制限します。
セーブデータとオフライン時間
- ロード時に修復し、エラーを発生させないでください。
sanitizeAmountを使います(数値以外、NaN、無限大、負の値は0になり、小数部分は切り捨てられます)。また、sanitizeLevelを使います(現在のmaxLevelを上限として制限する機能もあります)。不明なアップグレードIDを破棄し、修復した内容の一覧を返すことで、古いバージョンのデータや編集されたデータも問題なくロードできます。 - NaNや無限大は決して保存しないでください。 ゼロ除算の結果がウォレットに入る場合は、保存前に同じサニタイザーで捕捉してください。
- オフライン時間の算出にはサーバー時刻のみを使います:プレイヤーが退出したときにサーバーで
os.time()を保存し、参加時にaway = os.time() - lastSeenとします。lastSeenがない場合や未来の時刻の場合は現在時刻として扱い、収入は発生しません。 - 精度: Luauの数値は倍精度で、整数は
2^53(約9e15)まで正確です。残高が約1e15を超える可能性がある場合は、仮数部と指数部のペア、または底が10の対数を保存してください。
大きな数値の表示
EconomyMath.formatNumber(value, decimals?)は、接尾辞K、M、B、T、Qa、Qi、Sx、Sp、Oc、No、Dc、Ud、Dd、Td、Qad、Qid、Sxd、Spd、Ocd、Nod、Vg(1e63)を使い、その後は科学的記数法で表示します。
- 桁上がりの不具合:
999950 / 1000 = 999.95は四捨五入すると"1000.0K"になります。先に四捨五入し、丸めた値が1000に達した場合は次の接尾辞("1.0M")に進めます。テストではこのケースを確認します。 - 表示専用のクライアント側でフォーマットし、フォーマット済み文字列を数値に戻すために解析しないでください。
有料ランダムアイテム(卵、クレート、スピン)
Robuxで直接、またはRobuxで購入したゲーム内通貨を通じてランダムアイテムを購入する場合、Robloxの有料ランダムアイテムに関するガイドラインが適用されます。このページによると:
- 購入前に、すべての結果と実際の数値による確率をパーセンテージで表示し、その合計を正確に100%にします(長い小数は丸めてもかまいませんが、表示された合計が正確に100%にならない可能性があることを注記してください)。
- 運勢ブーストや天井システムなどの確率変更要素も対象です。購入前に効果を数値で説明し、ブーストが有効な間は表示される確率をリアルタイムで更新します。
- 全項目を一度に表示できない場合は、購入前に見える「Info」または「Details」と表示されたコントロールから開けるようにします。単語のない「(i)」アイコンだけでは不十分です。
PolicyService:GetPolicyInfoForPlayerAsync()はArePaidRandomItemsRestrictedを返します。該当するプレイヤーには、このページに記載されている対応策のいずれかを適用します。
EconomyMath.oddsPercent(outcomes)で確率パネルを構築し、同じリスト(ブーストを適用した後)を使って、サーバー側でEconomyMath.pickWeighted(outcomes, rng:NextNumber())により抽選します。これにより、表示確率と実際の確率にずれが生じません。
注意点
- Rebirth 6 に数週間かかる →
requirementGrowthが乗数の成長率を大幅に上回っています。vs prev列を確認し、requirementGrowthを下げるか、乗算型の乗数に切り替えてください。 - 約20レベル以降、価格が収入を上回る → 50回購入する想定のアップグレードで
costGrowthが1.5以上になっています(レベル50の価格はレベル0の約6億3,700万倍です)。1.07~1.15を使用してください。 - 数回リバースした後に終盤のバランスが崩れる → 要件曲線に対応していない乗算型の乗数(
1.5 ^ k)を使用しています。requirementGrowth / qを、目標とする比率に近づけてください。 - プレイヤーが獲得していない通貨を入手する → リモートが金額や価格を受け取っているか、検証がクライアント上でしか行われていません。IDを送信し、価格はサーバーで設定してください。
- 残高チェックを2回の購入が通過する → チェックと支出の間でハンドラーが処理を中断しています。
task.wait、DataStore 呼び出し、その他の中断処理より前に支出してください。 - 最大購入でサーバーがフリーズする → ループに上限がないか、価格が0です。
maxLevelを渡し、baseCost >= 1を保ってください(maxAffordableは無限ループする代わりにエラーを返します)。 - オフライン報酬で何日分もの収入が得られる → クライアント側のタイムスタンプを使っている、上限がない、または
lastSeenが破損しています。サーバーの時計を使って上限を設け、未来の時刻は現在時刻として扱ってください。 - HUD に「1000.0K」と表示される → 接尾辞を選ぶ前に
math.floor(x * 10) / 10を使っています。formatNumberを使用してください。 - セーブに失敗する、または読み込み結果が不正になる → NaN または無限大がウォレットに入り込んでいます。セーブ前とロード時にサニタイズしてください。
リソース
scripts/balance.luau— ペーシング計算ツール。レベルごとの価格、合計、収入、待ち時間、リバースごとの要件、乗数、待ち時間と比率、オフライン報酬の上限を計算します。まず--helpを実行してください。scripts/economy_math.luau— 数式、trySpend、サニタイザー、確率、formatNumberを純粋な Luau で実装しています。Lune と Roblox ModuleScript で実行できます。scripts/test_economy_math.luau— すべての関数のテストです(lune run。失敗時は終了コード1を返します)。- references/formulas.md — 導出、リバース比率、計算例。
- references/server-patterns.md — 設定の構成、収入の更新、最大購入、リバース、オフライン報酬を含むロード時の修復、有料ランダムアイテムの抽選。
関連スキル
roblox-luau— サービス、ModuleScript、クライアントとサーバーの分離。roblox-networking— リモートの検証とトークンバケットによるレート制限。roblox-datastores— このスキルで修復するセッションの読み込みと保存。roblox-ui— ショップ、アップグレード、確率のパネル。roblox-studio-workflow— サーバーとクライアントのプレイテスト。
主な参考資料
https://create.roblox.com/docs/production/monetization/paid-random-itemshttps://create.roblox.com/docs/scripting/events/remotehttps://create.roblox.com/docs/scripting/security/client-server-boundaryhttps://create.roblox.com/docs/reference/engine/classes/PolicyServicehttps://create.roblox.com/docs/reference/engine/datatypes/Randomhttps://lune-org.github.io/docs