實現噴射器行為 是在第一人稱射擊遊戲中編程一個爆炸機制的過程。雖然玩家可以通過單擊或按一下按鈕來發射,但創建令人滿意和準確的爆炸行為非常重要,因為這能增強玩家對整體遊戲的享受。
使用範例激光標籤體驗作為參考,本教程的這一部分教您如何為兩種類型的噴射器實現噴射器行為的腳本,包括以下指導:
- 偵測玩家何時按下發射按鈕。
- 檢查玩家是否可以使用他們的噴射器,如果他們最近按下了發射按鈕。
- 生成告訴伺服器誰發起了爆炸、從哪裡來以及每束激光的最終目的地的爆炸數據。
- 通知伺服器爆炸數據,以便它可以在爆炸碰撞到另一位玩家時執行適當的操作。
- 在每次爆炸之間重置噴射器,以便給噴射器足夠的時間冷卻,然後才能再次發射。
完成此部分後,您將學習可以讓噴射器檢測其爆炸何時與其他玩家碰撞的腳本,然後根據每種噴射器類型扣除相應的生命值。
偵測玩家輸入
實現噴射器行為的第一步是監聽玩家何時按下發射按鈕。玩家用來按下發射按鈕的輸入類型取決於他們用來訪問體驗的設備。例如,範例激光標籤體驗支持滑鼠和鍵盤控制、遊戲手柄和觸控控制。您可以在 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,您會看到這個經驗有三個噴射器狀態:Ready、Blasting 和 Disabled。要查看每個狀態的效果,您可以進行遊戲測試,選擇 Players 服務下的玩家,然後在 Properties 窗口中觀察 blasterStateClient 屬性。注意當您選擇噴射器時,它顯示 Disabled,大部分時間顯示 Ready,在您按下按鈕後的不到一秒鐘內顯示 Blasting。
這個小暫停防止您能夠以您點擊的速度發射。例如,如果您將函數更改為始終返回 true,您可以在沒有任何延遲的情況下快速發射噴射器,這對於激光標籤遊戲來說是不切實際的。
canLocalPlayerBlast
local function canLocalPlayerBlast(): boolean
return true -- 更新的行,請確保更改回來
end
生成爆炸數據
在驗證玩家的噴射器處於 Ready 狀態後,attemptBlastClient 呼叫 ReplicatedStorage ⟩ attemptBlastClient ⟩ blastClient。blastClient 的第一步是將 blasterStateClient 玩家屬性設置為 Blasting,這避免了之前的快速發射情況。
下一步是生成爆炸數據。如果您查看 ReplicatedStorage ⟩ Blaster ⟩ BlastData,您會看到每次爆炸由三條信息組成:
- 發起爆炸的玩家。
- 一個表示爆炸起點的 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 獲取玩家的噴射器類型。該範例提供兩種類型的噴射器:一種產生幾束具有寬水平擴散的光束,另一種產生單束光束。您可以在 ReplicatedStorage ⟩ Instances ⟩ LaserBlastersFolder 中找到它們的配置。
然後,該函數使用 currentCamera.CFrame 作為爆炸的起點,並將其傳遞給 getDirectionsForBlast。此時,代碼不再關注噴射器,而是關於激光束,您將在教程的 detect hits 部分中學到更多。最後,在創建 rayResults 表之後,generateBlastData 擁有返回爆炸數據至 blastClient 所需的所有信息。
通知伺服器
一旦 blastClient 拿到了完整的爆炸數據,它會觸發兩個事件:
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。
- 檢查玩家是否可以發射。
- 重置噴射器狀態。
- 對任何被標記的玩家減少生命值。
- 將爆炸事件複製給所有其他玩家,以便他們可以看到第三人稱的視覺效果。
有關這些伺服器操作的更多信息,請參見教程的 detect hits 部分。
重置噴射器
在範例激光標籤體驗中,噴射器使用了一個熱量機制。它們需要時間在每次爆炸之間「冷卻」,而不是在設定數量的爆炸後重新裝填。這種冷卻延遲在客戶端 (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 延遲過去後,玩家可以再次發射,整個過程重複。為了幫助玩家了解何時可以再次發射,經驗中包含了一個冷卻條。
此時,玩家可以生成和重生,瞄準和發射,但體驗仍需要確定每次爆炸的結果。在教程的下一部分,您將學習如何編程讓噴射器檢測爆炸何時擊中另一位玩家,然後根據噴射器設置減少相應的玩家生命值。