ブラスターの動作を実装するとは、ファーストパーソンシューター体験におけるブラストメカニクスをプログラミングするプロセスです。プレイヤーはボタンを1回クリックまたは押すことでブラストできますが、満足のいく正確なブラスト動作を作成することが重要です。これにより、プレイヤーの全体的なゲームプレイの楽しみが向上します。
サンプルレーザータグ体験を参考にして、このチュートリアルのセクションでは、ブラスターの動作を実装するためのスクリプトについて学び、次のことを含む2種類のブラスターのガイダンスを提供します:
- プレイヤーがブラストボタンを押したときを検出する。
- プレイヤーが最近ブラストボタンを押した場合、そのプレイヤーがブラスターを使用できるかどうかを確認する。
- ブラストデータを生成し、サーバーにブラストを開始したプレイヤー、それがどこから来たか、そして各レーザービームの最終目的地について伝える。
- ブラストデータをサーバーに通知し、ブラストが他のプレイヤーと衝突した場合に適切なアクションを実行できるようにする。
- 各ブラストの間にブラスターをリセットし、次のブラストが可能になるまでに十分なクールダウン時間を与える。
このセクションを完了すると、ブラスターが他のプレイヤーと衝突したときに検出し、各ブラスタータイプに応じて相応の健康を減少させることを可能にするスクリプトについて学びます。
プレイヤー入力を検出する
ブラスターの動作を実装する最初のステップは、プレイヤーがブラストボタンを押すのをリッスンすることです。プレイヤーがブラストボタンを押すために使用する入力タイプは、体験にアクセスするために使用しているデバイスによって異なります。たとえば、サンプルレーザータグ体験は、マウスおよびキーボードコントロール、ゲームパッド、タッチコントロールをサポートしています。これらの各入力タイプはReplicatedStorage ⟩ UserInputHandlerで確認できます。
このクライアントスクリプトは、ContextActionServiceを使用して、MouseButton1およびButtonR2をブラストアクションにバインドします。これは、プレイヤーが左マウスボタンまたはゲームパッドのR2ボタンを押すたびに、ブラスターからレーザービームが発射されることを意味します。モバイルデバイス用のブラストボタンはHUDGuiにあり、スクリプト内で後で接続されます。
UserInputHandlerContextActionService:BindAction("_", onBlasterActivated, false,Enum.UserInputType.MouseButton1,Enum.KeyCode.ButtonR2)
もう一つ重要なポイントは、onBlasterActivated()定義内でのEnum.UserInputState.Beginの使用です。ブラストを選ぶといった多くのユーザーインターフェースのインタラクションは、マウスボタンが上がった後(Enum.UserInputState.End)でないと発生しないため、ユーザーにはインタラクションを避けるための最後のチャンスが与えられます。しかし、ブラストメカニクスはボタンを押した瞬間に発生しない限り、応答性を感じません。
これを示すために、Enum.UserInputState.BeginをEnum.UserInputState.Endに変更し、プレイテストを行ってブラストの応答性が体験のゲームプレイにどのように影響するかを確認できます。たとえば、プレイヤーがボタンを押し続けてもブラストがトリガーされない場合、他のプレイヤーにタグを付ける際の体験がどのように変わるでしょうか?
UserInputHandler
local function onBlasterActivated(_actionName: string,
inputState: Enum.UserInputState, _inputObject: InputObject)
if inputState == Enum.UserInputState.End then -- 更新された行、必ず元に戻してください
attemptBlastClient()
end
end
プレイヤーがブラストできるか確認する
UserInputHandlerがボタン押下や画面タップを検出した後、ReplicatedStorage ⟩ Blaster ⟩ attemptBlastClientを呼び出して、プレイヤーがブラストできるかどうかを確認します。サンプルレーザータグ体験内のほとんどのチェックと同様に、これはクライアントで最初に、次にサーバーで発生します。attemptBlastClientは次にReplicatedStorage ⟩ Blaster ⟩ canLocalPlayerBlastを呼び出して、blasterStateClientプレイヤー属性のシンプルなチェックを行います:
canLocalPlayerBlast
local function canLocalPlayerBlast(): boolean
return localPlayer:GetAttribute(PlayerAttribute.blasterStateClient) == BlasterState.Ready
end
ReplicatedStorage ⟩ Blaster ⟩ BlasterStateを調べると、体験には3つのブラスターステートがあることが確認できます:Ready、Blasting、Disabled。これらの各状態の効果を確認するために、体験をプレイテストし、Playersサービスの下で自分のプレイヤーを選択し、PropertiesウィンドウでblasterStateClient属性を観察します。選択中はDisabledが表示され、ほとんどの時間Readyが表示され、ボタンを押した後はBlastingが1秒未満の間表示されることに注意してください。
このわずかなポーズにより、できるだけ速くブラストできないようになります。たとえば、関数を常にtrueを返すように変更すると、遅延なしでブラスターを急速にブラストでき、レーザータグゲームプレイには不自然です。
canLocalPlayerBlast
local function canLocalPlayerBlast(): boolean
return true -- 更新された行、必ず元に戻してください
end
ブラストデータを生成する
プレイヤーのブラスターがReady状態にあることを確認した後、attemptBlastClientはReplicatedStorage ⟩ attemptBlastClient ⟩ blastClientを呼び出します。blastClientが最初に行うステップは、blasterStateClientプレイヤー属性をBlastingに設定することです。これにより、以前の急速発射のケースを回避できます。
次のステップは、ブラストデータを生成することです。ReplicatedStorage ⟩ Blaster ⟩ BlastDataを確認すると、各ブラストは次の3つの情報で構成されていることがわかります:
- ブラストを開始したプレイヤー。
- ブラストの発生点を表すDataType.CFrame。
- もし他のプレイヤーにヒットした場合、そのヒットプレイヤーと各レーザービームの最終目的地を含むRayResultテーブル。
このデータを生成するために、blastClientはReplicatedStorage ⟩ attemptBlastClient ⟩ blastClient ⟩ generateBlastDataを呼び出します。この関数をここで確認できます。
generateBlastData
local function generateBlastData(): BlastData.Type
local blasterConfig = getBlasterConfig()
local rayDirections = getDirectionsForBlast(
currentCamera.CFrame, blasterConfig)
local rayResults = castLaserRay(
localPlayer, currentCamera.CFrame.Position, rayDirections)
local blastData: BlastData.Type = {
player = localPlayer,
originCFrame = currentCamera.CFrame,
rayResults = rayResults,
}
return blastData
end
この関数は、最初にgetBlasterConfigを使用してプレイヤーのブラスタータイプを取得します。サンプルには、幅の広い水平スプレッドで複数のビームを生成するブラスターと、単一のビームを生成するブラスターの2種類があります。これらの構成はReplicatedStorage ⟩ Instances ⟩ LaserBlastersFolderで見つけることができます。
次に、この関数はcurrentCamera.CFrameをブラストの発生点として使用し、これをgetDirectionsForBlastに渡します。この時点で、コードはもはやブラスターについてではなく、レーザービームについてのものです。あなたは、チュートリアルのヒットを検出するセクションでこれについてさらに学びます。最後に、rayResultsテーブルを作成した後、generateBlastDataはブラストデータをblastClientに返すために必要なすべての情報を得ます。
サーバーに通知する
blastClientがブラストの完全なデータを持つと、2つのイベントを発火します:
blastClientlocal laserBlastedBindableEvent = ReplicatedStorage.Instances.LaserBlastedBindableEventlocal laserBlastedEvent = ReplicatedStorage.Instances.LaserBlastedEventlaserBlastedBindableEvent:Fire(blastData)laserBlastedEvent:FireServer(blastData)
BindableEventは、ブラストを他のクライアントスクリプトに通知します。たとえば、ReplicatedStorage ⟩ FirstPersonBlasterVisualsは、このイベントを使用してブラストアニメーションやクールダウンバーなどのビジュアル効果を表示するタイミングを知るのに利用されます。同様に、RemoteEventはサーバースクリプトにブラストを通知し、サーバーはServerScriptService ⟩ LaserBlastHandler内でブラストの処理を開始します。
LaserBlastHandler
local function onLaserBlastedEvent(playerBlasted: Player, blastData: BlastData.Type)
local validatedBlastData = getValidatedBlastData(playerBlasted, blastData)
if not validatedBlastData then
return
end
if not canPlayerBlast(playerBlasted) then
return
end
blastServer(playerBlasted)
processTaggedPlayers(playerBlasted, blastData)
for _, replicateToPlayer in Players:GetPlayers() do
if playerBlasted == replicateToPlayer then
continue
end
replicateBlastEvent:FireClient(replicateToPlayer, playerBlasted, blastData)
end
end
チートを防ぐために、サーバーは各クライアントが送信するすべてのデータを検証する必要があります。これらのチェックには次のものが含まれます:
- BlastDataはテーブルですか?それはClass.CFrameと別のテーブルrayResultsを含んでいますか?
- プレイヤーはブラスターを装備していますか?
- プレイヤーは世界内にキャラクターと位置を持っていますか?
- ブラストデータを送信した後、プレイヤーはレーザー ビームをブラストした場所から過度に離れた位置に移動していませんか?
最後のチェックは判断を必要とし、サーバーのレイテンシやプレイヤーの移動速度に応じて、体験によって異なる値が許容されるかもしれません。この判断を行う方法を示すために、getValidatedBlastDataにプラントステートメントを追加し、体験をプレイテストして、位置の変化の一般的な大きさを確認することができます。
getValidatedBlastDatalocal distanceFromCharacterToOrigin = blastData.originCFrame.Position - rootPartCFrame.Positionprint(distanceFromCharacterToOrigin.Magnitude) -- 更新された行、必ず削除してくださいif distanceFromCharacterToOrigin.Magnitude > ToleranceValues.DISTANCE_SANITY_CHECK_TOLERANCE_STUDS thenwarn(`Player {player.Name} failed an origin sanity check while blasting`)returnend
移動してブラストすると、その出力をメモしてください。それは次のように見えるかもしれません:
1.90196299552917483.15495586395263672.57428836822509774.80445861816406252.6434271335601807
ReplicatedStorage ⟩ PlayerStateHandler ⟩ togglePlayerMovementでプレイヤーの移動速度を増加させると、体験を再度プレイテストした場合、ブラストの間に過度の移動のために多くの失敗したチェックに遭遇することがあるでしょう。
togglePlayerMovementlocal ENABLED_WALK_SPEED = 60 -- 更新された行、必ず元に戻してください
その後、サーバーは次のことを行います:
- rayResultsを検証します。
- プレイヤーがブラストできるか確認します。
- ブラスターの状態をリセットします。
- タグ付けされたプレイヤーの健康を減少させます。
- 他のすべてのプレイヤーにブラストを複製し、サードパーソンのビジュアルを表示させます。
これらのサーバー操作に関する詳細は、チュートリアルのヒットを検出するセクションを参照してください。
ブラスターをリセットする
サンプルのレーザータグ体験では、ブラスターは熱メカニクスを使用します。設定された数のブラストの後に再装填するのではなく、各ブラストの間に「クールダウン」する時間が必要です。この同じクールダウン遅延は、クライアント(blastClient)とサーバー(blastServer)の両方で発生し、サーバーが真実の源として機能します。
blastServer
local blasterConfig = getBlasterConfig(player)
local secondsBetweenBlasts = blasterConfig:GetAttribute("secondsBetweenBlasts")
task.delay(secondsBetweenBlasts, function()
local currentState = player:GetAttribute(PlayerAttribute.blasterStateServer)
if currentState == BlasterState.Blasting then
player:SetAttribute(PlayerAttribute.blasterStateServer, BlasterState.Ready)
end
end)
secondsBetweenBlasts属性は、ReplicatedStorage ⟩ Instances ⟩ LaserBlastersFolder内のブラスター構成の一部です。secondsBetweenBlastsの遅延が経過すると、プレイヤーは再びブラストでき、全プロセスが繰り返されます。プレイヤーが再びブラストできる時期を理解できるように、体験にはクールダウンバーが含まれています。
この時点で、プレイヤーはスポーン、リスポーンし、狙ってブラストできますが、体験は各ブラストの結果を決定する必要があります。次のチュートリアルのセクションでは、ブラスターがブラストが他のプレイヤーにヒットしたことを検出し、ブラスター設定に応じて適切な量のプレイヤーの健康を減らす機能をプログラムする方法を学びます。