スポーンとリスポーン

*このコンテンツは、ベータ版のAI(人工知能)を使用して翻訳されており、エラーが含まれている可能性があります。このページを英語で表示するには、 こちら をクリックしてください。

スポーンはゲーム内でオブジェクトやキャラクターを作成するプロセスであり、リスポーンはキャラクターの健康がゼロになったり、マップから落ちたりするなどの削除条件を満たした後に、オブジェクトやキャラクターをゲームに戻すプロセスです。これらのプロセスは、プレイヤーがゲームに参加でき、スキルを向上させるためにプレイを続けられるようにするために重要です。

サンプルレーザータグゲームを参考にして、このチュートリアルのセクションでは、スポーンとリスポーンを処理するためにRobloxの組み込み機能を使用し、カスタマイズする方法を教えます。これには、以下のスクリプトガイダンスが含まれます。

  • プレイヤーが自チームのスポーンゾーンにのみスポーンできるようにスポーン位置を設定する。
  • 新しいプレイヤーとそのキャラクターをゲームに参加する際にラウンドに追加する。
  • プレイヤーがスポーンおよびリスポーンする際にダメージを防ぐフォースフィールドをカスタマイズする。
  • ゲームプレイが適切なタイミングで正しく機能するようにクライアントの状態を処理する。
  • ラウンドからタグアウトされたキャラクターをリスポーンさせる。
  • ゲームプレイやキャラクターのパラメータを設定するために重要な小さな雑多なアクションを実行する。

このセクションには多くのスクリプトコンテンツが含まれていますが、ゲームを作成する際にすべてをゼロから書くのではなく、既存のコンポーネントを活用し、迅速に反復し、ビジョンに合ったカスタム実装が必要なシステムを見つけることを奨励します。このセクションを完了すると、ポイントを追跡し、プレイヤーの状態を監視し、ラウンドの結果を表示するラウンドベースのゲームプレイを実装する方法を学びます。

スポーン位置の設定

今すぐゲームをプレイテストすると、すべてのプレイヤーは緑チームのスポーンゾーンにあるSpawnLocationオブジェクトか、ピンクチームのスポーンゾーンにあるSpawnLocationオブジェクトのいずれかにランダムにスポーンします。これは、プレイヤーが相手のフォースフィールドが消えた瞬間に各スポーンゾーン内でお互いをタグできるというゲームプレイの問題を引き起こします。

この問題に対抗するために、サンプルレーザータグゲームは両方のスポーン位置をNeutralプロパティをfalseに設定して、対戦相手のチームのプレイヤーが間違ったスポーンゾーンにスポーンできないように制限し、TeamColorプロパティを前のチュートリアルセクションのチームカラーの割り当てからの対応するTeam.Color値に設定します。

  • TeamASpawnTeamColorプロパティがMintに設定された緑チームのスポーンゾーンのスポーン位置。
  • TeamBSpawnTeamColorプロパティがCarnation Pinkに設定されたピンクチームのスポーンゾーンのスポーン位置。
TeamASpawn
TeamBSpawn

プレイヤーがゲームに参加すると、ServerScriptServiceGameplayRoundsspawnPlayersInMapが各チームにすでにいるプレイヤーの数を確認し、最も少ないプレイヤーのチームを返します。

spawnPlayersInMap
local function getSmallestTeam(): Team
local teams = Teams:GetTeams()
-- チームを最小から最大の順にソート
table.sort(teams, function(teamA: Team, teamB: Team)
return #teamA:GetPlayers() < #teamB:GetPlayers()
end)
-- 最小のチームを返す
return teams[1]
end

最も少ないプレイヤーのチームがわかると、そのチームにプレイヤーを振り分け、プレイヤーのPlayer.Neutralプロパティをfalseに設定して、プレイヤーが自チームのスポーン位置にのみスポーンおよびリスポーンできるようにし、次にPlayerStateSelectingBlasterに設定します。これについては、後のチュートリアルで詳しく学びます。

spawnPlayersInMap
local function spawnPlayersInMap(players: { Player })
for _, player in players do
player.Team = getSmallestTeam()
player.Neutral = false
player:SetAttribute(PlayerAttribute.playerState, PlayerState.SelectingBlaster)
task.spawn(function()
player:LoadCharacter()
end)
end
end

WorkspaceWorldMapSpawnsを調べると、マップにもう1つのスポーン位置があることがわかります: NeutralSpawn。このスポーン位置は、ゲーム内の2つのチームのいずれかに設定されたTeamColorプロパティがないため、他のスポーン位置とは異なります。代わりに、このスポーン位置には、ラウンドがアクティブかどうかに応じて変わるNeutralプロパティがあります。

たとえば、ラウンドがアクティブな場合、Neutralプロパティはfalseに設定され、spawnPlayersInMapがプレイヤーをチームに振り分けてアリーナにスポーンできるようにします。しかし、ラウンドがアクティブでない場合、たとえば1ラウンドと次のラウンドの間の時間では、Neutralプロパティはtrueに設定され、プレイヤーはチームの状態に関係なくそこにスポーンできます。このプロセスにより、Neutralスポーン位置が機能的なロビーになります。

Neutral

デモとして、ServerScriptServiceGameplayRoundsSpawnPlayersInLobbyを調べると、ラウンドの終了時に実行されるこのスクリプトが、players: { Player }テーブルに渡されたすべてのプレイヤーに対して次の処理を行うことがわかります。

  • プレイヤーのPlayer.Neutralプロパティをtrueに設定して、プレイヤーのPlayer.Teamを自動的にnilにリセットし、ラウンドがアクティブでないときにロビーでリスポーンできるようにします。スポーン位置のNeutralプロパティもtrueに設定されています。
  • プレイヤーのPlayerStateInLobbyに変更して、プレイヤーのブラスターと一人称UIビジュアルを削除します。

中立スポーンゾーンと各ラウンドの機能についての詳細は、次のチュートリアルセクションのラウンドの追加を参照してください。

spawnPlayersInLobby
local function spawnPlayersInLobby(players: { Player })
for _, player in players do
player.Neutral = true
player:SetAttribute(PlayerAttribute.playerState, PlayerState.InLobby)
task.spawn(function()
player:LoadCharacter()
end)
end
end

新しいプレイヤーを接続する

スタジオのLuauコードは、イベント駆動型であることが多く、スクリプトはRobloxサービスからのイベントをリッスンし、その後、応答として関数を呼び出します。たとえば、マルチプレイヤーゲームに新しいプレイヤーを追加する際には、プレイヤーが正常に接続できるように必要なすべてを処理するイベントが必要です。サンプルレーザータグゲームでは、この対応するイベントはPlayers.PlayerAdded:Connectです。

Players.PlayerAdded:Connectは、ゲーム内の複数のスクリプトの一部です。Ctrl/Cmd+Shift+Fショートカットを使用してPlayers.PlayerAdded:Connectを検索すると、結果はゲームの初期設定を理解するための良い出発点を提供します。

StudioのFind AllウィンドウでPlayers.PlayerAddedの結果が強調表示されています。

デモとして、ServerScriptServiceSetupHumanoidを開いてください。PlayerCharacterの違いは、このスクリプトを理解するための重要なポイントです。

  • プレイヤーは接続されたクライアントであり、キャラクターHumanoidモデルです。
  • プレイヤーはブラスターを選択し、リーダーボードに追加される必要があります。キャラクターはスポーンし、ブラスターを受け取る必要があります。

SetupHumanoidは、プレイヤーがキャラクターを持っているか(参加したばかり)または持っていないか(リスポーン中)をすぐに確認します。キャラクターが見つかると、onCharacterAdded()を呼び出し、キャラクターからHumanoidモデルを取得し、カスタマイズのためにServerScriptServiceSetupHumanoidsetupHumanoidAsyncに渡します。これらの値を設定した後、スクリプトはキャラクターの健康がゼロになるのを待ちます。このセクションの後半でリスポーンについて詳しく学びます。

setupHumanoidAsync
local function setupHumanoidAsync(player: Player, humanoid: Humanoid)
humanoid.DisplayDistanceType = Enum.HumanoidDisplayDistanceType.Subject
humanoid.NameDisplayDistance = 1000
humanoid.HealthDisplayDistance = 1000
humanoid.NameOcclusion = Enum.NameOcclusion.OccludeAll
humanoid.HealthDisplayType = Enum.HumanoidHealthDisplayType.AlwaysOn
humanoid.BreakJointsOnDeath = false
humanoid.Died:Wait()
onHumanoidDied(player, humanoid)
end

このスクリプトの重要な点は、プロパティが完全にオプションであるため、関数の最初の6行を削除してもゲームは正常に動作することです。機能要件ではなく、各プロパティはゲームプレイの目標を満たすための設計上の決定を行うことを可能にします。たとえば:

  • キャラクター名を近くの距離で表示したい場合は、Humanoid.NameDisplayDistanceの値を減らします。
  • キャラクターの健康が100%未満の場合にのみ表示したい場合は、Humanoid.HealthDisplayTypeDisplayWhenDamagedに設定します。
  • キャラクターの健康が0に達したときにキャラクターが分解されるようにしたい場合は、Humanoid.BreakJointsOnDeathTrueに設定します。

これらのプロパティの値を変更した場合は、新しい設定の影響を確認するためにプレイテストを行うことが重要です。メザニンからServer & Clientsプレイテストを選択して、少なくとも2つのキャラクターを選択することで、プレイヤーが体験することを再現できます。

StudioのメザニンのテストモードドロップダウンのServer & Clientsオプション。

Players.PlayerAdded:Connectイベントの別の例は、ServerScriptServicePlayerStateHandlerにあります。前の例と同様に、PlayerStateHandlerはすぐにキャラクターを確認します。プレイヤーがロビーにいない場合、スクリプトはプレイヤー属性をSelectingBlaster状態に設定します。これは、プレイヤーがアリーナにスポーンした後に2種類の異なるブラスタータイプのいずれかを選択できるラウンドの初期状態です。この状態には、プレイヤーが選択を行っている間にダメージを受けないようにするフォースフィールドも含まれています。

PlayerStateHandler
local function onPlayerAdded(player: Player)
player.CharacterAdded:Connect(function()
if not player.Neutral then
player:SetAttribute(PlayerAttribute.playerState, PlayerState.SelectingBlaster)
onPlayerStateChanged(player, PlayerState.SelectingBlaster)
end
end)

PlayerStateHandlerの中で特に議論の余地がある変数は、attributeChangedConnectionByPlayerです。このテーブルは、すべてのプレイヤーとそのConnectionsGetAttributeChangedSignalに格納します。この接続をテーブルに格納する理由は、PlayerStateHandlerがプレイヤーがゲームを離れたときに切断できるようにするためです。このプロセスは、接続の数が時間とともに増え続けるのを防ぐためのメモリ管理の一種です。

PlayerStateHandler
local attributeChangedConnectionByPlayer = {}
local function onPlayerAdded(player: Player)
-- プレイヤー状態のすべての将来の更新を処理
attributeChangedConnectionByPlayer[player] = player
:GetAttributeChangedSignal(PlayerAttribute.playerState)
:Connect(function()
local newPlayerState = player:GetAttribute(PlayerAttribute.playerState)
onPlayerStateChanged(player, newPlayerState)
end)
end
-- プレイヤーが離れたときに属性変更接続を切断
local function onPlayerRemoving(player: Player)
if attributeChangedConnectionByPlayer[player] then
attributeChangedConnectionByPlayer[player]:Disconnect()
attributeChangedConnectionByPlayer[player] = nil
end
end

onPlayerAdded()内の両方の接続された関数はonPlayerStateChanged()を呼び出します。プレイヤーがチームに振り分けられた後の初期設定中に、onPlayerAdded()PlayerStateSelectingBlasterに設定するため、最初のif文はfalseと評価され、BlasterStateが無効になります。後のブラスターの実装セクションで、このプロセスの詳細について学びます。

PlayerStateHandler
local function onPlayerStateChanged(player: Player, newPlayerState: string)
-- プレイヤー状態が'Playing'のときのみブラスター状態は'Ready'
local newBlasterState = if newPlayerState == PlayerState.Playing then BlasterState.Ready else BlasterState.Disabled
-- プレイヤーがプレイを開始したときにフォースフィールドを破棄するロジックをスケジュール
if newPlayerState == PlayerState.Playing then
scheduleDestroyForceField(player)
end
player:SetAttribute(PlayerAttribute.blasterStateServer, newBlasterState)
end

ブレークポイントを追加したり、print()ステートメントを追加したりすると、onPlayerStateChanged()がゲーム全体で頻繁に呼び出されることがわかります。たとえば、ラウンドの初期設定中、メインコードパスに設定されたとき、プレイヤーがブラスターを選択したとき、プレイヤーがロビーに戻ったとき、またはNeutralスポーン位置に戻ったときなどです。さらに、プレイヤーがブラスターを選択した後、ServerScriptServiceBlasterSelectedHandlerPlayerStatePlayingに設定し、PlayerStateHandlerは最終的にscheduleDestroyForceField()を呼び出してフォースフィールドを削除できます。

フォースフィールドのカスタマイズ

サンプルレーザータグゲームでは、カスタム実装を使用するのではなく、Studioの組み込みForceFieldクラスを使用して、プレイヤーがブラスターを選択している間にダメージを受けないようにしています。これにより、プレイヤーがフォースフィールドを持ってスポーンするための唯一の要件は、SpawnLocation.Durationプロパティが0より大きいスポーン位置を含めることです。サンプルでは、フォースフィールドを有効にするために任意の値9,999を使用し、実際の期間はReplicatedStorageForceFieldClientVisualsでプログラム的に処理します。

setupHumanoidAsyncと同様に、ForceFieldClientVisuals内のほとんどの行はオプションです。たとえば、次のスクリプトのように関数の内容をコメントアウトすると、ゲームはStarterGuiForceFieldGui内の六角形スクリプトの代わりにデフォルトのスパークリングフォースフィールドを使用します。

Commenting Out Properties in ForceFieldClientVisuals
local function onCharacterAddedAsync(character: Model)
-- local forceField = character:WaitForChild("ForceField", 3)
-- if not forceField then
-- return
-- end
-- forceField.Visible = false
-- localPlayer.PlayerGui:WaitForChild("ForceFieldGui").Enabled = true
-- forceField.Destroying:Wait()
-- localPlayer.PlayerGui.ForceFieldGui.Enabled = false
end

カスタムフォースフィールドはGUIであり、新しいParticleEmitterではないため、ForceFieldClientVisualsスクリプトは各プレイヤーの一人称ビジュアルにのみ影響し、他のプレイヤーを見たときの三人称ビジュアルには影響しません。三人称ビジュアルはデフォルトのRobloxの外観を保持します。フォースフィールドの変更に関する詳細は、ForceField.Visibleを参照してください。

一人称フォースフィールドビジュアルには、画面の周囲に未来的な六角形のグリッドが含まれています。
一人称フォースフィールドビジュアル
三人称フォースフィールドビジュアルには、ゲームにスポーンするプレイヤーの周りに青いスパークリングオーブが含まれています。
三人称フォースフィールドビジュアル

フォースフィールドは、プレイヤーがスポーンとリスポーンの間に敵プレイヤーを気にせずに十分な時間を提供するために便利ですが、最終的にはメインのレーザータグゲームプレイのために消える必要があります。フォースフィールドの削除を処理するスクリプトはReplicatedStoragescheduleDestroyForceFieldにあり、3つのユニークな条件をチェックします。

  • プレイヤーがブラスターを選択した後、フォースフィールドはプレイヤーが周囲に慣れるのに十分な時間を持つ必要があります。
  • この慣れの時間中、フォースフィールドはアドバンテージになってはいけないため、プレイヤーがブラスターを撃った瞬間に消える必要があります。
  • プレイヤーがブラスターを撃つ前やフォースフィールドのタイムアウト前にキャラクターをリセットする場合、フォースフィールドは消える必要があります。

scheduleDestroyForceFieldスクリプト内のこれらのチェックは、これらの条件に対してendForceField()を呼び出します。

scheduleDestroyForceField
-- プレイヤーがブラスターを撃った場合、フォースフィールドを終了
local blasterStateAttribute = getBlasterStateAttribute()
attributeChangedConnection = player:GetAttributeChangedSignal(blasterStateAttribute):Connect(function()
local currentBlasterState = player:GetAttribute(blasterStateAttribute)
if currentBlasterState == BlasterState.Blasting then
endForceField()
end
end)
-- プレイヤーがリセットした場合、フォースフィールドを終了
characterRespawnedConnection = player.CharacterRemoving:Connect(endForceField)
-- 8秒後にフォースフィールドを終了
task.delay(MAX_FORCE_FIELD_TIME, endForceField)

endForceField()には、forceFieldEndedブール値の周りに一見奇妙なif文が含まれています。チェックが順次実行されるため、スクリプトはendForceField()関数を2回または3回呼び出すことができます。forceFieldEndedブール値は、関数がフォースフィールドを一度だけ破壊しようとすることを保証します。

scheduleDestroyForceField
local function endForceField()
if forceFieldEnded then
return
end
forceFieldEnded = true
attributeChangedConnection:Disconnect()
characterRespawnedConnection:Disconnect()
destroyForceField(player)
end

クライアント状態の処理

このセクションのほとんどはServerScriptServicePlayerStateHandlerに焦点を当てていますが、ReplicatedStorageにも同名の別のスクリプトがあります。分割の理由は、クライアント-サーバーアーキテクチャです。

  • クライアントは、リアルタイムで適切に応答できるようにプレイヤー状態情報を理解する必要があります。たとえば、正しいユーザーインターフェース要素を表示したり、プレイヤーが移動したりブラスターを撃ったりできるようにします。

  • サーバーは、エクスプロイトを防ぐために同じ情報を必要とします。たとえば、サーバーはキャラクターをスポーンおよび装備したり、フォースフィールドを無効にしたり、リーダーボードを表示したりするためにプレイヤー状態を必要とします。このため、このスクリプトはReplicatedStorageにあり、純粋にクライアント側の場所にはありません。

このコアロジックを確認するには、ユーザーの現在の状態を検証し、その状態に対応するアクションを処理する適切な関数を呼び出すReplicatedStoragePlayerStateHandler内の次のスクリプトを確認してください。

PlayerStateHandler
local function onPlayerStateChanged(newPlayerState: string)
if newPlayerState == PlayerState.SelectingBlaster then
onSelectingBlaster()
elseif newPlayerState == PlayerState.Playing then
onPlaying()
elseif newPlayerState == PlayerState.TaggedOut then
onTaggedOut()
elseif newPlayerState == PlayerState.InLobby then
onInLobby()
else
warn(`無効なプレイヤー状態 ({newPlayerState})`)
end
end

すべてのイベント応答は、このスクリプト内で論理的にグループ化されており、プレイヤーコントロール、カメラの動き、どのUIレイヤーが表示されるかを有効または無効にするために類似の動作が必要です。たとえば、ブラスター選択中は、プレイヤーは無敵であり、移動できない必要があります。サーバーはすでにフォースフィールドを処理していますが、クライアントは移動を処理します。デモとして、onSelectingBlaster()関数のロジックを確認すると、プレイヤーがブラスターを選択している間、クライアントがプレイヤーの移動を無効にすることがわかります。

PlayerStateHandler
local function onSelectingBlaster()
togglePlayerCamera(true)
togglePlayerMovement(false)
setGuiExclusivelyEnabled(playerGui.PickABlasterGui)
localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Disabled)
end

onPlaying()関数も同様に簡単です。移動を有効にし、メインのヘッドアップディスプレイ(HUD)に移行し、ブラスターを有効にし、サーバーと同じフォースフィールド関数を呼び出します。

PlayerStateHandler
local function onPlaying()
togglePlayerMovement(true)
setGuiExclusivelyEnabled(playerGui.HUDGui)
localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Ready)
scheduleDestroyForceField()
end

キャラクターのリスポーン

サンプルレーザータグゲームは、ReplicatedStoragePlayerStateHandler内のonTaggedOut()状態を通じてキャラクターをラウンドにリスポーンさせます。onSelectingBlaster()およびonPlaying()状態と同様に、onTaggedOut()playerState属性の変更に応じてユニークな動作をトリガーします。具体的には、プレイヤーの移動を無効にし、リスポーンUIを表示し、ブラスターを無効にします。

PlayerStateHandler
local function onTaggedOut()
-- タグアウト中はコントロールを無効にする
togglePlayerMovement(false)
togglePlayerCamera(false)
setGuiExclusivelyEnabled(playerGui.OutStateGui)
-- タグアウト中はブラスターを無効にする
localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Disabled)
end

この動作をテストしたい場合は、Escを押して設定タブに移動し、キャラクターをリセットボタンをクリックします。リスポーン画面をトリガーすると、移動、カメラの回転、ブラスターを撃つことができなくなることに注意してください。

Robloxの設定メニューでリセットキャラクターボタンが強調表示されています。
リセットキャラクターボタン
リスポーン画面がプレイヤーがラウンドにリスポーンする際に表示されます。
リスポーン画面

このスクリプトは実際にはキャラクターをリスポーンさせるのではなく、プレイヤーがサーバーによってキャラクターがリスポーンされていることを視覚的にフィードバックするために、行動を停止させるだけであることに注意することが重要です。デモとして、ServerScriptServiceSetupHumanoidsetupHumanoidAsynconHumanoidDiedを調べると、スクリプトはPlayerStateTaggedOutに設定し(実質的にReplicatedStoragePlayerStateHandlerに通知)、いくつかの視覚的インジケーターを追加します。実際のリスポーンのロジックは、Robloxの組み込みの動作です。

プレイヤーがラウンドにリスポーンすると、SpawnLocation.TeamColorプロパティに従って自チームのスポーン位置にリスポーンします。リスポーン時間をカスタマイズするには、SetupHumanoidの先頭に次の行を追加します。この技術についての詳細は、Players.RespawnTimeを参照してください。

SetupHumanoid
local Players = game:GetService("Players")
Players.RespawnTime = 10 -- 新しい行、秒単位

雑多な設定

初期設定の一環として、サンプルレーザータグゲームは、いくつかの小さなが重要なステップも実行します。

  • ゲームには、デフォルトのRoblox健康再生を無効にする空のスクリプトStarterPlayerStarterCharacterScriptsHealthが含まれています。このプロパティの動作についての説明は、Humanoid.Healthを参照してください。

  • ゲームは、StarterPlayer.CameraMode.LockFirstPersonプロパティを設定することで一人称カメラを使用します。ユーザーが一人称と三人称のカメラを切り替えられるようにしたい場合は、プロパティをスタジオで一度設定するだけでなく、プログラム的に変更し、視点の変化に対応するためにコントロールとUIを修正する必要があります。

  • ゲームは、プレイヤーが他のプレイヤーをタグするたびに獲得する「ポイント」の単位で、組み込みのRobloxリーダーボードを使用します。設定はServerScriptServiceSetupLeaderboardで確認できますが、ゲーム内リーダーボードが全体の概要を提供します。onPlayerTaggedはリーダーボードにポイントを追加します。これは、ラウンドの追加ヒットの検出で学ぶことになります。

プレイヤーがスポーンし、ブラスターを選択し、一人称の視点から狙うことができるようになったので、次のセクションではラウンドベースのゲームプレイを作成するためのスクリプトについて学びます。

©2026 Roblox Corporation。Roblox(ロブロックス)、RobloxロゴおよびPowering Imaginationは、米国並びにその他の国における登録商標および非登録商標です。