このチュートリアルでは、プレイヤーツールを作成 でブラスターからレーザーをキャストし、プレイヤーにヒットしたかどうかを検出する方法を学びます。
衝突を見つけるレイキャスト
レイキャスト は、定義された長さで開始位置から指定された方向に向かって、見えないレイを作成します。レイがそのパス上のオブジェクトや地形と衝突すると、位置や衝突したオブジェクトなどの衝突情報が返されます。

マウスの位置を見つける
レーザーを撃つ前に、プレイヤーが狙っている場所を最初に知らなければなりません。これは、カメラからゲームワールドへ直接スクリーン上のプレイヤーの 2D マウスロケーションをレイキャストして見つけることができます。レイは、プレイヤーがマウスで狙っているものと衝突します。
ブラスターツールから ツールコントローラー スクリプトを開き、プレイヤーツールを作成 。まだそのチュートリアルを完了していない場合は、ブラスター モデルをダウンロードしてスターターパックに挿入できます。
スクリプトの最上部で、値 1000 の常数 MAX_MOUSE_DISTANCE を宣言します。
機能 getWorldMousePosition を作成します。
local tool = script.Parentlocal MAX_MOUSE_DISTANCE = 1000local function getWorldMousePosition()endlocal function toolEquipped()tool.Handle.Equip:Play()endlocal function toolActivated()tool.Handle.Activate:Play()end-- イベントを適切な機能に接続するtool.Equipped:Connect(toolEquipped)tool.Activated:Connect(toolActivated)Use the GetMouseLocation function of UserInputService を使用して、画面上のプレイヤーの 2D マウスの位置を取得します。これを mouseLocation という変数に割り当てます。
local UserInputService = game:GetService("UserInputService")local tool = script.Parentlocal MAX_MOUSE_DISTANCE = 1000local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()end
今、2D マウスの位置が知られているので、その X および Y プロパティは、画面から 3D ゲームワールドに移動する 機能のパラメータとして使用できます。
使用する X および Y プロパティの mouseLocation は、ViewportPointToRay() 関数の引数として。これを screenToWorldRay という変数に割り当てます。
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- 2D マウスの位置からレイを作成local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)end
Raycast 関数を使用して、レイがオブジェクトにヒットするかどうかをチェックする時が来ました。これには開始位置と方向ベクトルが必要です:この例では、screenToWorldRay の起源と方向プロパティを使用します。
方向ベクトルの長さは、レイがどこまで移動するかを決定します。レイは MAX_MOUSE_DISTANCE の長さである必要があるので、方向ベクトルを MAX_MOUSE_DISTANCE で乗算する必要があります。
変数 directionVector を宣言し、値を 乗算して割り当てます。
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- 2D マウスロケーションからレイを作成local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)-- レイの単位方向ベクトルを最大距離で掛け算したlocal directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCEワークスペースの 関数を呼び出し、 起源 プロパティを最初の引数として、 を 2番目の引数として、呼び出します。これを raycastResult という変数に割り当てます。
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- 2D マウスロケーションからレイを作成local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)-- レイの単位方向ベクトルを最大距離で掛け算したlocal directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE-- 線の起源から線の方向へのレイキャストlocal raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
衝突情報
レイキャスト操作がレイによってヒットされたオブジェクトを見つけた場合、RaycastResult を返し、レイとオブジェクトの衝突に関する情報を含みます。
RaycastResult プロパティ | 説明 |
---|---|
インス턴ス | レイが交差した BasePart または Terrain セル。 |
地位 | 交差点が発生した場所; 通常、部品または地形の表面上のポイントです。 |
材料 | 衝突ポイントの材料。 |
ノーマル | 交差した顔の普通のベクトル。これを使用して、顔がどの方向を指しているかを判断できます。 |
位置 プロパティは、マウスがホバリングしているオブジェクトの位置になります。マウスが MAX_MOUSE_DISTANCE 距離内のオブジェクトにホバリングしていない場合、raycastResult は nil になります。
if 文を作成して、raycastResult が存在するかどうかをチェックします。
If raycastResult に値がある場合、その 位置 プロパティを返します。
If raycastResult が nil である場合、レイキャストの終わりを見つけます。screenToWorldRay.Origin と directionVector を追加して、マウスの 3D 位置を計算します。
local function getWorldMousePosition()
local mouseLocation = UserInputService:GetMouseLocation()
-- 2D マウスロケーションからレイを作成
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- レイの単位方向ベクトルを最大距離で掛け算した
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- 線の起源から線の方向へのレイキャスト
local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
if raycastResult then
-- 交差点の 3D ポイントを返す
return raycastResult.Position
else
-- オブジェクトがヒットしなかったので、レイの端に位置を計算する
return screenToWorldRay.Origin + directionVector
end
end
ターゲットに向かって発射
3D マウスの位置が知られたので、レーザーを発射するための ターゲットポジション として使用できます。2番目のレイは、 レイキャスト 関数を使用して、プレイヤーの武器とターゲットの位置の間に投射できます。
スクリプトの最上部に定数 MAX_LASER_DISTANCE を宣言し、レーザーブラスターの選択範囲または 500 に割り当てます。
local UserInputService = game:GetService("UserInputService")local tool = script.Parentlocal MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 500Create a function called fireWeapon under the getWorldMousePosition function.を呼び出す関数を作成します。
呼び出し getWorldMousePosition そして結果を mousePosition という変数に割り当てます。これがレイキャストのターゲット位置になります。
-- オブジェクトがヒットしなかったので、レイの端に位置を計算するreturn screenToWorldRay.Origin + directionVectorendendlocal function fireWeapon()local mouseLocation = getWorldMousePosition()endlocal function toolEquipped()tool.Handle.Equip:Play()end
今回、レイキャスト関数の方向ベクトルは、プレイヤーのツールポジションからターゲットの場所までの方向を表示します。
変数 targetDirection を宣言し、ツールの位置から mouseLocation を差し引いて方向ベクトルを計算します。
その ユニット プロパティを使用してベクトルを正常化します。これにより、後で長さで乗算するのが簡単になり、ベクトルの大きさが 1 になります。
local function fireWeapon()local mouseLocation = getWorldMousePosition()-- 正常化された方向ベクトルを計算し、レーザー距離で乗算するlocal targetDirection = (mouseLocation - tool.Handle.Position).Unitend変数名を directionVector として宣言し、targetDirection を掛けて MAX_LASER_DISTANCE に割り当てます。
local targetDirection = (mouseLocation - tool.Handle.Position).Unit-- 武器を発射する方向、最大距離で掛け算するlocal directionVector = targetDirection * MAX_LASER_DISTANCEend
A RaycastParams オブジェクトは、レイキャスト関数の追加パラメータを保存するために使用できます。レーザーブラスターで使用され、レイキャストが武器を発射しているプレイヤーと偶然に衝突しないようにします。レイキャストパラメータオブジェクトの FilterDescendantsInstances プロパティに含まれるパーツは、 無視されます レイキャストで。
fireWeapon 関数を続行し、 weaponRaycastParams という変数を宣言します。新しい RaycastParams オブジェクトを割り当てます。
プレイヤーのローカル 文字 を含むテーブルを作成し、weaponRaycastParams.FilterDescendantsInstances プロパティに割り当てます。
プレイヤーのツールハンドル位置から、directionVector 方向にレイキャスト。今回は引数として weaponRaycastParams を追加することを忘れないでください。これを weaponRaycastResult という変数に割り当てます。
local UserInputService = game:GetService("UserInputService")
local Players = game:GetService("Players")
local tool = script.Parent
local MAX_MOUSE_DISTANCE = 1000
local MAX_LASER_DISTANCE = 500
local function getWorldMousePosition()
local function fireWeapon()
local mouseLocation = getWorldMousePosition()
-- 正常化された方向ベクトルを計算し、レーザー距離で乗算する
local targetDirection = (mouseLocation - tool.Handle.Position).Unit
-- 武器を発射する方向を最大距離で掛け算した値
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- プレイヤーのキャラクターを無視して、自分を傷つけるのを防ぐ
local weaponRaycastParams = RaycastParams.new()
weaponRaycastParams.FilterDescendantsInstances = {Players.LocalPlayer.Character}
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
end
最後に、レイキャスト操作が値を返したかどうかをチェックする必要があります。値が返された場合、レイによってオブジェクトがヒットし、武器とヒット場所の間にレーザーが作成できます。何も返されなかった場合、レーザーを作成するために最終位置を計算する必要があります。
空の変数名 hitPosition を宣言します。
Use an if 文を使用して、weaponRaycastResult が値を持っているかどうかを確認します。オブジェクトがヒットした場合は、weaponRaycastResult.Position を hitPosition に割り当てます。
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)-- 開始から終了までの間にオブジェクトがヒットしたかどうかをチェックするlocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.PositionendIf weaponRaycastResult に値がない場合、ツールハンドルの 位置 と directionVector を合計して、レイキャストの終了位置を計算します。これを hitPosition に割り当てます。
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)-- 開始から終了までの間にオブジェクトがヒットしたかどうかをチェックするlocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.Positionelse-- 最大レーザー距離に基づいて最終位置を計算するhitPosition = tool.Handle.Position + directionVectorendendナビゲート to the toolActivated 機能と fireWeapon 機能を呼び出して、レーザーがツールが有効になるたびに発射するようにします。
local function toolActivated()tool.Handle.Activate:Play()fireWeapon()end
オブジェクトヒットをチェック
レーザーによってヒットされたオブジェクトがプレイヤーのキャラクターの一部か、ただの景観の一部かどうかを見つけるには、すべてのキャラクターに Humanoid を検索する必要があります。
まず、 キャラクターモデル を見つける必要があります。キャラクターの一部がヒットした場合、ヒットしたオブジェクトの親がキャラクターであるとは推定できません。レーザーは、体の部分、アクセサリ、またはツールを打つことができましたが、すべてはキャラクタの階層の異なる部分に位置しています。
FindFirstAncestorOfClass を使用して、レーザーによってヒットされたオブジェクトのキャラクターモデルの祖先を見つけることができます(存在する場合)。モデルを見つけて、それにヒューマノイドが含まれている場合、ほとんどの場合、それがキャラクターであると想定できます。
ハイライトされたコードを以下の 文に追加して、キャラクターがヒットしたかどうかをチェックします。
-- 開始から終了までの間にオブジェクトがヒットしたかどうかをチェックするlocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.Position-- インスタンスヒットは、キャラクターモデルの子供になる-- ヒューマノイドがモデルに見つかった場合、それはプレイヤーのキャラクターである可能性が高いlocal characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid thenprint("Player hit")endendelse-- 最大レーザー距離に基づいて最終位置を計算するhitPosition = tool.Handle.Position + directionVectorend
今、レーザーブラスターはレイキャスト操作が別のプレイヤーにヒットするたびに出力ウィンドウに Player hit を印刷する必要があります。
複数のプレイヤーでテスト
武器レイキャストが他のプレイヤーを見つけているかをテストするには、2人のプレイヤーが必要であるため、ローカルサーバーを開始する必要があります。
Studio で テスト タブを選択します。
プレイヤーのドロップダウンが「2人」に設定されていることを確認し、スタートボタンをクリックして ローカルサーバーを 2 クライアントで開始 します。3つのウィンドウが表示されます。最初のウィンドウはローカルサーバー、他のウィンドウは Player1 と Player2 のクライアントになります。
1つのクライアントで、武器をクリックして他のプレイヤーをテスト撮影します。「プレイヤーヒット」は、プレイヤーが撃たれるたびに出力に表示されるべきです。
詳しくは、 テスト タブ ここ で見つけることができます。
レーザーの位置を見つける
ブラスターはターゲットに赤い光ビームを発射すべきです。これの機能は ModuleScript 内にあるので、後で他のスクリプトで再利用できます。まず、スクリプトはレーザービームがレンダリングされる位置を見つける必要があります。
Create a モジュールスクリプト named LaserRenderer , parented to StarterPlayerScripts under StarterPlayer.
スクリプトを開き、モジュールテーブルの名前をスクリプト名に変更します LaserRenderer 。
変数 SHOT_DURATION を値 0.15 で宣言します。これはレーザーが見える時間(秒)の量になります。
LaserRenderer の機能を createLaser という名前で作成し、2つのパラメータ toolHandle と endPosition を持って作成します。
local LaserRenderer = {}local SHOT_DURATION = 0.15 -- レーザーが見える時間-- 開始位置から終了位置へレーザービームを作成するfunction LaserRenderer.createLaser(toolHandle, endPosition)endreturn LaserRenderer変数 startPosition を宣言し、 位置 プロパティを toolHandle の値として設定します。これがプレイヤーのレーザーブラスターの位置になります。
変数 laserDistance を宣言し、endPosition から startPosition を差し引いて、2つのベクトルの違いを見つけます。この 大きさ プロパティを使用して、レーザービームの長さを取得します。
function LaserRenderer.createLaser(toolHandle, endPosition)local startPosition = toolHandle.Positionlocal laserDistance = (startPosition - endPosition).MagnitudeendlaserCFrame 変数を宣言して、レーザービームの位置と方向を保存する位置はビームの始点と終点の中間になければなりません。Use CFrame.lookAt を使用して、新しい CFrame を startPosition に配置し、endPosition に向かって作成します。これをマイナスの laserDistance の Z軸値の新しい CFrameで掛け算して、中点を得る
function LaserRenderer.createLaser(toolHandle, endPosition)local startPosition = toolHandle.Positionlocal laserDistance = (startPosition - endPosition).Magnitudelocal laserCFrame = CFrame.lookAt(startPosition, endPosition) * CFrame.new(0, 0, -laserDistance / 2)end
レーザー部品を作成する
レーザービームを作成する場所を知ったので、ビーム自体を追加する必要があります。これは、ネオンパーツで簡単に行うことができます。
変数を宣言 laserPart そして新しい Part インスタンスを割り当てる。
次のプロパティを laserPart に設定します:
- サイズ : Vector3.new(0.2, 0.2, laserDistance)
- CFrame : laserCFrame
- 固定済み : 真
- 可能な衝突 : false
- 色 : Color3.fromRGB(225, 0, 0) (強い赤色)
- マテリアル : Enum.Material.Neon
親 laserPart を ワークスペース に。
パーツを Debris サービスに追加して、SHOT_DURATION 変数の秒数後に削除されるようにします。
function LaserRenderer.createLaser(toolHandle, endPosition)local startPosition = toolHandle.Positionlocal laserDistance = (startPosition - endPosition).Magnitudelocal laserCFrame = CFrame.lookAt(startPosition, endPosition) * CFrame.new(0, 0, -laserDistance / 2)local laserPart = Instance.new("Part")laserPart.Size = Vector3.new(0.2, 0.2, laserDistance)laserPart.CFrame = laserCFramelaserPart.Anchored = truelaserPart.CanCollide = falselaserPart.Color = Color3.fromRGB(225, 0, 0)laserPart.Material = Enum.Material.NeonlaserPart.Parent = workspace-- 削除してクリーンアップするデブリスサービスにレーザービームを追加するDebris:AddItem(laserPart, SHOT_DURATION)end
今、レーザービームをレンダリングする機能が完了したので、 ツールコントローラー に呼び出されることができます。
ツールコントローラー スクリプトの最上部で、 LaserRenderer という名前の変数を宣言し、PlayerScripts 内の LaserRenderer モジュールスクリプトを要求する
local UserInputService = game:GetService("UserInputService")local Players = game:GetService("Players")local LaserRenderer = require(Players.LocalPlayer.PlayerScripts.LaserRenderer)local tool = script.ParentfireWeapon 関数の底で、ツールハンドルと createLaser を引数として使用して LaserRenderer hitPosition 関数を呼び出します。
-- 最大レーザー距離に基づいて最終位置を計算するhitPosition = tool.Handle.Position + directionVectorendLaserRenderer.createLaser(tool.Handle, hitPosition)endプレイボタンをクリックして武器をテストします。ツールが有効になったときに、武器とマウスの間にレーザービームが表示されるべきです。
武器の発射評価するを制御
武器は、各ショットの間に遅延が必要で、プレイヤーが短時間に多くのダメージを与えるのを止めます。これは、プレイヤーが最後に発射した時から十分な時間が経過したかどうかをチェックすることで制御できます。
ツールコントローラ のトップに変数を宣言し、呼び出された FIRE_RATE 。これは、各ショットの間の最小時間になります。好きな値を与えてください; この例では 0.3 秒を使用しています。
別の変数を下に宣言し、値は 0 で timeOfPreviousShot と呼ばれます。これはプレイヤーが最後に発砲した時を保存し、毎回のショットで更新されます。
local MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 300local FIRE_RATE = 0.3local timeOfPreviousShot = 0パラメータなしで canShootWeapon という機能を作成します。この機能は、前のショットからどれくらい時間が経過したかを見て、真または偽を返します。
local FIRE_RATE = 0.3local timeOfPreviousShot = 0-- 前のショットが発射されてから十分な時間が経過したかどうかをチェックlocal function canShootWeapon()endlocal function getWorldMousePosition()機能内で、変数 currentTime を宣言し、tick() 関数を呼び出す結果を割り当てます。これは、1970年1月1日から、秒単位でどれくらい時間が経過したかを返します (時間を計算するために広く使用されている任意の日付)。
から を差し引き、結果が より小さい場合は、 より小さいと返し、そうでない場合は 真 を返します。
-- 前のショットが発射されてから十分な時間が経過したかどうかをチェックlocal function canShootWeapon()local currentTime = tick()if currentTime - timeOfPreviousShot < FIRE_RATE thenreturn falseendreturn trueend関数の終わりで、武器が を使用して発射されるたびに更新します。
hitPosition = tool.Handle.Position + directionVectorendtimeOfPreviousShot = tick()LaserRenderer.createLaser(tool.Handle, hitPosition)endtoolActivated 関数内で、 如果 文を作成し、武器が発射できるかどうかを確認するために canShootWeapon を呼び出します。
local function toolActivated()if canShootWeapon() thentool.Handle.Activate:Play()fireWeapon()endend
ブラスターをテストすると、どれほど早くクリックしても、各ショットの間に常に短い 0.3秒の遅延が発生することがわかります。
プレイヤーにダメージを与える
クライアントは他のクライアントに直接ダメージを与えることはできません;サーバーは、プレイヤーが攻撃されたときにダメージを発行する責任がある必要があります。
クライアントは、RemoteEvent を使用して、サーバーにキャラクターがヒットしたことを告知できます。これらは ReplicatedStorage に保存され、クライアントとサーバーの両方に表示される必要があります。
ReplicatedStorage に名前を イベント という ReplicatedStorage にフォルダを作成します。
リモートイベントをイベントフォルダに挿入し、名前を ダメージキャラクター とします。
In ツールコントローラ で、スクリプトの開始時に ReplicatedStorage とイベントフォルダの変数を作成します。
local UserInputService = game:GetService("UserInputService")local Players = game:GetService("Players")local ReplicatedStorage = game:GetService("ReplicatedStorage")local LaserRenderer = require(Players.LocalPlayer.PlayerScripts.LaserRenderer)local tool = script.Parentlocal eventsFolder = ReplicatedStorage.Eventslocal MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 500"Player hit" 印刷文を fireWeapon に Luau の行で置き換えて、 ダメージキャラクター リモートイベントを characterModel 変数を引数として発射します。
local characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid theneventsFolder.DamageCharacter:FireServer(characterModel)endendelse-- 最大レーザー距離に基づいて最終位置を計算するhitPosition = tool.Handle.Position + directionVectorend
サーバーは、イベントが発動したときにヒットしたプレイヤーにダメージを与える必要があります。
ServerScriptService に スクリプト を挿入し、名前を ServerLaserManager とします。
変数 LASER_DAMAGE という名前を宣言し、 10 または好みの値に設定します。
2つのパラメータ playerFired と characterToDamage を持つ機能名 damageCharacter を作成し、2つのパラメータを使用して呼び出します。
機能内で、キャラクターのヒューマノイドを見つけて、健康から LASER_DAMAGE を差し引きます。
damageCharacter 機能をイベントフォルダの ダメージキャラクター リモートイベントに接続します。
local ReplicatedStorage = game:GetService("ReplicatedStorage")local eventsFolder = ReplicatedStorage.Eventslocal LASER_DAMAGE = 10function damageCharacter(playerFired, characterToDamage)local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")if humanoid then-- キャラクターから体力を削除humanoid.Health -= LASER_DAMAGEendend-- イベントを適切な機能に接続するeventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)ローカルサーバーを開始して、ブラスターを 2 人のプレイヤーでテストします。他のプレイヤーを撃つと、そのプレイヤーの体力が LASER_DAMAGE に割り当てられた数で減少します。
他のプレイヤーのレーザービームをレンダリング
現在、レーザービームは武器を発射するクライアントによって作成されるため、彼らだけがレーザービームを見ることができます。
サーバー上でレーザービームが作成された場合、誰もがそれを見ることができます。しかし、クライアントが武器を発射してサーバーがショットに関する情報を受信する間に、小さな 遅延 が発生する可能性があります。これは、クライアントが武器を発射すると、武器を有効にしてからレーザービームを見るまでの遅延が発生することを意味します;その結果、武器は遅れて感じられます。
この問題を解決するために、各クライアントは独自のレーザービームを作成します。これは、武器を撃つクライアントがすぐにレーザービームを見ることを意味します。他のクライアントは、別のプレイヤーがシュートしてビームが表示される間に少しの遅延を経験します。これは最良のシナリオです:別のクライアントにレーザーをコミュニケートする方法は、より速くはありません。
シューターのクライアント
まず、クライアントはサーバーにレーザーを発射し、終了位置を提供したことを伝える必要があります。
ReplicatedStorage のイベントフォルダに リモートイベント を挿入し、名前を LaserFired とします。
fireWeapon 機能を ツールコントローラー スクリプトで見つけます。機能の終わりに、 LaserFired リモートイベントを hitPosition を引数として使用して発射します。
hitPosition = tool.Handle.Position + directionVectorendtimeOfPreviousShot = tick()eventsFolder.LaserFired:FireServer(hitPosition)LaserRenderer.createLaser(tool.Handle, hitPosition)end
サーバー
サーバーは今、クライアントが発射したイベントを受信し、すべてのクライアントにレーザービームの開始および終了位置を伝えて、彼らもレンダリングできるようにする必要があります。
In the ServerLaserManager スクリプト, create a function named playerFiredLaser above damageCharacter with two parameters called playerFired と endPosition .
機能を LaserFired リモートイベントに接続します。
-- レーザーが発射されたことをすべてのクライアントに通知して、レーザーを表示できるようにするlocal function playerFiredLaser(playerFired, endPosition)end-- イベントを適切な機能に接続するeventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)eventsFolder.LaserFired.OnServerEvent:Connect(playerFiredLaser)
サーバーはレーザーの開始位置が必要です。これはクライアントから送信できますが、可能な限りクライアントを信頼しないことが最善です。キャラクターの武器ハンドルの位置は、スタートポジションになるので、サーバーはそこから見つけることができます。
パラメータ playerFiredLaser を持つ上記の player 機能を作成し、 **** 機能の上に配置します。
次のコードを使用して、プレイヤーのキャラクターを武器で検索し、ハンドルオブジェクトを返します。
local LASER_DAMAGE = 10-- プレイヤーが持っているツールのハンドルを見つけるlocal function getPlayerToolHandle(player)local weapon = player.Character:FindFirstChildOfClass("Tool")if weapon thenreturn weapon:FindFirstChild("Handle")endend-- レーザーが発射されたことをすべてのクライアントに通知して、レーザーを表示できるようにするlocal function playerFiredLaser(playerFired, endPosition)
サーバーは今、 FireAllClients を LaserFired リモートイベントに呼び出し、レーザーをクライアントにレンダリングするために必要な情報を送信できます。これには、レーザーを発射した プレイヤー 、ブラスターの ハンドル 、およびレーザーの終わりの位置が含まれます。
playerFiredLaser 関数では、getPlayerToolHandle 関数を引数として呼び出し、playerFired を値として変数名 toolHandle に割り当てます。
ツールハンドル が存在する場合、playerFired、toolHandle、および endPosition を引数として使用するすべてのクライアントに対して、LaserFired イベントを発射します。
-- レーザーが発射されたことをすべてのクライアントに通知して、レーザーを表示できるようにするlocal function playerFiredLaser(playerFired, endPosition)local toolHandle = getPlayerToolHandle(playerFired)if toolHandle theneventsFolder.LaserFired:FireAllClients(playerFired, toolHandle, endPosition)endend
クライアントでレンダリング
今、 FireAllClients が呼び出されたので、各クライアントはサーバーからレーザービームをレンダリングするイベントを受け取ります。各クライアントは、 LaserRenderer モジュールを以前から再使用して、サーバーによって送信されたツールのハンドルポジションと終了ポジション値を使用してレーザービームをレンダリングできます。レーザービームを最初に発射したプレイヤーは、このイベントを無視する必要があります。そうしないと、2つのレーザーを見ることになります。
スタータープレイヤースクリプトに ローカルスクリプト を作成し、名前は ClientLaserManager 。
スクリプト内で、 LaserRenderer モジュールが必要です。
パラメータ createPlayerLaser 、 playerWhoShot 、 toolHandle 、 endPosition で名前の付いた機能を作成します。
機能を LaserFired リモートイベントにイベントフォルダに接続します。
機能では、 if 文を使用して、 がローカルプレイヤーと等しくないかどうかをチェックします。
if 文内で、createLaser と toolHandle と endPosition を引数として使用して、レーザーレンダラーモジュールから 関数を呼び出します。
local Players = game:GetService("Players")local ReplicatedStorage = game:GetService("ReplicatedStorage")local LaserRenderer = require(script.Parent:WaitForChild("LaserRenderer"))local eventsFolder = ReplicatedStorage.Events-- 他のプレイヤーのレーザーを表示local function createPlayerLaser(playerWhoShot, toolHandle, endPosition)if playerWhoShot ~= Players.LocalPlayer thenLaserRenderer.createLaser(toolHandle, endPosition)endendeventsFolder.LaserFired.OnClientEvent:Connect(createPlayerLaser)ローカルサーバーを開始して、ブラスターを 2 人のプレイヤーでテストします。各クライアントをモニターの異なる側に配置して、両方のウィンドウを同時に見ることができます。1つのクライアントに撃つとき、他のクライアントにレーザーが表示されるはずです。
サウンド効果
現在、発射サウンド効果は、弾丸を発射しているクライアント上でのみ再生します。コードを移動してサウンドを再生する必要があり、他のプレイヤーも聞くことができます。
ツールコントローラー スクリプトで、 ツールが有効化 機能に移動し、アクティベートサウンドを再生する行を削除します。
local function toolActivated()if canShootWeapon() thenfireWeapon()endendLaserRenderer の 底 機能で、shootingSound という名前の変数を宣言し、 メソッドの Activate サウンドをチェックするために メソッドを使用します。
Use an if 文を使用して、shootingSound が存在するかどうかをチェックします; 存在する場合は、その プレイ 機能を呼び出します。
laserPart.Parent = workspace-- 削除してクリーンアップするデブリスサービスにレーザービームを追加するDebris:AddItem(laserPart, SHOT_DURATION)-- 武器の射撃音を再生するlocal shootingSound = toolHandle:FindFirstChild("Activate")if shootingSound thenshootingSound:Play()endend
検証を使用してリモートを安全にする
サーバーが受信したリクエストからデータをチェックしていない場合、ハッカーはリモート機能とイベントを悪用し、サーバーに偽の値を送信することができます。これを防ぐためには、 サーバー側の検証 を使用することが重要です。
現在の形で、 ダメージキャラクター リモートイベントは、攻撃に非常に弱いです。ハッカーは、このイベントを使用して、撃つことなくゲームで望むプレイヤーにダメージを与えることができます。
検証は、サーバーに送信される値が現実的であるかどうかをチェックするプロセスです。この場合、サーバーは次のことが必要になります:
- プレイヤーとレーザーによって命中した位置の距離が特定の境界内にあるかどうかをチェックします。
- レーザーを発射した武器とヒット位置の間でレイキャストして、ショットが可能であり、壁を通り抜けなかったことを確認します。
クライバー
クライアントは、レイキャストによってヒットされた位置をサーバーに送信して、距離が現実的かどうかをチェックする必要があります。
In ツールコントローラー , navigate to the line where the DamageCharacter リモートイベントが fireWeapon 関数で発動されるラインに移動します。
hitPosition を引数として追加。
if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid theneventsFolder.DamageCharacter:FireServer(characterModel, hitPosition)endend
サーバー
クライアントは現在、DamageCharacter リモートイベントを通じて追加パラメータを送信しているので、 ServerLaserManager を調整して受け入れる必要があります。
サーバーレーザーマネージャー スクリプトで、hitPosition パラメータを damageCharacter 関数に追加します。
function damageCharacter(playerFired, characterToDamage, hitPosition)local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")if humanoid then-- キャラクターから体力を削除humanoid.Health -= LASER_DAMAGEendendgetPlayerToolHandle 関数の下で、3つのパラメータ isHitValid を持つ機能を作成します: playerFired、characterToDamage、hitPosition。
endlocal function isHitValid(playerFired, characterToDamage, hitPosition)end
最初のチェックは、ヒット位置とキャラクターヒットの間の距離です。
スクリプトの最上部に変数 MAX_HIT_PROXIMITY を宣言し、値を 10 に割り当てます。ヒットとキャラクターの間の最大許可距離がこれになります。キャラクターは、クライアントがイベントを発射して以来、わずかに移動している可能性があるため、許容が必要です。
local ReplicatedStorage = game:GetService("ReplicatedStorage")local eventsFolder = ReplicatedStorage.Eventslocal LASER_DAMAGE = 10local MAX_HIT_PROXIMITY = 10isHitValid 関数では、キャラクターとヒットポジションの間の距離を計算します。距離が MAX_HIT_PROXIMITY より大きい場合は、 false を返します。
local function isHitValid(playerFired, characterToDamage, hitPosition)-- キャラクタヒットとヒットポジションの距離を検証するlocal characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitudeif characterHitProximity > MAX_HIT_PROXIMITY thenreturn falseendend
2番目のチェックでは、発射された武器とヒットポジションの間のレイキャストが関与します。レイキャストがキャラクタではないオブジェクトを返した場合、ショットがブロックされていたため、ショットが無効であったと推測できます。
このチェックを実行するには、以下のコードをコピーしてください。機能の終わりに true を返します:終了わりに到達すると、すべてのチェックが通過します。
local function isHitValid(playerFired, characterToDamage, hitPosition)-- キャラクタヒットとヒットポジションの距離を検証するlocal characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitudeif characterHitProximity > 10 thenreturn falseend-- 壁を通して撮影しているかどうかをチェックlocal toolHandle = getPlayerToolHandle(playerFired)if toolHandle thenlocal rayLength = (hitPosition - toolHandle.Position).Magnitudelocal rayDirection = (hitPosition - toolHandle.Position).Unitlocal raycastParams = RaycastParams.new()raycastParams.FilterDescendantsInstances = {playerFired.Character}local rayResult = workspace:Raycast(toolHandle.Position, rayDirection * rayLength, raycastParams)-- インスタンスがヒットしたものがキャラクターではなかった場合は、ショットを無視するif rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) thenreturn falseendendreturn trueend宣言変数を damageCharacter 関数に呼び出された validShot で宣言します。それには、3つの引数で呼び出された isHitValid 関数の結果を割り当てます:playerFired、characterToDamage、そして hitPosition。
以下の if 文では、 と オペレータを追加して、validShot が 真実 かどうかをチェックします。
function damageCharacter(playerFired, characterToDamage, hitPosition)local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")local validShot = isHitValid(playerFired, characterToDamage, hitPosition)if humanoid and validShot then-- キャラクターから体力を削除humanoid.Health -= LASER_DAMAGEendend
今、ダメージキャラクターリモートイベントはより安全であり、ほとんどのプレイヤーが悪用するのを防ぐでしょう。悪意のあるプレイヤーの一部は、検証を回避する方法を見つけることが多いことに注意してください;リモートイベントを安全に保つことは連続的な努力です。
レーザーブラスターが完了し、レイキャストを使用した基本的なヒット検出システムが完了しました。ユーザーの入力を検出するチュートリアルを試して、レーザーブラスターにリロードアクションを追加するか、楽しいゲームマップを作成して他のプレイヤーとレーザーブラスターを試してみましょう!
最終コード
ツールコントローラ
local UserInputService = game:GetService("UserInputService")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local LaserRenderer = require(Players.LocalPlayer.PlayerScripts.LaserRenderer)
local tool = script.Parent
local eventsFolder = ReplicatedStorage.Events
local MAX_MOUSE_DISTANCE = 1000
local MAX_LASER_DISTANCE = 500
local FIRE_RATE = 0.3
local timeOfPreviousShot = 0
-- 前のショットが発射されてから十分な時間が経過したかどうかをチェック
local function canShootWeapon()
local currentTime = tick()
if currentTime - timeOfPreviousShot < FIRE_RATE then
return false
end
return true
end
local function getWorldMousePosition()
local mouseLocation = UserInputService:GetMouseLocation()
-- 2D マウスの位置からレイを作成
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- レイの単位方向ベクトルを最大距離で掛け算した
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- ロイの起源からその方向へのレイキャスト
local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
if raycastResult then
-- 交差点の 3D ポイントを返す
return raycastResult.Position
else
-- オブジェクトがヒットしなかったので、レイの端に位置を計算する
return screenToWorldRay.Origin + directionVector
end
end
local function fireWeapon()
local mouseLocation = getWorldMousePosition()
-- 正常化された方向ベクトルを計算し、レーザー距離で乗算する
local targetDirection = (mouseLocation - tool.Handle.Position).Unit
-- 武器を発射する方向、最大距離で掛け算する
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- プレイヤーのキャラクターを無視して、自分を傷つけるのを防ぐ
local weaponRaycastParams = RaycastParams.new()
weaponRaycastParams.FilterDescendantsInstances = {Players.LocalPlayer.Character}
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
-- 開始から終了までの間にオブジェクトがヒットしたかどうかをチェックする
local hitPosition
if weaponRaycastResult then
hitPosition = weaponRaycastResult.Position
-- インスタンスヒットは、キャラクターモデルの子供になる
-- ヒューマノイドがモデルに見つかった場合、それはプレイヤーのキャラクターである可能性が高い
local characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")
if characterModel then
local humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")
if humanoid then
eventsFolder.DamageCharacter:FireServer(characterModel, hitPosition)
end
end
else
-- 最大レーザー距離に基づいて最終位置を計算する
hitPosition = tool.Handle.Position + directionVector
end
timeOfPreviousShot = tick()
eventsFolder.LaserFired:FireServer(hitPosition)
LaserRenderer.createLaser(tool.Handle, hitPosition)
end
local function toolEquipped()
tool.Handle.Equip:Play()
end
local function toolActivated()
if canShootWeapon() then
fireWeapon()
end
end
tool.Equipped:Connect(toolEquipped)
tool.Activated:Connect(toolActivated)
レーザーレンダラー
local LaserRenderer = {}
local Debris = game:GetService("Debris")
local SHOT_DURATION = 0.15 -- レーザーが見える時間
-- 開始位置から終了位置へレーザービームを作成する
function LaserRenderer.createLaser(toolHandle, endPosition)
local startPosition = toolHandle.Position
local laserDistance = (startPosition - endPosition).Magnitude
local laserCFrame = CFrame.lookAt(startPosition, endPosition) * CFrame.new(0, 0, -laserDistance / 2)
local laserPart = Instance.new("Part")
laserPart.Size = Vector3.new(0.2, 0.2, laserDistance)
laserPart.CFrame = laserCFrame
laserPart.Anchored = true
laserPart.CanCollide = false
laserPart.Color = Color3.fromRGB(255, 0, 0)
laserPart.Material = Enum.Material.Neon
laserPart.Parent = workspace
-- 削除してクリーンアップするデブリスサービスにレーザービームを追加する
Debris:AddItem(laserPart, SHOT_DURATION)
-- 武器の射撃音を再生する
local shootingSound = toolHandle:FindFirstChild("Activate")
if shootingSound then
shootingSound:Play()
end
end
return LaserRenderer
サーバーレーザーマネージャ
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local eventsFolder = ReplicatedStorage.Events
local LASER_DAMAGE = 10
local MAX_HIT_PROXIMITY = 10
-- プレイヤーが持っているツールのハンドルを見つける
local function getPlayerToolHandle(player)
local weapon = player.Character:FindFirstChildOfClass("Tool")
if weapon then
return weapon:FindFirstChild("Handle")
end
end
local function isHitValid(playerFired, characterToDamage, hitPosition)
-- キャラクタヒットとヒットポジションの距離を検証する
local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
if characterHitProximity > MAX_HIT_PROXIMITY then
return false
end
-- 壁を通して撮影しているかどうかをチェック
local toolHandle = getPlayerToolHandle(playerFired)
if toolHandle then
local rayLength = (hitPosition - toolHandle.Position).Magnitude
local rayDirection = (hitPosition - toolHandle.Position).Unit
local raycastParams = RaycastParams.new()
raycastParams.FilterDescendantsInstances = {playerFired.Character}
local rayResult = workspace:Raycast(toolHandle.Position, rayDirection * rayLength, raycastParams)
-- インスタンスがヒットしたものがキャラクターではなかった場合は、ショットを無視する
if rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) then
return false
end
end
return true
end
-- レーザーが発射されたことをすべてのクライアントに通知して、レーザーを表示できるようにする
local function playerFiredLaser(playerFired, endPosition)
local toolHandle = getPlayerToolHandle(playerFired)
if toolHandle then
eventsFolder.LaserFired:FireAllClients(playerFired, toolHandle, endPosition)
end
end
function damageCharacter(playerFired, characterToDamage, hitPosition)
local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")
local validShot = isHitValid(playerFired, characterToDamage, hitPosition)
if humanoid and validShot then
-- キャラクターから体力を削除
humanoid.Health -= LASER_DAMAGE
end
end
-- イベントを適切な機能に接続する
eventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)
eventsFolder.LaserFired.OnServerEvent:Connect(playerFiredLaser)
客先レーザーマネージャー
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local LaserRenderer = require(Players.LocalPlayer.PlayerScripts:WaitForChild("LaserRenderer"))
local eventsFolder = ReplicatedStorage.Events
-- 他のプレイヤーのレーザーを表示
local function createPlayerLaser(playerWhoShot, toolHandle, endPosition)
if playerWhoShot ~= Players.LocalPlayer then
LaserRenderer.createLaser(toolHandle, endPosition)
end
end
eventsFolder.LaserFired.OnClientEvent:Connect(createPlayerLaser)