Spawn là quá trình tạo ra một đối tượng hoặc nhân vật trong một trò chơi, và respawn là quá trình đưa một đối tượng hoặc nhân vật trở lại vào trò chơi sau khi họ gặp điều kiện loại bỏ, chẳng hạn như sức khỏe của nhân vật đạt đến zero hoặc rơi ra khỏi bản đồ. Cả hai quá trình này đều quan trọng vì chúng đảm bảo rằng người chơi có thể tham gia trò chơi của bạn và có thể tiếp tục chơi để cải thiện kỹ năng của họ.
Sử dụng trò chơi laser tag mẫu làm tham chiếu, phần này của hướng dẫn sẽ dạy bạn cách sử dụng và tùy chỉnh các tính năng tích hợp sẵn của Roblox để xử lý việc spawn và respawn, bao gồm hướng dẫn lập trình về:
- Cấu hình các vị trí spawn để người chơi chỉ có thể spawn vào khu vực spawn của đội mình.
- Thêm người chơi mới và nhân vật của họ vào vòng chơi khi họ tham gia trò chơi.
- Tùy chỉnh các trường lực để ngăn chặn sát thương khi người chơi spawn và respawn.
- Xử lý trạng thái của client để gameplay hoạt động chính xác vào thời điểm thích hợp.
- Respawn các nhân vật sau khi họ bị loại khỏi vòng chơi.
- Thực hiện các hành động nhỏ, linh tinh mà rất quan trọng để thiết lập gameplay và các tham số của nhân vật.
Phần này bao gồm nhiều nội dung lập trình, nhưng thay vì viết mọi thứ từ đầu khi tạo một trò chơi, nó khuyến khích bạn tận dụng các thành phần hiện có, lặp lại nhanh chóng và tìm ra hệ thống nào cần một triển khai tùy chỉnh để phù hợp với tầm nhìn của bạn. Sau khi bạn hoàn thành phần này, bạn sẽ học cách triển khai gameplay theo vòng mà theo dõi điểm số, giám sát trạng thái người chơi và hiển thị kết quả vòng chơi.
Cấu hình các vị trí spawn
Nếu bạn thử nghiệm trò chơi ngay bây giờ, tất cả người chơi sẽ spawn ngẫu nhiên tại đối tượng SpawnLocation trong khu vực spawn của đội xanh, hoặc đối tượng SpawnLocation trong khu vực spawn của đội hồng. Điều này tạo ra một vấn đề trong gameplay khi người chơi có thể tag nhau trong mỗi khu vực spawn ngay khi trường lực của đối thủ biến mất.
Để chống lại vấn đề này, trò chơi laser tag mẫu cấu hình cả hai vị trí spawn với thuộc tính Neutral được đặt thành false để hạn chế người chơi của đội đối phương spawn vào khu vực spawn sai, và thuộc tính TeamColor được đặt thành giá trị tương ứng của Team.Color từ Gán màu đội trong phần trước của hướng dẫn:
- TeamASpawn – Vị trí spawn trong khu vực spawn của đội xanh với thuộc tính TeamColor được đặt thành Mint.
- TeamBSpawn – Vị trí spawn trong khu vực spawn của đội hồng với thuộc tính TeamColor được đặt thành Carnation Pink.


Khi một người chơi tham gia trò chơi, ServerScriptService ⟩ Gameplay ⟩ Rounds ⟩ spawnPlayersInMap kiểm tra xem có bao nhiêu người chơi đã có trên mỗi đội, sau đó trả về đội có ít người chơi nhất.
local function getSmallestTeam(): Team
local teams = Teams:GetTeams()
-- Sắp xếp các đội theo thứ tự tăng dần từ nhỏ nhất đến lớn nhất
table.sort(teams, function(teamA: Team, teamB: Team)
return #teamA:GetPlayers() < #teamB:GetPlayers()
end)
-- Trả về đội nhỏ nhất
return teams[1]
endKhi nó biết đội có ít người chơi nhất, nó sắp xếp người chơi vào đội đó, đặt thuộc tính Player.Neutral của họ thành false để người chơi chỉ có thể spawn và respawn vào vị trí spawn của đội mình, sau đó đặt trạng thái PlayerState của họ thành SelectingBlaster, mà bạn sẽ tìm hiểu thêm sau trong hướng dẫn.
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
endNếu bạn kiểm tra Workspace ⟩ World ⟩ Map ⟩ Spawns, bạn có thể thấy rằng có một vị trí spawn khác trong bản đồ: NeutralSpawn. Vị trí spawn này khác với các vị trí khác vì nó không có thuộc tính TeamColor được đặt thành một trong hai đội trong trò chơi; thay vào đó, vị trí spawn này có thuộc tính Neutral thay đổi tùy thuộc vào việc một vòng chơi có đang hoạt động hay không.
Ví dụ, nếu vòng chơi đang hoạt động, thuộc tính Neutral được đặt thành false để spawnPlayersInMap có thể sắp xếp người chơi vào các đội và spawn họ vào đấu trường. Tuy nhiên, nếu vòng chơi không hoạt động, chẳng hạn như thời gian giữa một vòng và vòng tiếp theo, thuộc tính Neutral được đặt thành true để người chơi có thể spawn ở đó bất kể trạng thái đội của họ. Quá trình này là điều làm cho vị trí spawn Neutral trở thành một sảnh chức năng.

Để minh họa, nếu bạn kiểm tra ServerScriptService ⟩ Gameplay ⟩ Rounds ⟩ SpawnPlayersInLobby, chạy vào cuối một vòng chơi, bạn có thể thấy rằng đối với mỗi người chơi được truyền vào bảng players: { Player }, script:
- Đặt thuộc tính Player.Neutral của họ thành true để tự động đặt lại Player.Team của họ thành nil, cho phép người chơi respawn trong sảnh khi một vòng không hoạt động, vì thuộc tính Neutral của vị trí spawn cũng được đặt thành true.
- Thay đổi trạng thái PlayerState của họ thành InLobby để loại bỏ blaster của người chơi và các hình ảnh giao diện người dùng trong chế độ xem thứ nhất.
Để biết thêm thông tin về khu vực spawn trung lập và chức năng của nó cho mỗi vòng, hãy xem Thêm Vòng trong phần tiếp theo của hướng dẫn.
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
endKết nối người chơi mới
Mã Luau trong Studio thường được điều khiển bởi sự kiện, có nghĩa là các script lắng nghe các sự kiện từ một dịch vụ Roblox, sau đó gọi một hàm để phản hồi. Ví dụ, khi thêm người chơi mới vào một trò chơi nhiều người, phải có một sự kiện xử lý mọi thứ cần thiết để người chơi kết nối thành công. Trong trò chơi laser tag mẫu, sự kiện tương ứng này là Players.PlayerAdded:Connect.
Players.PlayerAdded:Connect là một phần của nhiều script trong trò chơi. Nếu bạn sử dụng phím tắt Ctrl/Cmd+Shift+F và tìm kiếm Players.PlayerAdded:Connect, các kết quả cung cấp một điểm khởi đầu tốt để hiểu về thiết lập ban đầu của trò chơi.

Để minh họa, mở ServerScriptService ⟩ SetupHumanoid. Sự phân biệt giữa Player và Character là chìa khóa để hiểu script này:
- Người chơi cần chọn một blaster và được thêm vào bảng điểm. Nhân vật cần được spawn và nhận một blaster.
SetupHumanoid ngay lập tức kiểm tra xem người chơi có một nhân vật (vừa tham gia) hay không (đang respawn). Sau khi tìm thấy một cái, nó gọi onCharacterAdded(), lấy mô hình Humanoid từ nhân vật và truyền nó đến ServerScriptService ⟩ SetupHumanoid ⟩ setupHumanoidAsync để tùy chỉnh. Sau khi thiết lập các giá trị này, script sẽ chờ cho sức khỏe của nhân vật đạt đến zero. Bạn sẽ tìm hiểu thêm về respawn sau trong phần này của hướng dẫn.
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)
endLưu ý quan trọng với script này là các thuộc tính hoàn toàn là tùy chọn, có nghĩa là nếu bạn xóa sáu dòng đầu tiên của hàm, trò chơi vẫn hoạt động đúng. Thay vì là yêu cầu chức năng, mỗi thuộc tính cho phép bạn đưa ra các quyết định thiết kế phù hợp với mục tiêu gameplay của bạn. Ví dụ:
- Nếu bạn muốn tên nhân vật hiển thị ở khoảng cách gần hơn, giảm giá trị của Humanoid.NameDisplayDistance.
- Nếu bạn chỉ muốn hiển thị sức khỏe của nhân vật nếu nó dưới 100%, đặt Humanoid.HealthDisplayType thành DisplayWhenDamaged.
- Nếu bạn muốn nhân vật bị vỡ ra khi sức khỏe của họ đạt 0, đặt Humanoid.BreakJointsOnDeath thành True.
Nếu bạn thay đổi các giá trị của những thuộc tính này, điều quan trọng là phải thử nghiệm để bạn có thể thấy tác động của các cài đặt mới của bạn. Bạn có thể tái tạo những gì người chơi trải nghiệm trong một mô phỏng nhiều client bằng cách chọn ít nhất hai nhân vật trong một bài thử nghiệm Server & Clients từ mezzanine.

Một ví dụ khác về sự kiện Players.PlayerAdded:Connect là trong ServerScriptService ⟩ PlayerStateHandler. Giống như trong ví dụ trước, PlayerStateHandler ngay lập tức kiểm tra xem có một nhân vật hay không. Nếu người chơi không ở trong sảnh, script đặt một thuộc tính của người chơi thành trạng thái SelectingBlaster, trạng thái ban đầu cho một vòng mà trong đó người chơi có thể chọn từ một trong hai loại blaster khác nhau sau khi spawn vào đấu trường. Trạng thái này cũng bao gồm một trường lực ngăn chặn người chơi bị sát thương trong khi họ đang thực hiện lựa chọn của mình.
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)Một biến cụ thể trong PlayerStateHandler đáng được thảo luận: attributeChangedConnectionByPlayer. Bảng này lưu trữ tất cả người chơi và Connections của họ đến GetAttributeChangedSignal. Lý do lưu trữ kết nối này trong một bảng là để PlayerStateHandler có thể ngắt kết nối nó khi người chơi rời khỏi trò chơi. Quá trình này phục vụ như một loại quản lý bộ nhớ để ngăn số lượng kết nối ngày càng tăng theo thời gian.
local attributeChangedConnectionByPlayer = {}
local function onPlayerAdded(player: Player)
-- Xử lý tất cả các cập nhật trong tương lai đến trạng thái người chơi
attributeChangedConnectionByPlayer[player] = player
:GetAttributeChangedSignal(PlayerAttribute.playerState)
:Connect(function()
local newPlayerState = player:GetAttribute(PlayerAttribute.playerState)
onPlayerStateChanged(player, newPlayerState)
end)
end
-- Ngắt kết nối khỏi kết nối thay đổi thuộc tính khi người chơi rời
local function onPlayerRemoving(player: Player)
if attributeChangedConnectionByPlayer[player] then
attributeChangedConnectionByPlayer[player]:Disconnect()
attributeChangedConnectionByPlayer[player] = nil
end
endBạn có thể thấy rằng cả hai hàm kết nối trong onPlayerAdded() đều gọi onPlayerStateChanged(). Trong thiết lập ban đầu sau khi một người chơi được sắp xếp vào một đội, onPlayerAdded() đặt PlayerState thành SelectingBlaster, vì vậy câu lệnh if đầu tiên đánh giá là false và vô hiệu hóa BlasterState. Trong phần Triển khai blaster sau của hướng dẫn, bạn sẽ tìm hiểu thêm chi tiết về quy trình này.
local function onPlayerStateChanged(player: Player, newPlayerState: string)
-- Trạng thái blaster là 'Ready' chỉ khi trạng thái người chơi là 'Playing'
local newBlasterState = if newPlayerState == PlayerState.Playing then BlasterState.Ready else BlasterState.Disabled
-- Lên lịch logic xóa trường lực khi người chơi bắt đầu chơi
if newPlayerState == PlayerState.Playing then
scheduleDestroyForceField(player)
end
player:SetAttribute(PlayerAttribute.blasterStateServer, newBlasterState)
endNếu bạn thêm các điểm dừng hoặc thậm chí chỉ một câu lệnh print(), bạn có thể thấy rằng onPlayerStateChanged() được gọi thường xuyên trong suốt trò chơi: chẳng hạn như trong thiết lập ban đầu của một vòng, để tự đặt mình vào đường dẫn mã chính, sau khi người chơi chọn một blaster, và khi người chơi trở lại sảnh, hoặc vị trí spawn Neutral. Hơn nữa, sau khi người chơi chọn một blaster, ServerScriptService ⟩ BlasterSelectedHandler đặt PlayerState thành Playing, và PlayerStateHandler cuối cùng có thể xóa trường lực bằng cách gọi scheduleDestroyForceField().
Tùy chỉnh trường lực
Thay vì sử dụng một triển khai tùy chỉnh, trò chơi laser tag mẫu sử dụng lớp ForceField tích hợp sẵn của Studio để ngăn chặn người chơi bị sát thương trong khi họ đang chọn blaster của mình. Điều này đảm bảo rằng yêu cầu duy nhất để người chơi spawn với một trường lực là bao gồm các vị trí spawn với thuộc tính SpawnLocation.Duration lớn hơn 0. Mẫu sử dụng một giá trị tùy ý là 9,999 để kích hoạt các trường lực, sau đó xử lý thời gian thực tế một cách lập trình trong ReplicatedStorage ⟩ ForceFieldClientVisuals.
Tương tự như setupHumanoidAsync, hầu hết các dòng trong ForceFieldClientVisuals là tùy chọn. Ví dụ, nếu bạn bình luận nội dung của hàm như script sau đây, trò chơi sẽ sử dụng trường lực lấp lánh mặc định thay vì script hình lục giác trong StarterGui ⟩ ForceFieldGui.
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
endBởi vì trường lực tùy chỉnh là một GUI thay vì một ParticleEmitter mới, script ForceFieldClientVisuals chỉ ảnh hưởng đến hình ảnh trong chế độ xem thứ nhất cho mỗi người chơi, không ảnh hưởng đến hình ảnh trong chế độ xem thứ ba khi người chơi nhìn vào người chơi khác. Hình ảnh trong chế độ xem thứ ba giữ nguyên diện mạo mặc định của Roblox. Để biết thêm thông tin về việc sửa đổi trường lực, hãy xem ForceField.Visible.


Trường lực rất hữu ích vì chúng cung cấp cho người chơi đủ thời gian giữa việc spawn và respawn mà không cần lo lắng về những người chơi đối thủ, nhưng cuối cùng chúng cần biến mất để gameplay laser tag chính diễn ra. Script xử lý việc xóa trường lực nằm trong ReplicatedStorage ⟩ scheduleDestroyForceField, và nó kiểm tra ba điều kiện độc đáo:
- Sau khi người chơi chọn một blaster, trường lực cần tồn tại đủ lâu để cho phép người chơi làm quen với môi trường xung quanh.
- Trong thời gian làm quen này, trường lực không thể là một lợi thế, vì vậy chúng cần biến mất ngay khi một người chơi bắn blaster của họ.
- Trường lực cần biến mất khi người chơi đặt lại nhân vật của họ, hoặc trước khi bắn hoặc trước khi trường lực hết thời gian.
Mỗi trong số các kiểm tra này trong script scheduleDestroyForceField gọi endForceField() cho các điều kiện này.
-- Kết thúc trường lực nếu người chơi bắn
local blasterStateAttribute = getBlasterStateAttribute()
attributeChangedConnection = player:GetAttributeChangedSignal(blasterStateAttribute):Connect(function()
local currentBlasterState = player:GetAttribute(blasterStateAttribute)
if currentBlasterState == BlasterState.Blasting then
endForceField()
end
end)
-- Kết thúc trường lực nếu người chơi đặt lại
characterRespawnedConnection = player.CharacterRemoving:Connect(endForceField)
-- Kết thúc trường lực sau 8 giây
task.delay(MAX_FORCE_FIELD_TIME, endForceField)endForceField() bao gồm một câu lệnh if có vẻ kỳ lạ xung quanh boolean forceFieldEnded. Bởi vì các kiểm tra chạy tuần tự, script có thể gọi hàm endForceField() hai hoặc thậm chí ba lần. Boolean forceFieldEnded đảm bảo rằng hàm chỉ cố gắng xóa một trường lực một lần.
local function endForceField()
if forceFieldEnded then
return
end
forceFieldEnded = true
attributeChangedConnection:Disconnect()
characterRespawnedConnection:Disconnect()
destroyForceField(player)
endXử lý trạng thái client
Trong khi phần lớn của phần này tập trung vào ServerScriptService ⟩ PlayerStateHandler, có một script khác cùng tên trong ReplicatedStorage. Lý do cho sự phân chia này là kiến trúc client-server:
Client cần hiểu thông tin trạng thái người chơi để có thể phản hồi một cách thích hợp trong thời gian thực, chẳng hạn như hiển thị các yếu tố giao diện người dùng đúng, hoặc cho phép người chơi di chuyển và bắn.
Server cần tất cả thông tin này để có thể ngăn chặn các hành vi gian lận. Ví dụ, server cũng cần trạng thái người chơi để thực hiện các hành động như spawn và trang bị nhân vật, vô hiệu hóa trường lực, và hiển thị bảng điểm. Đây là lý do tại sao script này nằm trong ReplicatedStorage và không phải là một vị trí hoàn toàn phía client.
Để xem logic cốt lõi này, hãy xem script sau trong ReplicatedStorage ⟩ PlayerStateHandler xác minh trạng thái hiện tại của người dùng, sau đó gọi hàm thích hợp xử lý các hành động tương ứng cho trạng thái đó.
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
endTất cả các phản hồi sự kiện được nhóm lại một cách hợp lý trong script này vì chúng yêu cầu hành vi tương tự về việc kích hoạt hoặc vô hiệu hóa điều khiển của người chơi, di chuyển camera, và lớp UI nào là hiển thị. Ví dụ, trong quá trình chọn blaster, người chơi cần vừa không thể bị tổn thương vừa không thể di chuyển. Server đã xử lý trường lực, nhưng client xử lý việc di chuyển. Để minh họa, nếu bạn kiểm tra logic cho hàm onSelectingBlaster(), bạn có thể thấy rằng client vô hiệu hóa việc di chuyển của người chơi trong khi họ đang chọn một blaster.
local function onSelectingBlaster()
togglePlayerCamera(true)
togglePlayerMovement(false)
setGuiExclusivelyEnabled(playerGui.PickABlasterGui)
localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Disabled)
endHàm onPlaying() cũng tương tự như vậy. Nó kích hoạt việc di chuyển, chuyển sang hiển thị chính (HUD), kích hoạt blaster, và gọi cùng một hàm trường lực như server.
local function onPlaying()
togglePlayerMovement(true)
setGuiExclusivelyEnabled(playerGui.HUDGui)
localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Ready)
scheduleDestroyForceField()
endRespawn các nhân vật
Trò chơi laser tag mẫu xử lý việc respawn nhân vật trở lại vào một vòng thông qua trạng thái onTaggedOut() trong ReplicatedStorage ⟩ PlayerStateHandler. Giống như trạng thái onSelectingBlaster() và onPlaying(), onTaggedOut() kích hoạt hành vi độc đáo theo các thay đổi của thuộc tính playerState. Cụ thể, nó vô hiệu hóa việc di chuyển của người chơi, hiển thị UI respawn, và vô hiệu hóa blaster.
local function onTaggedOut()
-- Vô hiệu hóa điều khiển khi bị loại
togglePlayerMovement(false)
togglePlayerCamera(false)
setGuiExclusivelyEnabled(playerGui.OutStateGui)
-- Vô hiệu hóa blaster khi bị loại
localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Disabled)
endNếu bạn muốn kiểm tra hành vi này, bạn có thể nhấn Esc, điều hướng đến tab Cài đặt, sau đó nhấp vào nút Đặt lại Nhân vật. Lưu ý rằng khi bạn kích hoạt màn hình respawn, bạn không thể di chuyển, xoay camera, hoặc bắn blaster của mình.


Điều quan trọng cần lưu ý là script này không thực sự respawn các nhân vật, nó chỉ ngăn họ hành động và cung cấp phản hồi hình ảnh cho người chơi rằng server đang respawn các nhân vật của họ. Để minh họa, nếu bạn kiểm tra ServerScriptService ⟩ SetupHumanoid ⟩ setupHumanoidAsync ⟩ onHumanoidDied, script đặt PlayerState thành TaggedOut (về cơ bản thông báo cho ReplicatedStorage ⟩ PlayerStateHandler), và thêm một số chỉ báo hình ảnh. Logic thực tế của việc respawn là một hành vi tích hợp sẵn của Roblox.
Khi người chơi respawn trở lại vào vòng chơi, họ respawn tại vị trí spawn của đội mình theo thuộc tính SpawnLocation.TeamColor. Để tùy chỉnh thời gian respawn, bạn có thể thêm dòng sau vào đầu SetupHumanoid. Để tìm hiểu thêm về kỹ thuật này, hãy xem Players.RespawnTime.
local Players = game:GetService("Players")
Players.RespawnTime = 10 -- dòng mới, tính bằng giâyThiết lập linh tinh
Như một phần của thiết lập ban đầu, trò chơi laser tag mẫu cũng thực hiện một số bước nhỏ nhưng quan trọng:
Trò chơi bao gồm một script trống có tên StarterPlayer ⟩ StarterCharacterScripts ⟩ Health mà vô hiệu hóa việc hồi phục sức khỏe mặc định của Roblox. Để giải thích về hành vi của thuộc tính này, hãy xem Humanoid.Health.
Trò chơi sử dụng camera chế độ xem thứ nhất bằng cách đặt thuộc tính StarterPlayer.CameraMode.LockFirstPerson. Lưu ý rằng nếu bạn muốn cho phép người dùng thay đổi giữa camera chế độ xem thứ nhất và thứ ba, bạn phải thay đổi thuộc tính này một cách lập trình thay vì chỉ đặt nó một lần trong Studio, và sửa đổi các điều khiển và UI để bù đắp cho sự thay đổi trong góc nhìn.
Trò chơi sử dụng bảng điểm tích hợp sẵn của Roblox với đơn vị "điểm", mà người chơi kiếm được mỗi khi họ tag một người chơi khác ra ngoài. Bạn có thể thấy cấu hình trong ServerScriptService ⟩ SetupLeaderboard, nhưng Bảng điểm trong trò chơi cung cấp một cái nhìn tổng quan đầy đủ. Lưu ý rằng onPlayerTagged thêm điểm vào bảng điểm, mà bạn sẽ tìm hiểu trong Thêm vòng và Phát hiện cú đánh.
Bây giờ người chơi có thể spawn, chọn một blaster, và nhắm từ góc nhìn chế độ xem thứ nhất, phần tiếp theo sẽ dạy bạn về các script đứng sau việc tạo gameplay theo vòng.