演出・エフェクト初級Robux不要

Robloxでゴール到達バッジを作る:BadgeServiceの使い方

Creator Dashboardでゴール到達バッジを作り、GoalPlatformへ触れた未取得プレイヤーへサーバーから安全に付与しよう。

公開日
黄色いゴールへ到着してゴール到達バッジを獲得したRoblox画面

黄色いGOALへ乗ると「ゴール到達」Badgeが付与され、プレイヤーのInventoryへ達成記録が残るようにする。完成までの目安は10分だ。Publish済みの自分のゲームと、Creator Dashboardへ入れる無料アカウントを使う。

黄色いGoalPlatformへ乗ってゴール到達Badgeを獲得した完成状態

この記事は、黄色いGoalPlatformを作る記事ゲームをPrivateでPublishする記事の続きだ。Course > GoalPlatformがあり、自分が所有するPublish済みゲームから始めよう。

Tip — Badgeは達成記録として残るメダル

Badgeは、ゴールや特別な条件を達成したプレイヤーへ贈るメダルだ。取得するとInventoryのBadgesへ残り、同じBadgeをもう一度取っても2個には増えない。

1. Creator DashboardでBadgeを作る

Creator Dashboardで対象ゲームを開き、左側のエンゲージメント > バッジへ進む。バッジを作成を押そう。

512×512ピクセルの正方形画像をアップロードし、次を入力する。

項目 入力する内容
名前 ゴール到達
詳細 3ステージのアスレチックで黄色いGOALへ到達した!
バッジが有効です オン

ゴール到達Badgeの画像、名前、詳細、有効状態を入力した作成画面

Roblox公式のBadge解説では、所有するゲームごとにGMT基準の24時間で最初の5個まで無料、超過分は1個100 Robuxだ。作成ボタンの近くに100 Robuxと表示された場合は押さず、次の無料枠まで待とう。この記事の検証では無料表示を確認して作成した。

画像は円形に切り抜かれるため、文字やトロフィーなど大切な部分は中央へ置く。バッジが有効ですをオフにするとプレイヤーが取得できないので、オンのままバッジを作成を押そう。

アップロード後のプレビューでは、トロフィーの左右や上下が円の外へ切れていないかも確認する。元画像が正方形でも、一覧やInventoryでは円形で表示される。外周ぎりぎりに細い文字を置くと読めなくなるため、主役を中央へ寄せ、背景は切れても困らない模様にしよう。

名前と詳細は、プレイヤーが「なぜ取れたか」をあとから思い出せる内容にする。クリアだけでは条件が分かりにくいが、3ステージ黄色いGOALまで書けば、別のBadgeが増えても区別できる。作成後は内容審査が終わるまで、画像がほかの人へすぐ表示されない場合がある。

2. Badge IDをコピーする

Badge一覧へ戻り、作ったゴール到達へマウスを重ねる。を開き、Asset IDをコピーを押そう。

作成したゴール到達BadgeのメニューからAsset IDをコピーする場所

Tip — Badge IDはScriptからBadgeを指定する番号

Badge IDとAsset IDは、ここでは同じ固有番号を指す。ゲームのExperience IDやPlace IDとは別物だ。今回コピーした77529583068327を次のBADGE_IDへ入れる。

番号をメモ帳から手で写すと、途中の数字を落としやすい。メニューからコピーし、local BADGE_ID =の右へそのまま貼り付けよう。前後へ引用符は付けず、数字として書く。別のゲームで作ったBadge IDを使うと、対象ゲームとの組み合わせが違うため付与できない。

3. GoalBadge Scriptを追加する

Studioへ戻り、ServerScriptServiceへScriptを追加してGoalBadgeへ名前を変える。中身をすべて消し、次へ置き換えよう。

local BadgeService = game:GetService("BadgeService")
local Players = game:GetService("Players")

local BADGE_ID = 77529583068327
local goal = workspace:WaitForChild("Course"):WaitForChild("GoalPlatform")
local processing = {}

local function awardGoalBadge(player)
	if processing[player] then
		return
	end
	processing[player] = true

	local infoSuccess, badgeInfo = pcall(
		BadgeService.GetBadgeInfoAsync,
		BadgeService,
		BADGE_ID
	)
	if not infoSuccess then
		warn("Goal badge info failed:", badgeInfo)
		processing[player] = nil
		return
	end
	if not badgeInfo.IsEnabled then
		warn("Goal badge is disabled")
		processing[player] = nil
		return
	end

	local checkSuccess, hasBadge = pcall(
		BadgeService.UserHasBadgeAsync,
		BadgeService,
		player.UserId,
		BADGE_ID
	)
	if not checkSuccess then
		warn("Goal badge check failed:", hasBadge)
	elseif hasBadge then
		print("GOAL_BADGE_ALREADY_OWNED", player.Name)
	else
		local callSuccess, awarded = pcall(
			BadgeService.AwardBadgeAsync,
			BadgeService,
			player.UserId,
			BADGE_ID
		)
		if callSuccess and awarded then
			print("GOAL_BADGE_AWARDED", player.Name)
		else
			warn("Goal badge award failed:", awarded)
		end
	end

	processing[player] = nil
end

local function onGoalTouched(hit)
	local character = hit:FindFirstAncestorOfClass("Model")
	local humanoid = character and character:FindFirstChildOfClass("Humanoid")
	local player = humanoid and Players:GetPlayerFromCharacter(character)
	if player then
		awardGoalBadge(player)
	end
end

goal.Touched:Connect(onGoalTouched)
Players.PlayerRemoving:Connect(function(player)
	processing[player] = nil
end)

ServerScriptServiceへ置いたGoalBadgeとBadgeServiceのコード

GoalPlatform.Touchedは足や体の複数Partで続けて発生する場合がある。processing[player]がある間は同じPlayerの通信を重ねず、完了後にだけ次を受け付ける。

3つのBadgeServiceメソッドはRoblox側と通信するため、失敗する可能性がある。pcall()で受け止め、まずGetBadgeInfoAsync()で有効状態、次にUserHasBadgeAsync()で取得済みかを確認し、未取得の場合だけAwardBadgeAsync()を呼ぶ。公式もBadgeの付与はサーバー側Scriptから行うよう案内している

順番を省かないのが大切だ。最初に有効状態を見ることで、Dashboard側で停止したBadgeを無理に付与しない。次に所有状態を見ることで、取得済みプレイヤーへ不要な付与通信を繰り返さない。最後のAwardBadgeAsync()trueを返した場合だけ、成功ログを出す。

processing[player]は保存データではなく、いま処理中かだけを覚える短い表だ。通信を待つ間に同じキャラクターの右足や左足が触れても2本目の処理を始めない。Playerが退出したら行を消すため、長時間serverが動いても退出済みPlayerの記録を残さない。

Badge付与をLocalScriptへ置かない理由もここにある。画面側のLocalScriptはプレイヤー自身の端末で動くため、達成していないのに付与を要求する処理へ書き換えられる可能性がある。ゴールの実物へ触れたかをserver側で確かめ、そのserverだけがBadgeServiceを呼ぶ形にしよう。

4. 未取得と取得済みを確認する

Playを押し、黄色いGOALへ歩いて乗ろう。初回はBadge取得通知が表示され、Outputへ次が出る。

GOAL_BADGE_AWARDED robtsuku

Stop後にRobloxのInventoryでバッジを開き、ゴール到達が追加されていることを確認する。

プレイヤーのInventoryへ追加されたゴール到達Badge

もう一度Playして同じGOALへ乗る。今度は新しいBadgeを増やさず、Outputへ次が出れば取得済み分岐も成功だ。

GOAL_BADGE_ALREADY_OWNED robtsuku

確認するのは、初回の付与、Inventoryへの追加、2回目の取得済み判定、赤いエラーなしの4点だ。この記事の実機確認では、初回に取得通知とGOAL_BADGE_AWARDED、Dashboardの獲得者数1、Inventoryのゴール到達を確認した。取得通知だけでは一時的に見逃すことがあるため、InventoryとOutputも合わせて見よう。

初回テストでは、Play開始直後にOutputを開き、GOALへ触れる前に赤い行がないことを確認する。そのままキャラクターを実際に歩かせよう。Command Barから関数を直接呼ぶだけでは、TouchedからPlayerを探す部分まで通った証拠にならない。

Inventoryへ反映されるまで少し待つ場合は、何度もGOALへ乗り直さず、まずOutputを見る。GOAL_BADGE_AWARDEDがあれば付与APIは成功している。ページを再読み込みしてBadgeを確認しよう。逆に警告が出ていれば、先にその内容を直してから新しいPlayを始める。

2回目は、同じアカウントで新しいPlayを開始する。取得済みのため通知は増えないが、GOAL_BADGE_ALREADY_OWNEDが1回出れば正常だ。GOALの上で体が動いても赤いエラーが連続せず、ゲームをそのまま操作できることまで確かめよう。

うまく動かないとき

  • Badgeを作成できない: Publish済みで自分が所有するゲームを開いているか確認する
  • 100 Robuxと表示される: 作成せず、GMT基準の次の無料枠まで待つ
  • Goal badge info failedが出る: BADGE_IDへExperience IDではなくAsset IDをコピーで得た番号を入れる
  • Badgeが無効と出る: Creator Dashboardで対象Badgeの有効設定をオンにする
  • GOALへ乗っても反応しない: Course > GoalPlatformの名前と親子関係を確認する
  • 取得済みなのに毎回通信する: UserHasBadgeAsync()hasBadgeの分岐を消していないか確認する

完成

これで、未取得プレイヤーがGOALへ触れたときだけBadgeを付与し、取得済みや通信失敗でもゲームを止めないゴール処理が完成した。別のBadgeを増やすときも、Dashboardで作ったAsset IDと達成条件を対応させよう。

公式リファレンス

画像の拡大表示