Roblox
Belajar Luau 08: Pemain, Leaderstats, Koin, dan Checkpoint
Membuat game Roblox terasa sungguhan: perbedaan Player dan Character, event pemain, papan skor leaderstats, koin yang bisa dikumpulkan, dan checkpoint obby dengan respawn.

Di pelajaran ini game-mu mulai terasa seperti game sungguhan: ada papan skor di pojok layar, koin yang bisa dikumpulkan, dan checkpoint obby.
| Script | Isi | Diletakkan di |
|---|---|---|
| Script 1: leaderstats | Event PlayerAdded, membuat papan skor (Koin, Stage, Mati) |
ServerScriptService |
| Script 2: koin | Satu script yang mengurus banyak koin sekaligus | ServerScriptService |
| Script 3: checkpoint | Checkpoint obby dan respawn di checkpoint terakhir | ServerScriptService |
Ketiga script diletakkan di ServerScriptService, bukan di dalam Part. Untuk game yang mulai besar, cara ini jauh lebih rapi: satu script bisa mengurus 50 koin, sehingga tidak perlu 50 script.
Konsep 1: Player dan Character itu berbeda
Ini bagian yang paling sering membingungkan pemula.
game
├── Players
│ └── Budi ← PLAYER: "akun" pemain. Ada selama pemain online.
│ └── leaderstats Tempat menyimpan Koin, Stage, dll.
│
└── Workspace
└── Budi ← CHARACTER: "badan" 3D pemain (Model).
├── Humanoid Hilang saat mati, lalu dibuat BARU saat respawn.
├── HumanoidRootPart
└── ...
| Player | Character | |
|---|---|---|
| Letaknya | game.Players |
workspace |
| Kapan ada | Dari masuk sampai keluar game | Dibuat ulang setiap respawn |
| Cocok untuk menyimpan | Koin, level, stage | Tidak cocok, karena hilang saat mati |
| Dari satu ke yang lain | player.Character |
Players:GetPlayerFromCharacter(character) |
Itulah alasan koin disimpan di Player, bukan di Character. Kalau disimpan di Character, koin akan hilang setiap pemain mati.
Konsep 2: event pemain
| Event | Kapan terjadi |
|---|---|
Players.PlayerAdded |
Pemain masuk game |
Players.PlayerRemoving |
Pemain keluar game. Di sinilah data nanti disimpan (pelajaran 10). |
player.CharacterAdded |
Character pemain muncul, baik saat pertama masuk maupun setiap respawn |
humanoid.Died |
Character mati |
Konsep 3: leaderstats
Roblox punya aturan khusus: kalau di dalam Player ada Folder bernama persis leaderstats, isinya otomatis tampil sebagai papan skor di pojok kanan atas layar.
Isinya adalah objek Value:
IntValueuntuk bilangan bulat (koin, level, stage)NumberValueuntuk angka desimalStringValueuntuk teks
Nilainya diubah lewat properti .Value, misalnya koin.Value += 10.
Penting: leaderstats belum tersimpan. Saat pemain keluar lalu masuk lagi, semuanya kembali ke 0. Cara menyimpannya dengan DataStore dibahas di pelajaran 10.
Persiapan
Lanjutkan dari tempat pelajaran 07, atau buat New → Baseplate baru. Pastikan Explorer, Properties, dan Output terbuka.
Script 1: leaderstats
Langkah di Studio:
- Di ServerScriptService, tambahkan Script dan tempel kode di bawah.
- Tekan Play. Di pojok kanan atas akan muncul papan skor dengan kolom Koin, Stage, dan Mati.
- Saat Play masih berjalan, buka Explorer → Players → nama kamu. Folder
leaderstatsada di sana.
-- GetService adalah cara resmi mengambil "layanan" Roblox.
-- Lebih aman daripada game.Players.
local Players = game:GetService("Players")
-- Function ini dipanggil setiap kali ada pemain masuk.
-- Parameter "player" adalah objek Player milik pemain tersebut.
local function saatPemainMasuk(player)
print(`{player.Name} masuk ke game! (UserId: {player.UserId})`)
-- 1. Folder bernama PERSIS "leaderstats" -> otomatis jadi papan skor
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
-- 2. Setiap Value di dalamnya = satu kolom di papan skor.
-- Urutan kolom = urutan Value dibuat.
local koin = Instance.new("IntValue")
koin.Name = "Koin"
koin.Value = 0
koin.Parent = leaderstats
local stage = Instance.new("IntValue")
stage.Name = "Stage"
stage.Value = 0
stage.Parent = leaderstats
local mati = Instance.new("IntValue")
mati.Name = "Mati"
mati.Value = 0
mati.Parent = leaderstats
-- 3. Parent PALING AKHIR (setelah semua isinya siap)
leaderstats.Parent = player
-- 4. Hitung kematian.
-- CharacterAdded dipanggil setiap kali character muncul (termasuk
-- setiap respawn). Humanoid di character yang BARU perlu
-- dihubungkan lagi, karena character lama sudah dihapus.
player.CharacterAdded:Connect(function(character)
local humanoid = character:WaitForChild("Humanoid")
humanoid.Died:Connect(function()
mati.Value += 1
print(`{player.Name} mati. Total: {mati.Value}x`)
end)
end)
end
local function saatPemainKeluar(player)
local koin = player.leaderstats.Koin.Value
print(`{player.Name} keluar dengan {koin} koin`)
-- Di Pelajaran 10, di sinilah data akan DISIMPAN ke DataStore.
end
Players.PlayerAdded:Connect(saatPemainMasuk)
Players.PlayerRemoving:Connect(saatPemainKeluar)
-- Pemain yang SUDAH masuk sebelum script ini jalan tidak memicu
-- PlayerAdded. Jarang terjadi, tapi di Studio bisa saja. Untuk amannya,
-- proses juga pemain yang sudah ada (GetPlayers = array, pelajaran 5).
for _, player in ipairs(Players:GetPlayers()) do
saatPemainMasuk(player)
end
Script 2: koin
Langkah di Studio:
- Di Workspace, buat Folder bernama
Koin. - Buat beberapa Part di dalam folder itu, misalnya 5. Untuk setiap Part:
- Bentuk: di Properties, ubah Shape menjadi
Cylinder, atau pakai Part biasa yang kecil - Ukuran kecil, misalnya
2, 2, 2 - Centang Anchored
- Matikan CanCollide supaya pemain bisa menembus koin
- Nama Part bebas; script mengambil semua Part di folder
Koin
- Bentuk: di Properties, ubah Shape menjadi
- Tambahkan Script di ServerScriptService, lalu tempel kode di bawah.
- Tekan Play dan kumpulkan koinnya. Angka Koin di papan skor bertambah, dan koin muncul lagi setelah 5 detik.
Tips: setelah membuat 1 koin, pilih Part tersebut lalu tekan Ctrl+D untuk menduplikasinya, kemudian geser ke tempat lain.
local Players = game:GetService("Players")
local NILAI_KOIN = 1
local WAKTU_MUNCUL_LAGI = 5 -- detik
local folderKoin = workspace:WaitForChild("Koin")
-- Function ini mengatur SATU koin.
-- Nanti dipanggil untuk SETIAP koin di folder (loop di bawah).
local function aturKoin(koin)
koin.Color = Color3.fromRGB(255, 215, 0)
koin.Material = Enum.Material.Neon
koin.Anchored = true
koin.CanCollide = false
-- Setiap koin punya debounce SENDIRI. Variabel ini dibuat baru setiap
-- kali aturKoin dipanggil, jadi koin A dan koin B tidak saling ganggu.
local sudahDiambil = false
koin.Touched:Connect(function(bagian)
if sudahDiambil then
return
end
-- Dari bagian tubuh -> character -> PLAYER
local character = bagian.Parent
local player = Players:GetPlayerFromCharacter(character)
if not player then
return -- bukan pemain (misalnya NPC atau Part lain)
end
sudahDiambil = true
-- Tambah koin di leaderstats
local leaderstats = player:FindFirstChild("leaderstats")
if leaderstats then
leaderstats.Koin.Value += NILAI_KOIN
end
-- Sembunyikan koin
koin.Transparency = 1
task.wait(WAKTU_MUNCUL_LAGI)
-- Munculkan lagi
koin.Transparency = 0
sudahDiambil = false
end)
end
-- Atur semua koin yang ada di folder
for _, koin in ipairs(folderKoin:GetChildren()) do
if koin:IsA("BasePart") then -- pastikan memang Part, bukan objek lain
aturKoin(koin)
end
end
-- Bonus: koin yang ditambahkan ke folder SETELAH game berjalan
-- juga langsung bisa diambil.
folderKoin.ChildAdded:Connect(function(objek)
if objek:IsA("BasePart") then
aturKoin(objek)
end
end)
-- Animasi berputar untuk semua koin.
-- CFrame = posisi + rotasi. Kita putar sedikit demi sedikit di loop.
while true do
for _, koin in ipairs(folderKoin:GetChildren()) do
if koin:IsA("BasePart") then
koin.CFrame = koin.CFrame * CFrame.Angles(0, math.rad(3), 0)
end
end
task.wait() -- WAJIB! Tanpa ini Studio akan freeze (pelajaran 3)
end
Script 3: checkpoint
Langkah di Studio:
- Di Workspace, buat Folder bernama
Checkpoints. - Buat Part di dalamnya dan beri nama dengan angka:
1,2,3, dan seterusnya. Urutan angka ini adalah urutan checkpoint.- Bentuk pipih seperti lantai, misalnya
6, 1, 6 - Centang Anchored
- Susun berjauhan sebagai lintasan obby, dan letakkan lava dari pelajaran 07 di antaranya
- Bentuk pipih seperti lantai, misalnya
- Tambahkan Script di ServerScriptService, lalu tempel kode di bawah.
- Tekan Play. Injak checkpoint 1 lalu 2: warnanya berubah dan Stage di papan skor naik. Setelah itu jatuhlah ke lava. Kamu akan respawn di checkpoint terakhir dan angka Mati bertambah.
local Players = game:GetService("Players")
local WARNA_BELUM = Color3.fromRGB(150, 150, 150)
local WARNA_SUDAH = Color3.fromRGB(0, 200, 255)
local folderCheckpoint = workspace:WaitForChild("Checkpoints")
-- Kumpulkan checkpoint ke dalam table: checkpoints[1] = Part "1", dst.
-- tonumber("3") mengubah teks "3" menjadi angka 3.
local checkpoints = {}
for _, part in ipairs(folderCheckpoint:GetChildren()) do
local nomor = tonumber(part.Name)
if nomor and part:IsA("BasePart") then
checkpoints[nomor] = part
part.Anchored = true
part.Color = WARNA_BELUM
else
warn(`Checkpoint "{part.Name}" diabaikan: namanya harus angka`)
end
end
print(`{#checkpoints} checkpoint ditemukan`)
-- ------------------------------------------------------------
-- BAGIAN A: Menyentuh checkpoint -> Stage naik
-- ------------------------------------------------------------
local function aturCheckpoint(nomor, part)
part.Touched:Connect(function(bagian)
local player = Players:GetPlayerFromCharacter(bagian.Parent)
if not player then
return
end
local leaderstats = player:FindFirstChild("leaderstats")
if not leaderstats then
return -- leaderstats belum dibuat oleh script 1
end
local stage = leaderstats.Stage
-- Hanya boleh naik SATU tingkat. Pemain di stage 2 yang menyentuh
-- checkpoint 5 (misalnya lewat jalan pintas) tidak dihitung.
-- Menyentuh checkpoint lama juga tidak menurunkan stage.
if nomor == stage.Value + 1 then
stage.Value = nomor
print(`{player.Name} mencapai checkpoint {nomor}!`)
-- Efek kecil. Catatan: perubahan warna di server terlihat oleh
-- SEMUA pemain. Efek khusus per pemain butuh LocalScript
-- (dibahas di Pelajaran 9).
part.Color = WARNA_SUDAH
end
end)
end
-- pairs (bukan ipairs) karena kita butuh nomor DAN part-nya,
-- dan aman kalau ada nomor yang terlewat.
for nomor, part in pairs(checkpoints) do
aturCheckpoint(nomor, part)
end
-- ------------------------------------------------------------
-- BAGIAN B: Respawn di checkpoint terakhir
-- ------------------------------------------------------------
local function saatCharacterMuncul(player, character)
local stage = player:WaitForChild("leaderstats"):WaitForChild("Stage")
local checkpoint = checkpoints[stage.Value]
-- Stage 0 = belum menyentuh checkpoint apa pun -> pakai SpawnLocation biasa
if not checkpoint then
return
end
-- Tunggu bagian tubuh utama siap, lalu pindahkan character
character:WaitForChild("HumanoidRootPart")
task.wait() -- beri waktu 1 frame supaya Roblox selesai memasang character
-- PivotTo memindahkan SELURUH model sekaligus.
-- + Vector3 menaikkan posisi 3 stud supaya tidak tertanam di lantai.
character:PivotTo(checkpoint.CFrame + Vector3.new(0, 3, 0))
end
Players.PlayerAdded:Connect(function(player)
player.CharacterAdded:Connect(function(character)
saatCharacterMuncul(player, character)
end)
end)
Menguji dengan beberapa pemain
Supaya yakin skor setiap pemain terpisah, buka tab Test, atur Clients ke 2, lalu klik Start. Studio akan membuka 2 jendela pemain sekaligus.
Error yang sering muncul
| Masalah | Penyebab |
|---|---|
| Papan skor tidak muncul | Nama folder tidak persis leaderstats (huruf kecil semua) |
Infinite yield possible on 'Budi:WaitForChild("leaderstats")' |
Script 1 belum ada, atau ada error di script 1. Periksa Output. |
Koin is not a valid member of Workspace |
Folder Koin atau Checkpoints belum dibuat, atau namanya salah ketik |
| Koin bertambah puluhan sekaligus | Debounce-nya hilang atau rusak (lihat pelajaran 07, script 3) |
| Respawn tidak di checkpoint | Nama Part checkpoint bukan angka, misalnya "Checkpoint1", bukan "1" |
Latihan
- Stat baru: tambahkan
IntValuebernamaLeveldi leaderstats. Setiap kali Stage mencapai kelipatan 5, Level bertambah 1. - Koin emas: buat folder
KoinEmasyang bernilai 10. Ubah script 2 supaya nilai koin diambil dari Attribute Part. Caranya: di Properties, gulir ke bawah ke bagian Attributes, klik +, lalu buat atributNilaibertipe Number. Di script, ambil nilainya denganpart:GetAttribute("Nilai"). - Pesan selamat datang: saat pemain masuk,
printpesan berbeda untuk pemain pertama di server dibanding pemain berikutnya. Petunjuk:#Players:GetPlayers(). - Hukuman mati: setiap kali pemain mati, kurangi Koin sebanyak 5, tetapi jangan sampai di bawah 0.
Kunci jawaban
Coba kerjakan sendiri dulu sebelum membaca bagian ini.
Latihan 1: stat Level dari Stage
Di script 1, di dalam saatPemainMasuk, setelah membuat mati:
local level = Instance.new("IntValue")
level.Name = "Level"
level.Value = 1
level.Parent = leaderstats
-- Setiap kali Stage berubah, hitung ulang Level
stage.Changed:Connect(function(nilaiStage)
level.Value = 1 + math.floor(nilaiStage / 5)
end)
| Stage | Perhitungan | Level |
|---|---|---|
| 0–4 | 1 + 0 | 1 |
| 5–9 | 1 + 1 | 2 |
| 10–14 | 1 + 2 | 3 |
Kenapa memakai rumus, bukan if stage % 5 == 0 then level += 1? Cara += 1 akan rusak kalau Stage melompat, misalnya karena Skip Stage di pelajaran 11 atau data dimuat dari DataStore. Rumus selalu menghasilkan nilai yang benar, apa pun yang terjadi sebelumnya.
Latihan 2: koin emas dengan Attribute
Ubah script 2 supaya bisa mengurus beberapa folder dan membaca nilai dari Attribute:
local Players = game:GetService("Players")
local NILAI_KOIN = 1
local WAKTU_MUNCUL_LAGI = 5
-- Daftar folder koin yang diurus script ini
local DAFTAR_FOLDER = {"Koin", "KoinEmas"}
local function aturKoin(koin)
-- Nilai dari Attribute "Nilai". Kalau tidak ada, pakai nilai bawaan.
local nilai = koin:GetAttribute("Nilai") or NILAI_KOIN
local sudahDiambil = false
koin.Touched:Connect(function(bagian)
if sudahDiambil then
return
end
local player = Players:GetPlayerFromCharacter(bagian.Parent)
if not player then
return
end
sudahDiambil = true
local leaderstats = player:FindFirstChild("leaderstats")
if leaderstats then
leaderstats.Koin.Value += nilai
end
koin.Transparency = 1
task.wait(WAKTU_MUNCUL_LAGI)
koin.Transparency = 0
sudahDiambil = false
end)
end
local semuaKoin = {} -- dipakai juga oleh loop animasi di bawah
for _, namaFolder in ipairs(DAFTAR_FOLDER) do
local folder = workspace:WaitForChild(namaFolder)
for _, koin in ipairs(folder:GetChildren()) do
if koin:IsA("BasePart") then
aturKoin(koin)
table.insert(semuaKoin, koin)
end
end
end
while true do
for _, koin in ipairs(semuaKoin) do
koin.CFrame = koin.CFrame * CFrame.Angles(0, math.rad(3), 0)
end
task.wait()
end
Di Studio: pilih setiap Part di folder KoinEmas, lalu di Properties → Attributes → + buat atribut Nilai (Number) bernilai 10. Supaya mudah dibedakan, beri tampilan berbeda juga, misalnya lebih besar atau berkilau.
Keuntungan Attribute: desainer peta bisa mengatur nilai setiap koin tanpa menyentuh kode. Koin bernilai 50 di tempat rahasia? Cukup ubah atributnya.
Latihan 3: pesan pemain pertama
Di saatPemainMasuk:
local jumlahPemain = #Players:GetPlayers()
if jumlahPemain == 1 then
print(`{player.Name} adalah pemain pertama di server ini! 🎉`)
else
print(`{player.Name} bergabung. Sekarang ada {jumlahPemain} pemain.`)
end
Saat PlayerAdded terpicu, pemain baru sudah dihitung di GetPlayers(). Jadi pemain pertama menghasilkan angka 1, bukan 0.
Latihan 4: hukuman mati
Di bagian humanoid.Died:Connect(...):
humanoid.Died:Connect(function()
mati.Value += 1
koin.Value = math.max(koin.Value - 5, 0)
end)
math.max(x, 0) memastikan hasilnya tidak pernah negatif. Pemain dengan 3 koin menjadi 0, bukan -2.
Pertimbangan desain: hukuman yang terlalu berat membuat pemain frustrasi dan keluar. Di obby yang sulit, pemain bisa mati ratusan kali. Bayangkan apakah hukuman ini masih adil untuk pemain yang mati 50 kali di stage yang sama.
Diskusi (0)
Kami menyimpan nama (opsional), komentar, dan hash teknis (bukan alamat IP asli) untuk mencegah spam. Cookie fungsional dipakai agar Anda dapat mengedit atau menghapus komentar sendiri dari browser ini. Untuk permintaan penghapusan, gunakan halaman kontak.
Belum ada komentar. Mulai diskusinya.