スポーンとリスポーン

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

スポーン中 は、エクスペリエンスでオブジェクトやキャラクターを作成するプロセスであり、 リスポーン中 は、キャラクターの体力がゼロになったり、マップから落ちたりするような削除条件に出会った後、オブジェクトやキャラクターをエクスペリエンスに戻すプロセスです。両方のプロセスは重要であり、プレイヤーがエクスペリエンスに参加でき、スキルを向上させるためにプレイを続けることができるからです。

サンプルレーザータグエクスペリエンスを参考にして、このチュートリアルのこのセクションでは、RoblRoblox(ロブロックス) の内蔵機能を使用して、スポーンとリスポーンを処理する方法、スクリプトガイドラインを含む、以下の内容について学習します:

  • プレイヤーが自分のチームのスポーンゾーンにのみスポーンできるように、スポーン場所を構成する。
  • 新しいプレイヤーと彼らのキャラクターを、経験に参加するときにラウンドに追加する。
  • プレイヤーがスポーンしてリスポーンするときにダメージを防ぐ力場をカスタマイズする。
  • ゲームプレイが適切な時に正しく機能するように、クライアント状態を処理する。
  • ラウンドからタグが外された後、キャラクターをリスポーンする。
  • ゲームプレイとキャラクターパラメータを設定するのに重要な小さな、雑多なアクションを実行する。

このセクションには、多くのスクリプトコンテンツが含まれていますが、エクスペリエンスを作成するときにすべてをスクラッチから書くのではなく、既存のコンポーネントを活用し、迅速に反復し、ビジョンにマッチするカスタム実装が必要なシステムを特定することを推奨します。このセクションを完了すると、ポイントを追跡し、プレイヤーの状態をモニタリングし、ラウンド結果を表示するラウンドベースのゲームプレイを実装する方法を学びます。

スポーン場所を構成する

今すぐエクスペリエンスをプレイテストした場合、すべてのプレイヤーがランダムに緑のチームのスポーンゾーンまたはピンクのチームのスポーンゾーンの オブジェクトにスポーンします。これは、プレイヤーが相手の力場が消えた瞬間に、各スポーンゾーン内でお互いをタグできるゲームプレイの問題を提示します。

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

  • TeamASpawn – グリーンチームのスポーンゾーンで、TeamColor プロパティが ミント に設定されたスポーン場所。
  • TeamBSpawn – ピンクチームのスポーンゾーンで、TeamColor プロパティが カーネーションピンク に設定されたスポーン場所。

チームASpawn
>

チームBSpawn
>

プレイヤーがエクスペリエンスに参加すると、 サーバースクリプトサービス > ゲームプレイ > ラウンド > 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

チームに最少数のプレイヤーがいることを知ったら、そのチームにプレイヤーを排列し、プレイヤーの プロパティを 偽 に設定して、プレイヤーが自分のチームのスポーン場所にのみスポーンし、リスポーンできるようにし、後でチュートリアルで詳しく学ぶことになる を設定します。

スポーンプレイヤーインマップ

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

ワークスペース > ワールド > マップ > スポーン > ニュートラルスポーン を調べると、マップにスポーン場所が1つ増えていることがわかります:ニュートラルスポーン。このスポーン場所は、エクスペリエンスの 2つのチームのいずれかに設定された TeamColor プロパティがないため、他のものとユニークです;代わりに、このスポーン場所には、ラウンドがアクティブかどうかに応じて変化する Neutral プロパティがあります。

たとえば、ラウンドが有効である場合、Neutral プロパティは false に設定され、 spawnPlayersInMap でプレイヤーをチームに分け、アリーナにスポーンできます。しかし、ラウンドがアクティブでない場合、例えば、1ラウンドと次の間の時間、Neutral プロパティは true に設定され、プレイヤーはチームのステータスに関係なくそこにスポーンできます。このプロセスは、 中立 スポーン場所を機能するロビーにするものです。

中立

デモを示すには、 サーバースクリプトサービス > ゲームプレイ > ラウンド > SpawnPlayersInLobby で、ラウンドの終わりに実行されるスクリプト:

  • その Player.Neutral プロパティを真に設定して、自動的にその Player.Teamnil にリセットし、プレイヤーがラウンドがアクティブでないときにロビーで再生できるように、スポーン場所の Neutral プロパティも true に設定されます。
  • プレイヤーのブラスターとファーストパーソン UI ビジュアルを削除するために、PlayerStateInLobby に変更

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

ロビーでプレイヤーをスポーンする

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 は、エクスペリエンスの複数のスクリプトの一部です。 ショートカットを使用して を検索すると、結果はエクスペリエンスの初期設定を理解するのに良い出発点を提供します。

Studio's Find All window with the Players.PlayerAdded results highlighted.

デモンストレーションするには、 サーバースクリプトサービス > SetupHumanoid を開きます。PlayerCharacter の区別は、このスクリプトを理解するための鍵です:

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

SetupHumanoid すぐにプレイヤーがキャラクターを持っているか (新しく参加した) 否か (リスポーン中) をチェックします。それが 1つ見つかった後、onCharacterAdded() を呼び出し、キャラクタからHumanoidモデルを取得し、それを ServerScriptService > SetupHumanoid > setupHumanoidAsync にパスしてカスタマイズします。これらの値を設定した後、スクリプトはキャラクターの健康がゼロに達するのを待ちます。チュートリアルのこのセクションでは、後でリスポーンについて詳しく学びます。

設定ヒューマノイドAsync

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.HealthDisplayTypeダメージを受けたときに表示する に設定します。
  • キャラクターが体力が 0 になると分離するようにしたい場合は、Humanoid.BreakJointsOnDeathTrue に設定します。

これらのプロパティの値を変更する場合、新しい設定の影響を見るためにプレイテストすることが重要です。テストタブの クライアントとサーバー セクションで少なくとも 2 文字を選択して、プレイヤーがマルチプレイヤー環境で経験するものを再現できます。

Studio's Test tab with the the players dropdown highlighted. This setting needs to be at least two players to see the impact of your new settings.

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

プレイヤーステートハンドラー

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 プレイヤーがエクスペリエンスを終了するときに、 切断する ことができるようにするためです。このプロセスは、接続数が時間経過に増加するのを防ぐ種類のメモリ管理として機能します。

プレイヤーステートハンドラー

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 文が偽に評価され、BlasterState を無効にします。チュートリアルの後半の ブラスターを実装する セクションでは、このプロセスについて詳しく学びます。

プレイヤーステートハンドラー

local function onPlayerStateChanged(player: Player, newPlayerState: string)
-- ブラスター状態は、プレイヤー状態が「プレイ中」の場合にのみ「準備完了」です
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() が頻繁に呼び出されることがわかります:例えば、ラウンドの初期設定中、メインコードパスに設定するため、プレイヤーがブラスターを選択したとき、またはプレイヤーがロビーに戻ったとき、または ニュートラル スポーン場所。さらに、プレイヤーがブラスターを選択した後、 サーバースクリプトサービス > ブラスター選択ハンドラー は、 を に設定し、 は最終的にフォースフィールドを削除するように呼び出します。

力場をカスタマイズする

カスタム実装を使用するのではなく、サンプルレーザータグエクスペリエンスは、Studio の内蔵クラス ForceField を使用して、プレイヤーがブラスターを選択している間にダメージを受けないようにします。これにより、プレイヤーがフォースフィールドでスポーンする唯一の要件は、0以上の SpawnLocation.Duration プロパティを持つスポーン場所を含むことになります。サンプルは、任意の値 9,999 を使用してフォースフィールドを有効にし、 ReplicatedStorage > ForceFieldClientVisuals で実際の期間をプログラマティックに処理します。

setupHumanoidAsync と同様に、ForceFieldClientVisuals のラインのほとんどはオプションです。たとえば、次のスクリプトのように機能の内容をコメントアウトすると、エクスペリエンスは StarterGui > ForceFieldGui のヘキサゴナルスクリプトではなく、デフォルトのスパークリングフォースフィールドを使用します。

ForceFieldClientVisuals でプロパティをコメントアウトする

local function onCharacterAddedAsync(character: Model)
-- ローカルフォースフィールド = キャラクター:WaitForChild("ForceField", 3)
-- forceField でない場合
-- 返す
-- 終了
-- forceField.Visible = false
-- localPlayer.PlayerGui:WaitForChild("ForceFieldGui") が有効になった = 真
-- forceField.Destroying:待つ()
-- localPlayer.PlayerGui.ForceFieldGui.Enabled = 無効
end

カスタムフォースフィールドが新しい ParticleEmitter ではなく GUIであるため、ForceFieldClientVisualsスクリプトは、プレイヤーが他のプレイヤーを見るときの1プレイヤービジュアルにのみ影響し、 **** プレイヤーが他のプレイヤーを見るときの3プレイヤービジュアルには影響しません。第三者視覚はデフォルトの Roblox 外外見を維持します。フォースフィールドの修正に関する詳細は、ForceField.Visible を参照してください。

First-person force field visuals include a futuristic hexagonal grid on the perimeter of the screen.

1人称フォースフィールドビジュアル
>

Third-person force field visuals include a blue sparkling orb around the player spawning into the experience.

第三者視点のフォースフィールドビジュアル
>

フォースフィールドは有用です、プレイヤーにスポーンとリスポーンの間の十分な時間を提供し、敵のプレイヤーについて心配する必要はありませんが、最終的にメインのレーザータグゲームプレイのために消滅する必要がありますフォースフィールド除去を処理するスクリプトは、 ReplicatedStorage > scheduleDestroyForceField にあり、3つのユニークな条件をチェックします:

  • プレイヤーがブラスターを選択した後、フォースフィールドはプレイヤーが周囲の環境に慣れるのに十分な時間続く必要があります。
  • この馴染み時間中、フォースフィールドは優位性にはならないので、プレイヤーがブラスターを爆破する瞬間に消える必要があります。
  • プレイヤーがキャラクターをリセットすると、フォースフィールドは爆発する前かフォースフィールドの時間切れ前に消滅する必要があります。

これらのチェックは、scheduleDestroyForceField スクリプトコールで endForceField() これらの条件をチェックします。

スケジュール破壊フォースフィールド

-- プレイヤーが爆破した場合の力場を終わらせる
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() には、見た目の奇妙な if 文が forceFieldEnded ブールの周りに含まれています。チェックが順次実行されるため、スクリプトは endForceField() 関数を 2 回または 3 回呼び出すことができます。forceFieldEnded ブールンは、機能がフォースフィールドを破壊するのは一度だけであることを保証します。

スケジュール破壊フォースフィールド

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

クライアント状態を処理

このセクションの大部分は ServerScriptService > PlayerStateHandler に焦点を当てていますが、 ReplicatedStorage に同じ名前の別のスクリプトがあります。分離の理由は、クライアント-サーバーアーキテクチャです:

  • クライアントは、プレイヤーの状態情報を理解する必要があり、実時間に適切に応答できるように、正しいユーザーインターフェイス要素を表示するか、プレイヤーが移動して爆発できるようにします。

  • サーバーは、悪用を防ぐために、これらすべての情報が必要です。たとえば、サーバーは、キャラクターのスポーンや装備、フォースフィールドの無効化、リーダーボードの表示などのアクションを実行するには、プレイヤー状態が必要です。これが、このスクリプトが ReplicatedStorage にある理由で、純粋なクライアント側の場所ではありません。

このコアロジックを見るには、 ReplicatedStorage > 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(`Invalid player state ({newPlayerState})`)
end
end

すべてのイベント応答は、プレイヤーのコントロールの有効操作または無効化、カメラの移動、どの UI レイヤーが表示されているかといった同様の動作が必要なため、このスクリプトで論理的にグループ化されます。例えば、ブラスターの選択中、プレイヤーは侵襲不可能で移動できない必要があります。サーバーはすでにフォースフィールドを処理していますが、クライアントは移動を処理します。デモンストレーションするには、onSelectingBlaster() 関数のロジックをチェックすると、クライアントがブラスターを選択している間、プレイヤーの移動を無効にすることができます。

プレイヤーステートハンドラー

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

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

プレイヤーステートハンドラー

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

キャラクターをリスポーンする

サンプルレーザータグエクスペリエンスは、onTaggedOut() 状態を介して ReplicatedStorage > PlayerStateHandler で再生キャラクターを回収し、ラウンドに戻します。onSelectingBlaster()onPlaying() 状態のように、onTaggedOut() は、playerState 属性の変更に応じてユニークな動作をトリガーします。特に、プレイヤーの移動を無効にし、リスポーン UI を表示し、ブラスターを無効にします。

プレイヤーステートハンドラー

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

この動作をテストしたい場合は、Esc を押し、 設定 タブに移動し、 キャラクターをリセット ボタンをクリックします。リスポーン画面をトリガーすると、移動したり、カメラを回転させたり、ブラスターを爆破したりすることはできません。

Roblox's settings menu with the Reset Character button highlighted.

キャラクターリセットボタン
>

The respawn screen displays as a player respawns back into the round.

リスポーン画面
>

このスクリプトは実際にキャラクターを再スポーンしていないことに注意することが重要です、単にアクションを停止し、 サーバー がキャラクターを再スポーンしているプレイヤーにビジュアルフィードバックを提供します。デモを示すには、 ServerScriptService > SetupHumanoid > setupHumanoidAsync > onHumanoidDied でスクリプトを設定し、PlayerStateTaggedOut (基本的に ReplicatedStorage > PlayerStateHandler を通知し、視覚的な指標を追加します)、およびいくつかのビジュアル指標を追加します。リスポーンの実際のロジックは、組み込みの Roblox 動作です。

プレイヤーがラウンドに再出現すると、SpawnLocation.TeamColor プロパティに従ってチームのスポーン場所で再出現します。リスポーン時間をカスタマイズするには、SetupHumanoid のトップに次の行を追加できます。この技術について詳しく学ぶには、Players.RespawnTime を参照してください。

ヒューマノイドの設定

local Players = game:GetService("Players")
Players.RespawnTime = 10 -- new line, in seconds

その他の設定

初期設定の一環として、サンプルレーザータグエクスペリエンスも、小さながらも重要なステップを実行します:

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

  • エクスペリエンスは、StarterPlayer.CameraMode.LockFirstPerson プロパティを設定して、一人称カメラを使用します。ユーザーが第一人称と第三人称のカメラを交換できるようにしたい場合は、Studio で一度だけ設定するのではなく、プロパティをプログラマティックに変更し、視野の変更を補正するためにコントロールと UI を変更する必要があります。

  • エクスペリエンスは、プレイヤーが他のプレイヤーをタグアウトするたびに獲得する「ポイント」の内蔵 Roblox リーダーボードを使用します。 サーバースクリプトサービス の構成を 設定リーダーボード で見ることができますが、 エクスペリエンス内のリーダーボード は全体の概要を提供します。注: onPlayerTagged は、リーダーボードにポイントを追加しますが、それについては ラウンドを追加ヒットを検出 で学びます。

プレイヤーがスポーンできるようになり、ブラスターを選んで、1プレイヤーの視点から狙えるようになったので、次のセクションでは、ラウンドベースのゲームプレイを作成するためのスクリプトについて教えます。