Roblox Studioでコインショップを作る:Toolを購入する方法
ProximityPromptで10コインを支払い、残高をサーバーで確認してLightStick ToolをBackpackへ渡すショップを作ろう。

ショップの前でEを押すと、10コインを支払ってLightStickを1本買えるようにする。残高が9以下なら何も変わらず、20から買えば10になり、画面下のホットバーへToolが追加される。完成までの目安は8分だ。Robux、外部モデル、Publishは必要ない。

この記事は、3枚のコインを拾ってScoreを増やす記事と、LightStick Toolを作る記事の続きだ。leaderstats > ScoreとStarterPack > LightStick > Handleがある状態から始めよう。
今回は、購入の判断をすべてサーバーのScriptで行う。見た目だけのPromptを押せても、残高不足、遠すぎる操作、同じToolの連続購入はサーバーで止める。
1. 販売用ToolをServerStorageへ移す
Stopした編集画面で、StarterPackのLightStickをServerStorageへ移す。ServerStorageの右にある+からFolderを追加してShopItemsと名付け、その中へLightStickを入れよう。
ServerStorage
└─ ShopItems
└─ LightStick (Tool)
└─ Handle (Part)

Tip — ServerStorageはサーバーだけの保管場所
ServerStorageの中身は、参加したプレイヤーへ自動配布されない。ショップで売る元のToolを保管し、購入できたときだけ複製するのに向いている。StarterPackに残すと全員が最初から持つため、必ず親のLightStickを移そう。
LightStickの中にHandleがあり、LightStick自身がToolであることも確認する。Handleだけを保管すると、ホットバーへ入るアイテムにならない。
LightStickの代わりに自作した見た目を売りたい場合は、先にBlenderで作った剣をOriginalSword > Handleへ入れる記事まで進めよう。販売用の保管場所と購入処理は同じで、ITEM_NAMEだけをOriginalSwordへそろえればよい。
移動後にPlayを1回押し、購入前のホットバーへLightStickが出ていないことも確認しよう。まだ表示される場合は、同じ名前のToolがStarterPackやWorkspaceにもう1つ残っている。Stopへ戻り、Explorer上部の検索欄へLightStickと入力すると重複を探しやすい。販売用の正本はServerStorage > ShopItemsの1本だけにする。
2. ショップとProximityPromptを作る
WorkspaceへPartを追加し、名前をCoinShopへ変える。プレイヤーが近づける場所へ置き、Anchoredをオンにしよう。色や大きさは自由だ。画像の看板は見つけやすくするための飾りなので、購入処理には必要ない。
CoinShopの右の+からProximityPromptを追加し、名前をBuyPromptへ変える。Propertiesは次の値にする。
| Property | 値 |
|---|---|
ActionText |
10コインで購入 |
ObjectText |
LightStick |
KeyboardKeyCode |
E |
HoldDuration |
0 |
MaxActivationDistance |
10 |
RequiresLineOfSight |
オフ |

Tip — ProximityPromptは近づくと出る操作案内
ProximityPromptは、Partの近くでEなどの入力案内を表示する仕組みだ。スマートフォンでは同じPromptがタップ操作になる。今回は表示だけをPromptへ任せ、購入条件は次のサーバーScriptで調べる。
3. サーバーで残高を確認する
ServerScriptServiceへScriptを追加し、名前をShopServerへ変える。中身をすべて消して、次のコードへ置き換えよう。
local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
local COST = 10
local ITEM_NAME = "LightStick"
local MAX_DISTANCE = 12
local COOLDOWN = 0.5
local shop = workspace:WaitForChild("CoinShop")
local prompt = shop:WaitForChild("BuyPrompt")
local itemTemplate = ServerStorage
:WaitForChild("ShopItems")
:WaitForChild(ITEM_NAME)
local lastTriggered = {}
local function alreadyHasItem(player)
local backpack = player:FindFirstChildOfClass("Backpack")
local character = player.Character
return (backpack and backpack:FindFirstChild(ITEM_NAME))
or (character and character:FindFirstChild(ITEM_NAME))
end
local function buyItem(player)
local now = os.clock()
if now - (lastTriggered[player] or 0) < COOLDOWN then
return
end
lastTriggered[player] = now
local character = player.Character
local root = character and character:FindFirstChild("HumanoidRootPart")
if not root or (root.Position - shop.Position).Magnitude > MAX_DISTANCE then
return
end
local leaderstats = player:FindFirstChild("leaderstats")
local score = leaderstats and leaderstats:FindFirstChild("Score")
local backpack = player:FindFirstChildOfClass("Backpack")
if not score or not backpack or not itemTemplate:IsA("Tool") then
return
end
if alreadyHasItem(player) or score.Value < COST then
return
end
local item = itemTemplate:Clone()
score.Value -= COST
item.Parent = backpack
end
prompt.Triggered:Connect(buyItem)
Players.PlayerRemoving:Connect(function(player)
lastTriggered[player] = nil
end)
COSTが商品の価格、itemTemplateが保管したToolの元だ。Triggeredで受け取ったプレイヤーについて、距離、Score、Backpack、すでに同じToolを持っていないかを順番に確認する。条件をすべて通ったときだけ10を引き、複製したToolをBackpackへ入れる。
MAX_DISTANCEをPromptの表示距離より少し大きい12にしているのは、入力した直後のわずかな移動を許しつつ、遠くからの不正な呼び出しを拒否するためだ。COOLDOWNは同じプレイヤーから0.5秒以内に続く入力を無視する。どちらも見た目の設定ではなく、サーバーが最後に確かめる条件になる。
alreadyHasItem()はBackpackだけでなくCharacterも調べる。Toolを装備すると、Explorer上ではBackpackからCharacterへ一時的に移るためだ。片方だけを調べると、装備中にもう一度買って2本持ててしまう。購入条件の確認中には待ち時間を作らず、すべて通った後にだけScoreを引くので、残高が10未満になる購入は成立しない。
Roblox公式のセキュリティ解説でも、クライアントから起きた操作はサーバー側で状況、値、頻度を検証するよう案内している。Promptの表示や入力だけを購入成立の根拠にせず、価格と残高をサーバーに置くのが大切だ。
Tip — Backpackは現在持っているアイテム一覧
BackpackへToolを入れると、そのPlay中のホットバーへ現れる。今回は保存処理を追加しないため、Stopして新しくPlayすると購入前へ戻る。購入済みアイテムを次回も残す機能は別の仕組みになる。
4. 9コインと20コインで確かめる
今回は3枚のコインだけでは9と20を作れないため、Studio内のテスト値を使う。Play中のExplorerでPlayers > 自分の名前 > leaderstats > Scoreを選び、PropertiesのValueを9へ変えよう。この変更は現在のStudioテストだけで、Robuxや購入履歴は動かさない。すでにコイン保存を追加しているゲームでは、テスト後にStopし、通常の保存値へ戻ったことも確認する。
Scoreを9にした状態でショップへ近づく。Promptが表示されても、Eを押した後のScoreが9のまま、ホットバーにLightStickがなければ残高不足を止められている。残高不足メッセージは今回作っていないため、何も増減しないのが正しい結果だ。

一度Stopして新しくPlayし、同じ場所からScoreのValueを20へ変える。もう一度Eを押そう。Scoreがちょうど10減り、ホットバーへLightStickが1本出れば成功だ。0.5秒待ってから続けてPromptを押しても、2本目が増えず、Scoreが負にならないことまで確認する。
装備したまま再度試しても、Scriptはキャラクター内のToolも調べるため2本目を渡さない。別の商品も売りたくなったら、商品名、価格、Promptを分けるところから発展させられる。
価格を変更するときは、サーバーのCOSTを正しい値として先に変え、PromptのActionTextも同じ表示へ直す。ActionTextだけを5コインにしても、実際の支払いはサーバーの10コインのままだ。表示と成立条件がずれないよう、Play前に2か所を読み合わせよう。
最後にStopしてもう一度Playし、購入前のScoreと空のホットバーへ戻ることを確認する。この記事ではToolの所有を保存していないため、このリセットは失敗ではない。編集画面のServerStorage > ShopItems > LightStickは残り、次のPlayでも同じショップを試せればよい。
うまく動かないとき
- 最初からホットバーにある:
LightStickがStarterPackに残っている。親のToolをServerStorage > ShopItemsへ移す - Promptが出ない:
BuyPromptがCoinShopの直下にあり、MaxActivationDistanceが10になっているか確認する - 20コインでも買えない:
leaderstats、Score、CoinShop、BuyPrompt、LightStickの大文字小文字をコードとそろえる - 買えたのに手に持てない:
LightStick > Handleの構成と、Handle.Anchoredがオフかを確認する - Playし直すと消える: 今回は現在のPlayだけの購入で正常だ。Toolの所有保存はこの記事の単一目標に含めない
完成
これで、9コイン以下では購入を断り、10コイン以上では10だけ消費してLightStickをBackpackへ渡すショップが完成した。連続入力や離れた場所からの操作もサーバーで止めている。
次は、プレイヤー一覧を開かなくても現在のScoreが分かるように、画面上へコインカウンターを表示しよう。
