Menambahkan putaran memungkinkan Anda untuk menyusun gameplay menjadi fase dengan titik awal dan akhir yang jelas sehingga pemain dapat mengukur kemajuan mereka dan memiliki kesempatan berkala untuk bermain di lapangan yang setara. Ini sangat penting untuk gameplay berbasis tim karena memberikan pemain kesempatan untuk mengubah gaya bermain mereka tergantung pada siapa yang ada di tim mereka selama putaran tersebut.
Menggunakan contoh permainan laser tag sebagai referensi, bagian tutorial ini mengajarkan Anda cara menggunakan dan menyesuaikan fitur bawaan Roblox untuk menyusun setiap putaran, termasuk panduan scripting tentang:
- Memulai putaran dengan mereset poin individu dan tim, kemudian memunculkan pemain ke zona spawn tim mereka.
- Menyesuaikan variabel yang menetapkan tujuan untuk putaran di bagian atas layar setiap pemain.
- Melacak kontribusi poin pemain untuk skor tim mereka.
- Memicu layar UI unik tergantung pada apakah tim pemain menang atau kalah dalam putaran.
- Mengakhiri putaran dengan memutuskan koneksi pemain dan memunculkan mereka di lobi netral.
Setelah Anda menyelesaikan bagian ini, Anda akan belajar bagaimana mengimplementasikan perilaku blaster yang akurat dan memuaskan bagi pemain.

Mulai loop
ServerScriptService ⟩ Gameplay ⟩ Rounds menangani sebagian besar logika untuk mengimplementasikan putaran, dan dimulai dengan memanggil fungsi startRoundLoopAsync() untuk menandai awal dari loop putaran. Sementara pemain bergabung ke lobi dan menunggu untuk disortir ke dalam tim, startRoundLoopAsync() memanggil fungsi resetScores() di ServerScriptService ⟩ Gameplay ⟩ Scoring untuk mereset papan peringkat dan poin tim.
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
endSekarang semua orang memulai dengan nol poin, startRoundLoopAsync() kemudian mengatur properti lokasi spawn Neutral Neutral menjadi false sehingga hanya pemain dengan properti Team.Color yang sama dengan properti TeamColor lokasi spawn yang dapat muncul di sana. Karena properti TeamColor lokasi spawn diatur ke putih alih-alih tim mint atau pink karnasi dari contoh, konfigurasi ini mencegah semua pemain dari muncul atau respawn di sana sementara putaran aktif.
Untuk pemain yang saat ini berada di lobi, startRoundLoopAsync() meneruskan semua pemain yang saat ini berada dalam permainan ke fungsi spawnPlayersInMap di ServerScriptService ⟩ Gameplay ⟩ Rounds ⟩ spawnPlayersInMap untuk menyortir dan menyeimbangkan semua orang ke dalam tim dengan jumlah pemain yang kira-kira sama.
Untuk pemain baru yang bergabung ke permainan setelah grup lobi disortir ke dalam tim, startRoundLoopAsync() mendengarkan acara Players.PlayerAdded:Connect, kemudian memanggil fungsi spawnPlayersInMap lagi untuk menambahkan mereka ke tim dengan jumlah pemain paling sedikit. Untuk informasi lebih lanjut tentang proses ini, lihat Konfigurasi Lokasi Spawn dari bagian sebelumnya Memunculkan dan Respawn dari tutorial.
-- Spawn semua pemain di peta
neutralSpawn.Neutral = false
spawnPlayersInMap(Players:GetPlayers())
-- Spawn pemain baru di peta saat mereka bergabung
local playerAddedConnection = Players.PlayerAdded:Connect(function(player: Player)
spawnPlayersInMap({ player })
end)Tetapkan tujuan
Sekarang setiap pemain berada di arena dengan rekan tim mereka, permainan perlu memberikan instruksi tentang apa yang harus dilakukan untuk berhasil dalam putaran. Permainan laser tag contoh menangani kebutuhan ini dengan memberikan prompt tujuan di bagian atas layar setiap pemain dengan panduan yang jelas tentang apa yang perlu dilakukan tim untuk menang.

Sementara Anda dapat mempelajari lebih lanjut tentang cara mengonfigurasi dan menampilkan komponen UI Tujuan dalam Kurikulum UI, bagian ini berfokus pada cara mengimplementasikan tujuan saat putaran dimulai, dimulai dengan cara menetapkan jumlah poin yang perlu dicapai setiap tim untuk menyelesaikan putaran.
Meskipun prompt tujuan saat runtime memberi tahu pemain bahwa mereka perlu mencetak tiga poin untuk menang, jika Anda memeriksa prompt di StarterGui ⟩ HUDGui, Anda dapat melihat bahwa itu sebenarnya berisi "%d" yang dapat dikonfigurasi untuk nilai poin.

"%d" adalah string placeholder yang dapat Anda tingkatkan atau kurangi kapan saja untuk memenuhi kebutuhan gameplay Anda dengan memperbarui variabel TEAM_SCORE_LIMIT di ReplicatedStorage ⟩ TEAM_SCORE_LIMIT. Misalnya, jika Anda mengatur angka ini menjadi 200 yang sangat tinggi, prompt dan penghitung poin tim akan diperbarui sesuai.
local TEAM_SCORE_LIMIT = 200 -- baris yang diperbarui, pastikan untuk mengubah kembali
return TEAM_SCORE_LIMIT
Pembaruan variabel sederhana ini berfungsi saat runtime karena saat putaran dimulai, ReplicatedStorage ⟩ HUDGuiSetup ⟩ SetObjective memerlukan skrip modul TEAM_SCORE_LIMIT sehingga dapat menukar string placeholder di objek TextLabel UI Tujuan.
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)
endLacak poin
Sekarang pemain memiliki tujuan untuk putaran, permainan perlu melacak poin setiap tim sampai mereka mencapai tujuan mereka. Sementara perilaku default layanan Teams secara otomatis mengelompokkan setiap pemain di bawah tim mereka dan menjumlahkan kontribusi setiap pemain ke skor tim mereka, penting untuk menyimpan dan memantau poin di lokasi terpisah untuk gameplay berbasis putaran karena jika seorang pemain mencetak poin kemudian meninggalkan sebelum putaran berakhir, kontribusinya akan dikurangi dari papan peringkat segera setelah mereka terputus dari permainan.
Untuk memastikan ini tidak terjadi dan setiap kontribusi menuju tujuan tim dipertahankan, ReplicatedStorage ⟩ HUDGuiSetup ⟩ StartSyncingTeamPoints menyimpan semua poin secara terpisah di bawah atribut teamPoints dalam layanan Teams. Saat teamPoints meningkat, skrip modul ini memanggil fungsi startSyncingTeamPoints untuk menemukan penghitung tim Class.GuiObjects dalam komponen UI Tujuan.
Ketika menemukan TeamACounter dan TeamBCounter, ia mendapatkan atribut teamColor mereka, yang berkorelasi dengan zona spawn tim: TeamACounter menampilkan poin tim hijau, dan TeamBCounter melacak poin tim pink.
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)Skrip modul kemudian memanggil fungsi getTeamFromTeamColor untuk memvalidasi bahwa atribut teamColor mint dari TeamACounter dan atribut teamColor pink karnasi dari TeamBCounter keduanya cocok dengan properti Team.Color yang sesuai di bawah layanan Teams. Jika ya, ia mengembalikan kedua tim tersebut.
local function getTeamFromTeamColor(teamColor: Color3): Team?
for _, team in Teams:GetTeams() do
if team.TeamColor == teamColor then
return team
end
end
return nil
endKetika ini terjadi, startSyncingTeamPoints mengatur objek TextLabel kedua penghitung tim ke nilai teamPoints mereka yang sesuai, dan terus memperbarui mereka setiap kali seorang pemain mencetak poin dengan menandai pemain lain di tim lawan.
teamPointCounter.TextLabel.Text = team:GetAttribute(GuiAttribute.teamPoints)
team:GetAttributeChangedSignal(GuiAttribute.teamPoints):Connect(function()
teamPointCounter.TextLabel.Text = team:GetAttribute(GuiAttribute.teamPoints)Semua yang ada di bagian ini sejauh ini telah berfokus pada cara melacak poin di layar pemain, tetapi penting untuk meninjau logika yang menangani pelacakan poin di server sehingga ia tahu kapan sebuah tim mencapai tujuan dan memenangkan putaran. Jika Anda mengunjungi kembali ServerScriptService ⟩ Gameplay ⟩ Scoring, Anda dapat melihat bahwa skrip modul dimulai dengan membuat sebuah acara yang dapat diikat, yang akan dipicu setiap kali seorang pemain mencetak poin.
local teamScoreChangedBindable = Instance.new("BindableEvent")
local Scoring = {
teamScoreChanged = teamScoreChangedBindable.Event,
}Kemudian memanggil fungsi incrementScore, yang melakukan tindakan berikut:
- Mengambil skor individu pemain di papan peringkat, dan menambah satu.
- Memicu acara yang dapat diikat yang disebutkan sebelumnya dengan tim pemain dan skor mereka.
Proses ini secara efektif menjaga agar klien dan server tetap selaras mengenai skor individu pemain dan skor tim mereka.
function Scoring.incrementScore(player: Player, amount: number)
local team = player.Team
assert(team, `Pemain {player.Name} harus berada di tim untuk mencetak poin, tetapi tidak memiliki tim`)
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)
endTampilkan hasil
Saat pemain menandai satu sama lain dan mencetak poin untuk tim mereka, ServerScriptService ⟩ Gameplay ⟩ Rounds memeriksa apakah tim yang mencetak poin memenuhi tujuan putaran. Jika skor tim mereka lebih rendah dari variabel TEAM_SCORE_LIMIT di ReplicatedStorage ⟩ TEAM_SCORE_LIMIT, server terus menunggu sampai salah satu tim mencetak lagi.
Namun, setelah skor tim mencapai variabel TEAM_SCORE_LIMIT, skrip memicu sebuah instance acara roundWinnerRemote dengan nama pemain dan tim mereka.
-- Periksa apakah putaran telah selesai setelah setiap skor
local team: Team
local score: number = 0
while score < TEAM_SCORE_LIMIT do
team, score = Scoring.teamScoreChanged:Wait()
end
-- Tampilkan tim pemenang
for _, player in Players:GetPlayers() do
-- Mengirimkan tim apa yang dimiliki pemain di akhir putaran
-- karena tim pemain akan segera dihapus, jadi klien
-- tidak akan dapat memeriksa timnya sendiri
roundWinnerRemote:FireClient(player, team, player.Team)
endSkrip ReplicatedStorage ⟩ RoundResultsGuiSetup di setiap klien mendengarkan instance acara roundWinnerRemote ini sehingga dapat:
- Menampilkan layar UI StarterGui ⟩ RoundResultsGui yang unik yang mengumumkan hasil putaran dan apakah pemain berada di tim pemenang.
- Memutar klip audio kemenangan atau kekalahan.
Sebagai contoh, jika seorang pemain berada di tim yang mencetak poin kemenangan, mereka menerima beberapa bentuk umpan balik tentang hasil putaran dalam bentuk layar UI yang menampilkan teks kemenangan, dan klip audio yang memutar suara ceria. Sebaliknya, jika seorang pemain tidak berada di tim yang mencetak poin kemenangan, mereka menerima layar UI yang menampilkan teks kekalahan, dan klip audio yang memutar suara yang mengerikan.
local function onRoundWinner(winner: Team, localTeam: Team?)
local victoryDefeatText = "Putaran berakhir!"
if localTeam then
-- Jika tim kita menang, kita akan menampilkan Kemenangan! Jika tidak, tampilkan Kekalahan...
local isVictory = winner == localTeam
if isVictory then
victorySound:Play()
victoryDefeatText = VICTORY_TEXT
else
defeatSound:Play()
victoryDefeatText = DEFEAT_TEXT
end
endReset tim
Pada saat yang sama bahwa ServerScriptService ⟩ Gameplay ⟩ Rounds memverifikasi bahwa sebuah tim memenuhi tujuan putaran dan memicu tampilan UI yang sesuai untuk setiap pemain, ia juga memindahkan semua pemain dari arena ke lobi dengan memutuskan koneksi mereka dari putaran. Ini memulai proses secara resmi mengakhiri putaran dan mereset kedua tim.
Menggunakan logika yang sama dalam Konfigurasi Lokasi Spawn, Rounds kemudian mengatur properti lokasi spawn Neutral Neutral menjadi true sehingga pemain dapat muncul di sana terlepas dari status tim mereka. Ini berarti lobi menjadi satu-satunya lokasi di mana pemain dapat muncul setelah mereka terputus dari putaran.
-- Kirim semua orang ke lobi
playerAddedConnection:Disconnect()
neutralSpawn.Neutral = true
spawnPlayersInLobby(Players:GetPlayers())Setelah menunggu sepuluh detik untuk intermission, skrip server Rounds kemudian memulai loop lagi dengan mereset skor semua orang dan menyortir mereka ke dalam tim baru. Contoh ini mengulangi proses putaran siklik ini lagi sampai tidak ada pemain di dalam server.
Sekarang pemain dapat muncul ke dalam peta dengan tim mereka sendiri dan memainkan satu putaran penuh, bagian selanjutnya mengajarkan Anda tentang skrip di balik perilaku setiap blaster.