Thêm vòng

*Nội dung này được dịch bằng AI (Beta) và có thể có lỗi. Để xem trang này bằng tiếng Anh, hãy nhấp vào đây.

Thêm vòng cho phép bạn cấu trúc gameplay thành các giai đoạn với một điểm bắt đầu và kết thúc rõ ràng để người chơi có thể đo lường tiến trình của họ và có cơ hội định kỳ để có một sân chơi công bằng. Điều này đặc biệt quan trọng đối với gameplay theo đội vì nó cung cấp cho người chơi cơ hội thay đổi phong cách chơi của họ tùy thuộc vào ai đang ở trong đội của họ trong vòng đó.

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 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 để cấu trúc mỗi vòng, bao gồm hướng dẫn lập trình về:

  • Bắt đầu một vòng bằng cách đặt lại điểm cá nhân và điểm đội, sau đó đưa người chơi đến khu vực xuất hiện của đội họ.
  • Tùy chỉnh các biến thiết lập mục tiêu cho vòng ở đầu màn hình của mỗi người chơi.
  • Theo dõi đóng góp điểm của người chơi cho điểm số của đội họ.
  • Kích hoạt các màn hình UI độc đáo tùy thuộc vào việc đội của người chơi thắng hay thua vòng.
  • Kết thúc một vòng bằng cách ngắt kết nối người chơi và đưa họ vào sảnh trung lập.

Sau khi bạn hoàn thành phần này, bạn sẽ học cách thực hiện hành vi của súng bắn mà chính xác và thỏa mãn người chơi.

Bắt đầu vòng lặp

ServerScriptServiceGameplayRounds xử lý hầu hết logic cho việc thực hiện các vòng, và nó bắt đầu bằng cách gọi hàm startRoundLoopAsync() để đánh dấu sự bắt đầu của một vòng lặp. Trong khi người chơi tham gia vào sảnh và chờ để phân loại vào một đội, startRoundLoopAsync() gọi hàm resetScores() trong ServerScriptServiceGameplayScoring để đặt lại cả bảng điểm và điểm đội.

Scoring
function Scoring.resetScores()
for _, player in Players:GetPlayers() do
player.leaderstats.Points.Value = 0
end
for _, team in Teams:GetTeams() do
team:SetAttribute(GuiAttribute.teamPoints, 0)
end
end

Bây giờ mọi người đều bắt đầu với điểm số bằng không, startRoundLoopAsync() sau đó đặt thuộc tính Neutral của vị trí xuất hiện Neutral thành false để chỉ những người chơi có thuộc tính Team.Color giống như thuộc tính TeamColor của vị trí xuất hiện mới có thể xuất hiện ở đó. Bởi vì thuộc tính TeamColor của vị trí xuất hiện được đặt thành trắng thay vì các đội màu mint hoặc hồng, cấu hình này ngăn tất cả người chơi xuất hiện hoặc tái xuất hiện ở đó trong khi một vòng đang hoạt động.

Đối với những người chơi hiện đang ở trong sảnh, startRoundLoopAsync() chuyển tất cả người chơi hiện có trong trò chơi đến hàm spawnPlayersInMap trong ServerScriptServiceGameplayRoundsspawnPlayersInMap để phân loại và cân bằng mọi người vào một đội với số lượng người chơi tương đương.

Đối với bất kỳ người chơi mới nào tham gia trò chơi sau khi nhóm trong sảnh được phân loại vào một đội, startRoundLoopAsync() lắng nghe sự kiện Players.PlayerAdded:Connect, sau đó gọi lại hàm spawnPlayersInMap để thêm họ vào đội có ít người chơi nhất. Để biết thêm thông tin về quy trình này, hãy xem Cấu hình Vị trí Xuất hiện từ phần Xuất hiện và Tái xuất hiện trước đó của hướng dẫn.

Rounds
-- Xuất hiện tất cả người chơi trong bản đồ
neutralSpawn.Neutral = false
spawnPlayersInMap(Players:GetPlayers())
-- Xuất hiện người chơi mới trong bản đồ khi họ tham gia
local playerAddedConnection = Players.PlayerAdded:Connect(function(player: Player)
spawnPlayersInMap({ player })
end)

Đặt mục tiêu

Bây giờ mỗi người chơi đã ở trong đấu trường với các đồng đội của họ, trò chơi cần cung cấp hướng dẫn về những gì cần làm để thành công trong vòng. Trò chơi laser tag mẫu giải quyết yêu cầu này bằng cách cung cấp một thông báo mục tiêu ở đầu màn hình của mỗi người chơi với hướng dẫn rõ ràng về những gì đội cần làm để thắng.

Trong khi bạn có thể tìm hiểu thêm về cách cấu hình và hiển thị thành phần UI Mục tiêu trong Chương trình UI, phần này tập trung vào cách thực hiện mục tiêu khi vòng bắt đầu, bắt đầu với cách đặt số điểm mà mỗi đội cần hoàn thành vòng.

Mặc dù thông báo mục tiêu tại thời gian chạy thông báo cho người chơi rằng họ cần ghi được ba điểm để thắng, nếu bạn kiểm tra thông báo trong StarterGuiHUDGui, bạn có thể thấy rằng nó chứa một chuỗi có thể cấu hình "%d" cho giá trị điểm.

"%d" là một chuỗi giữ chỗ mà bạn có thể tăng hoặc giảm bất kỳ lúc nào để đáp ứng yêu cầu gameplay của riêng bạn bằng cách cập nhật biến TEAM_SCORE_LIMIT trong ReplicatedStorageTEAM_SCORE_LIMIT. Ví dụ, nếu bạn đặt số này thành một số quá cao là 200, thông báo và bộ đếm điểm đội sẽ cập nhật tương ứng.

TEAM_SCORE_LIMIT
local TEAM_SCORE_LIMIT = 200 -- dòng đã cập nhật, hãy chắc chắn thay đổi lại
return TEAM_SCORE_LIMIT

Cập nhật biến đơn giản này hoạt động trong thời gian chạy vì khi vòng bắt đầu, ReplicatedStorageHUDGuiSetupSetObjective yêu cầu mô-đun kịch bản TEAM_SCORE_LIMIT để nó có thể thay thế chuỗi giữ chỗ trong đối tượng TextLabel của UI Mục tiêu.

TEAM_SCORE_LIMIT
local TEAM_SCORE_LIMIT = require(ReplicatedStorage.TEAM_SCORE_LIMIT)
local function setObjective(gui: ScreenGui)
local bodyTextLabel = gui.Objective.ObjectiveDisplay.Body.BodyTextLabel
bodyTextLabel.Text = bodyTextLabel.Text:format(TEAM_SCORE_LIMIT)
end

Theo dõi điểm

Bây giờ người chơi đã có một mục tiêu cho vòng, trò chơi cần theo dõi điểm của mỗi đội cho đến khi họ đạt được mục tiêu của mình. Trong khi hành vi mặc định của dịch vụ Teams tự động nhóm mỗi người chơi dưới đội của họ và cộng dồn đóng góp của từng người chơi vào điểm số của đội họ, điều quan trọng là lưu trữ và theo dõi điểm ở một vị trí riêng biệt cho gameplay dựa trên vòng vì nếu một người chơi ghi điểm rồi rời đi trước khi vòng kết thúc, đóng góp của họ sẽ bị trừ khỏi bảng điểm ngay khi họ ngắt kết nối khỏi trò chơi.

Để đảm bảo điều này không xảy ra và mọi đóng góp cho mục tiêu đội được bảo tồn, ReplicatedStorageHUDGuiSetupStartSyncingTeamPoints lưu trữ tất cả điểm số riêng biệt dưới thuộc tính teamPoints trong dịch vụ Teams. Khi teamPoints tăng lên, mô-đun kịch bản này gọi hàm startSyncingTeamPoints để tìm bộ đếm đội Class.GuiObjects trong thành phần UI Mục tiêu.

Khi nó tìm thấy TeamACounterTeamBCounter, nó lấy thuộc tính teamColor của chúng, tương ứng với các khu vực xuất hiện của đội: TeamACounter hiển thị điểm của đội màu xanh lá cây, và TeamBCounter theo dõi điểm của đội màu hồng.

StartSyncingTeamPoints
local function startSyncingTeamPoints(gui: ScreenGui)
for _, teamPointCounter in gui.Objective.TeamPointCounter:GetChildren() do
if not teamPointCounter:IsA("GuiObject") then
continue
end
local iconTeamColor = teamPointCounter:GetAttribute(GuiAttribute.teamColor)

Mô-đun kịch bản sau đó gọi hàm getTeamFromTeamColor của nó để xác thực rằng thuộc tính teamColor mint của TeamACounter và thuộc tính teamColor carnation pink của TeamBCounter đều khớp với các thuộc tính Team.Color tương ứng dưới dịch vụ Teams. Nếu đúng, nó trả về cả hai đội.

StartSyncingTeamPoints
local function getTeamFromTeamColor(teamColor: Color3): Team?
for _, team in Teams:GetTeams() do
if team.TeamColor == teamColor then
return team
end
end
return nil
end

Khi điều này xảy ra, startSyncingTeamPoints đặt các đối tượng TextLabel của cả hai bộ đếm đội thành các giá trị teamPoints tương ứng của chúng, và tiếp tục cập nhật chúng bất cứ khi nào một người chơi ghi điểm bằng cách loại bỏ một người chơi khác trong đội đối diện.

StartSyncingTeamPoints
teamPointCounter.TextLabel.Text = team:GetAttribute(GuiAttribute.teamPoints)
team:GetAttributeChangedSignal(GuiAttribute.teamPoints):Connect(function()
teamPointCounter.TextLabel.Text = team:GetAttribute(GuiAttribute.teamPoints)

Mọi thứ trong phần này cho đến nay đã tập trung vào cách theo dõi điểm trên màn hình của người chơi, nhưng điều quan trọng là xem xét logic xử lý theo dõi điểm trên máy chủ để nó biết khi nào một đội đạt được mục tiêu và thắng vòng. Nếu bạn quay lại ServerScriptServiceGameplayScoring, bạn có thể thấy rằng mô-đun kịch bản bắt đầu bằng cách tạo một sự kiện có thể liên kết, sẽ được kích hoạt mỗi khi một người chơi ghi điểm.

Scoring
local teamScoreChangedBindable = Instance.new("BindableEvent")
local Scoring = {
teamScoreChanged = teamScoreChangedBindable.Event,
}

Sau đó, nó gọi hàm incrementScore, thực hiện các hành động sau:

  • Lấy đội của người chơi và giá trị teamPoints hiện tại của họ trên đối tượng Team trong dịch vụ Teams, sau đó cộng thêm một.
  • Lấy điểm cá nhân của người chơi trên bảng điểm, và cộng thêm một.
  • Kích hoạt sự kiện có thể liên kết đã đề cập trước đó với cả đội của người chơi và điểm số của họ.

Quy trình này hiệu quả giữ cho cả client và server đồng bộ về cả điểm số cá nhân của người chơi và điểm số đội của họ.

Scoring
function Scoring.incrementScore(player: Player, amount: number)
local team = player.Team
assert(team, `Người chơi {player.Name} phải ở trong một đội để ghi điểm, nhưng không có đội nào`)
local teamPoints = team:GetAttribute(GuiAttribute.teamPoints)
teamPoints += amount
team:SetAttribute(GuiAttribute.teamPoints, teamPoints)
local leaderstat = player.leaderstats.Points
leaderstat.Value += amount
teamScoreChangedBindable:Fire(team, teamPoints)
end

Hiển thị kết quả

Khi người chơi loại bỏ nhau và ghi điểm cho đội của họ, ServerScriptServiceGameplayRounds kiểm tra xem đội đã ghi điểm có đạt được mục tiêu vòng hay không. Nếu điểm số của đội họ thấp hơn biến TEAM_SCORE_LIMIT trong ReplicatedStorageTEAM_SCORE_LIMIT, máy chủ tiếp tục chờ cho đến khi một trong các đội ghi điểm lần nữa.

Tuy nhiên, khi điểm số của một đội đạt đến biến TEAM_SCORE_LIMIT, kịch bản kích hoạt một sự kiện roundWinnerRemote với tên của người chơi và đội của họ.

Rounds
-- Kiểm tra xem vòng đã kết thúc sau mỗi lần ghi điểm
local team: Team
local score: number = 0
while score < TEAM_SCORE_LIMIT do
team, score = Scoring.teamScoreChanged:Wait()
end
-- Hiển thị đội thắng
for _, player in Players:GetPlayers() do
-- Gửi thông tin đội mà người chơi đang ở vào cuối vòng
-- vì đội của người chơi sắp bị xóa, vì vậy client
-- sẽ không thể kiểm tra đội của chính mình
roundWinnerRemote:FireClient(player, team, player.Team)
end

Kịch bản ReplicatedStorageRoundResultsGuiSetup trên mỗi client lắng nghe sự kiện roundWinnerRemote này để nó có thể:

  • Hiển thị một màn hình UI StarterGuiRoundResultsGui độc đáo thông báo kết quả của vòng và nếu người chơi ở trong đội thắng.
  • Phát một đoạn âm thanh chiến thắng hoặc thất bại.

Ví dụ, nếu một người chơi ở trong đội ghi điểm chiến thắng, họ nhận được nhiều hình thức phản hồi về kết quả vòng dưới dạng một màn hình UI hiển thị văn bản chiến thắng, và một đoạn âm thanh phát ra âm thanh vui vẻ. Ngược lại, nếu một người chơi không ở trong đội ghi điểm chiến thắng, họ nhận được một màn hình UI hiển thị văn bản thất bại, và một đoạn âm thanh phát ra âm thanh đáng sợ.

Phản hồi chiến thắng
Phản hồi thất bại
RoundResultsGuiSetup
local function onRoundWinner(winner: Team, localTeam: Team?)
local victoryDefeatText = "Vòng đã kết thúc!"
if localTeam then
-- Nếu đội của chúng ta thắng, chúng ta sẽ hiển thị Chiến thắng! Ngược lại hiển thị Thất bại...
local isVictory = winner == localTeam
if isVictory then
victorySound:Play()
victoryDefeatText = VICTORY_TEXT
else
defeatSound:Play()
victoryDefeatText = DEFEAT_TEXT
end
end

Đặt lại đội

Cùng lúc mà ServerScriptServiceGameplayRounds xác minh rằng một đội đã đạt được mục tiêu vòng và kích hoạt hiển thị UI phù hợp cho mỗi người chơi, nó cũng chuyển tất cả người chơi từ đấu trường về sảnh bằng cách ngắt kết nối họ khỏi vòng. Điều này bắt đầu quy trình kết thúc chính thức vòng và đặt lại cả hai đội.

Sử dụng cùng một logic trong Cấu hình Vị trí Xuất hiện, Rounds sau đó đặt thuộc tính Neutral của vị trí xuất hiện Neutral thành true để người chơi có thể xuất hiện ở đó bất kể trạng thái đội của họ. Điều này có nghĩa là sảnh trở thành vị trí duy nhất mà người chơi có thể xuất hiện sau khi họ bị ngắt kết nối khỏi vòng.

Rounds
-- Gửi mọi người đến sảnh
playerAddedConnection:Disconnect()
neutralSpawn.Neutral = true
spawnPlayersInLobby(Players:GetPlayers())

Sau khi chờ mười giây cho một khoảng thời gian nghỉ, kịch bản máy chủ Rounds sau đó bắt đầu vòng lặp lại bằng cách đặt lại điểm số của mọi người và phân loại họ vào các đội mới. Mẫu lặp lại quy trình vòng này cho đến khi không còn người chơi nào trong máy chủ.

Bây giờ người chơi có thể xuất hiện vào bản đồ với đội của họ và chơi một vòng đầy đủ, phần tiếp theo sẽ dạy bạn về các kịch bản đứng sau hành vi của mỗi khẩu súng.

©2026 Roblox Corporation. Roblox, logo Roblox và Powering Imagination là các nhãn hiệu đã đăng ký và chưa đăng ký của chúng tôi tại Hoa Kỳ và các quốc gia khác.