アイテム・ショップ初級プラスRobux不要

Roblox Studioでコインショップを作る:Toolを購入する方法

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

公開日
10コインのショップで購入し、ホットバーへLightStickが追加されたStudio画面

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

10コインのショップで購入し、ホットバーへLightStickが追加された完成状態

この記事は、3枚のコインを拾ってScoreを増やす記事と、LightStick Toolを作る記事の続きだ。leaderstats > ScoreStarterPack > LightStick > Handleがある状態から始めよう。

今回は、購入の判断をすべてサーバーのScriptで行う。見た目だけのPromptを押せても、残高不足、遠すぎる操作、同じToolの連続購入はサーバーで止める。

1. 販売用ToolをServerStorageへ移す

Stopした編集画面で、StarterPackLightStickServerStorageへ移す。ServerStorageの右にあるからFolderを追加してShopItemsと名付け、その中へLightStickを入れよう。

ServerStorage
└─ ShopItems
   └─ LightStick (Tool)
      └─ Handle  (Part)

ServerStorageのShopItems内へLightStick Toolを置いた構成

Tip — ServerStorageはサーバーだけの保管場所

ServerStorageの中身は、参加したプレイヤーへ自動配布されない。ショップで売る元のToolを保管し、購入できたときだけ複製するのに向いている。StarterPackに残すと全員が最初から持つため、必ず親のLightStickを移そう。

LightStickの中にHandleがあり、LightStick自身がToolであることも確認する。Handleだけを保管すると、ホットバーへ入るアイテムにならない。

LightStickの代わりに自作した見た目を売りたい場合は、先にBlenderで作った剣をOriginalSword > Handleへ入れる記事まで進めよう。販売用の保管場所と購入処理は同じで、ITEM_NAMEだけをOriginalSwordへそろえればよい。

移動後にPlayを1回押し、購入前のホットバーへLightStickが出ていないことも確認しよう。まだ表示される場合は、同じ名前のToolがStarterPackWorkspaceにもう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 オフ

CoinShop Partの直下にBuyPromptを置いたExplorer構成

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のValue9へ変えよう。この変更は現在のStudioテストだけで、Robuxや購入履歴は動かさない。すでにコイン保存を追加しているゲームでは、テスト後にStopし、通常の保存値へ戻ったことも確認する。

Scoreを9にした状態でショップへ近づく。Promptが表示されても、Eを押した後のScoreが9のまま、ホットバーにLightStickがなければ残高不足を止められている。残高不足メッセージは今回作っていないため、何も増減しないのが正しい結果だ。

ショップへ近づくとLightStickを10コインで購入するPromptが表示された状態

一度Stopして新しくPlayし、同じ場所からScoreのValue20へ変える。もう一度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でも同じショップを試せればよい。

うまく動かないとき

  • 最初からホットバーにある: LightStickStarterPackに残っている。親のToolをServerStorage > ShopItemsへ移す
  • Promptが出ない: BuyPromptCoinShopの直下にあり、MaxActivationDistance10になっているか確認する
  • 20コインでも買えない: leaderstatsScoreCoinShopBuyPromptLightStickの大文字小文字をコードとそろえる
  • 買えたのに手に持てない: LightStick > Handleの構成と、Handle.Anchoredがオフかを確認する
  • Playし直すと消える: 今回は現在のPlayだけの購入で正常だ。Toolの所有保存はこの記事の単一目標に含めない

完成

これで、9コイン以下では購入を断り、10コイン以上では10だけ消費してLightStickをBackpackへ渡すショップが完成した。連続入力や離れた場所からの操作もサーバーで止めている。

次は、プレイヤー一覧を開かなくても現在のScoreが分かるように、画面上へコインカウンターを表示しよう

公式リファレンス

画像の拡大表示