Roblox
Belajar Luau 11: Monetisasi dengan Game Pass dan Developer Products
Menghasilkan Robux dari game Roblox: perbedaan Game Pass dan Developer Product, alur pembelian MarketplaceService, ProcessReceipt yang aman, pembagian hasil, dan tips agar pemain mau membeli.

Inilah tujuan akhir dari rencana belajarmu: game yang menghasilkan Robux. Ada dua cara utama menjual sesuatu di dalam game:
| Game Pass | Developer Product | |
|---|---|---|
| Dibeli | Sekali saja, dimiliki selamanya | Berkali-kali |
| Contoh | VIP, koin 2x, sepatu cepat permanen | 100 koin, skip stage, nyawa tambahan |
| Cara mengecek | UserOwnsGamePassAsync |
ProcessReceipt (callback setiap pembelian) |
| Tingkat kesulitan | Mudah | Harus hati-hati. Kalau salah, pemain bisa kehilangan barang yang sudah dibayar. |
| File | Jenis | Diletakkan di |
|---|---|---|
KonfigProduk.luau |
ModuleScript bernama KonfigProduk |
ReplicatedStorage |
1-gamepass-vip.server.luau |
Script | ServerScriptService |
2-developer-product.server.luau |
Script | ServerScriptService |
3-toko-robux.client.luau |
LocalScript | StarterPlayer → StarterPlayerScripts |
Butuh: Pelajaran 8 (koin dan checkpoint) dan Pelajaran 10 (DataStore). Game juga harus sudah dipublikasikan.
Konsep 1: Alur pembelian
1. Pemain klik tombol "Beli VIP" (CLIENT)
2. Client memanggil PromptGamePassPurchase → jendela beli resmi Roblox muncul
3. Pemain klik "Buy" dan Robux terpotong (diurus Roblox, bukan kita)
4. Roblox memberi tahu SERVER kita (event / ProcessReceipt)
5. SERVER memberikan barangnya ← tugas kita
Client hanya boleh menampilkan jendela beli. Yang memberikan barang selalu server, setelah Roblox mengonfirmasi pembayarannya.
Konsep 2: ProcessReceipt untuk Developer Products
Setiap kali Developer Product dibeli, Roblox memanggil function ProcessReceipt milikmu. Function itu wajib me-return salah satu dari:
| Return | Artinya |
|---|---|
Enum.ProductPurchaseDecision.PurchaseGranted |
"Barang sudah diberikan, transaksi selesai." |
Enum.ProductPurchaseDecision.NotProcessedYet |
"Belum bisa, coba lagi nanti." Roblox akan memanggil lagi, misalnya saat pemain masuk berikutnya. |
Aturan pentingnya:
- Return
PurchaseGrantedhanya setelah barang benar-benar diberikan. - Pemanggilan bisa terulang untuk pembelian yang sama, jadi catat
PurchaseIdsupaya barang tidak diberikan dua kali. - Hanya boleh ada SATU script yang mengisi
MarketplaceService.ProcessReceiptdi seluruh game.
Konsep 3: Pembagian hasil
- Roblox memotong sekitar 30% dari setiap penjualan di dalam game. Kamu menerima sekitar 70%.
- Robux hasil penjualan masuk sebagai "pending" selama beberapa hari sebelum bisa dipakai.
- Pemain Roblox Premium yang bermain di gamemu memberi Premium Payouts otomatis berdasarkan lama mereka bermain, tanpa perlu kode apa pun.
Langkah 1: Buat Game Pass dan Produk di Creator Dashboard
- Buka create.roblox.com → Creations, lalu pilih game-mu.
- Di menu kiri, buka Monetization → Passes, lalu klik Create a Pass.
- Nama:
VIP, deskripsi: "Koin 2x dan lari lebih cepat!", lalu unggah gambar ikon. - Setelah dibuat, buka pass tersebut, aktifkan Item for Sale, dan isi harga (misalnya 99 Robux).
- Salin ID-nya, yaitu angka panjang yang ada di URL atau di menu titik tiga → Copy Asset ID.
- Nama:
- Buka Monetization → Developer Products, lalu buat 3 produk:
100 Koin, misalnya 25 Robux500 Koin, misalnya 99 RobuxSkip Stage, misalnya 15 Robux- Salin ID masing-masing.
- Buka
KonfigProduk.luaudan ganti semua angka0dengan ID yang sudah disalin.
Langkah 2: Pasang script
- Buat ModuleScript bernama
KonfigProdukdi ReplicatedStorage, lalu tempel isinya. - Pasang ketiga script lain sesuai tabel di atas.
- Ubah script koin dari Pelajaran 8 (
2-koin.server.luau) supaya VIP mendapat koin 2x. Cari baris ini:
lalu ganti menjadi:leaderstats.Koin.Value += NILAI_KOINlocal pengali = if player:GetAttribute("VIP") then 2 else 1 leaderstats.Koin.Value += NILAI_KOIN * pengali
Kode
KonfigProduk.luau
- Jenis: ModuleScript, beri nama PERSIS "KonfigProduk"
- Letakkan di: ReplicatedStorage
return {
GamePass = {
VIP = 0, -- <- ganti dengan ID Game Pass VIP
},
-- Urutan array = urutan tombol di layar
Produk = {
{id = 0, kode = "Koin100", nama = "🪙 100 Koin"}, -- <- ganti ID
{id = 0, kode = "Koin500", nama = "💰 500 Koin"}, -- <- ganti ID
{id = 0, kode = "SkipStage", nama = "⏭️ Skip Stage"}, -- <- ganti ID
},
}
1-gamepass-vip.server.luau
- Jenis: Script
- Letakkan di: ServerScriptService
- Butuh: ModuleScript "KonfigProduk" di ReplicatedStorage
local Players = game:GetService("Players")
local MarketplaceService = game:GetService("MarketplaceService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local KonfigProduk = require(ReplicatedStorage:WaitForChild("KonfigProduk"))
local ID_VIP = KonfigProduk.GamePass.VIP
local KECEPATAN_VIP = 24 -- normalnya 16
if ID_VIP == 0 then
warn("ID Game Pass VIP belum diisi di KonfigProduk!")
end
-- Cek apakah pemain memiliki Game Pass.
-- Ini permintaan ke server Roblox lewat internet, jadi bisa gagal -> pcall.
local function punyaGamePass(player, idPass)
if idPass == 0 then
return false
end
local berhasil, hasil = pcall(function()
return MarketplaceService:UserOwnsGamePassAsync(player.UserId, idPass)
end)
if not berhasil then
warn(`Gagal mengecek Game Pass untuk {player.Name}: {hasil}`)
return false
end
return hasil
end
-- Terapkan efek VIP ke character (dipanggil setiap respawn)
local function terapkanKeCharacter(player, character)
if not player:GetAttribute("VIP") then
return
end
local humanoid = character:WaitForChild("Humanoid")
humanoid.WalkSpeed = KECEPATAN_VIP
end
local function jadikanVIP(player)
-- Attribute = data kecil yang ditempel ke Instance. Bisa dibaca script
-- lain (server MAUPUN client) dengan player:GetAttribute("VIP").
player:SetAttribute("VIP", true)
print(`⭐ {player.Name} sekarang VIP!`)
if player.Character then
terapkanKeCharacter(player, player.Character)
end
end
-- ------------------------------------------------------------
-- Pemain masuk: cek kepemilikan
-- ------------------------------------------------------------
local function saatPemainMasuk(player)
player.CharacterAdded:Connect(function(character)
terapkanKeCharacter(player, character)
end)
if punyaGamePass(player, ID_VIP) then
jadikanVIP(player)
end
end
Players.PlayerAdded:Connect(saatPemainMasuk)
for _, player in ipairs(Players:GetPlayers()) do
task.spawn(saatPemainMasuk, player)
end
-- ------------------------------------------------------------
-- Pemain BARU SAJA membeli di dalam game -> langsung berikan,
-- tidak perlu menunggu pemain keluar lalu masuk lagi.
-- ------------------------------------------------------------
MarketplaceService.PromptGamePassPurchaseFinished:Connect(function(player, idPass, dibeli)
if dibeli and idPass == ID_VIP then
jadikanVIP(player)
end
end)
2-developer-product.server.luau
- Jenis: Script
- Letakkan di: ServerScriptService
- Butuh: ModuleScript "KonfigProduk" di ReplicatedStorage
- ⚠️ HANYA BOLEH ADA SATU script yang mengisi
local Players = game:GetService("Players")
local MarketplaceService = game:GetService("MarketplaceService")
local DataStoreService = game:GetService("DataStoreService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local KonfigProduk = require(ReplicatedStorage:WaitForChild("KonfigProduk"))
-- DataStore khusus untuk mencatat pembelian yang SUDAH diberikan,
-- supaya barang tidak diberikan dua kali kalau Roblox mengulang panggilan.
local riwayatPembelian = DataStoreService:GetDataStore("RiwayatPembelian_v1")
-- ------------------------------------------------------------
-- PENANGAN setiap produk: [kode] = function(player)
-- Function ini HARUS memberikan barangnya, atau error kalau gagal.
-- ------------------------------------------------------------
local function ambilLeaderstats(player)
local leaderstats = player:FindFirstChild("leaderstats")
if not leaderstats then
error("leaderstats belum siap") -- error -> pembelian dicoba lagi nanti
end
return leaderstats
end
local PENANGAN_PRODUK = {
Koin100 = function(player)
ambilLeaderstats(player).Koin.Value += 100
end,
Koin500 = function(player)
ambilLeaderstats(player).Koin.Value += 500
end,
SkipStage = function(player)
local leaderstats = ambilLeaderstats(player)
local folderCheckpoint = workspace:FindFirstChild("Checkpoints")
local jumlahCheckpoint = folderCheckpoint and #folderCheckpoint:GetChildren() or 0
if leaderstats.Stage.Value < jumlahCheckpoint then
leaderstats.Stage.Value += 1
-- Respawn -> script checkpoint (Pelajaran 8) memindahkan
-- pemain ke checkpoint yang baru.
player:LoadCharacter()
else
-- Sudah di stage terakhir. Pemain sudah membayar, jadi beri
-- kompensasi daripada tidak memberi apa-apa.
leaderstats.Koin.Value += 100
print(`{player.Name} sudah di stage terakhir, diberi 100 koin`)
end
end,
}
-- Ubah array Produk menjadi dictionary: [idProduk] = kode
local kodeDariId = {}
for _, produk in ipairs(KonfigProduk.Produk) do
if produk.id == 0 then
warn(`ID produk "{produk.kode}" belum diisi di KonfigProduk!`)
else
kodeDariId[produk.id] = produk.kode
end
end
-- ------------------------------------------------------------
-- PROCESS RECEIPT
-- info berisi: PlayerId, ProductId, PurchaseId, CurrencySpent, ...
-- ------------------------------------------------------------
local BELUM = Enum.ProductPurchaseDecision.NotProcessedYet
local SELESAI = Enum.ProductPurchaseDecision.PurchaseGranted
local function prosesPembelian(info)
-- Kunci unik untuk pembelian ini
local kunci = `{info.PlayerId}_{info.PurchaseId}`
-- 1. Sudah pernah diberikan? (Roblox bisa mengulang panggilan)
local berhasilBaca, sudahDiberikan = pcall(function()
return riwayatPembelian:GetAsync(kunci)
end)
if not berhasilBaca then
return BELUM -- tidak bisa memastikan -> coba lagi nanti
end
if sudahDiberikan then
return SELESAI
end
-- 2. Pemain masih ada di server ini?
local player = Players:GetPlayerByUserId(info.PlayerId)
if not player then
return BELUM -- diproses lagi saat pemain masuk berikutnya
end
-- 3. Produknya kita kenal?
local kode = kodeDariId[info.ProductId]
local penangan = kode and PENANGAN_PRODUK[kode]
if not penangan then
warn(`Produk tidak dikenal: {info.ProductId}`)
return BELUM
end
-- 4. Berikan barangnya
local berhasilBeri, pesanError = pcall(penangan, player)
if not berhasilBeri then
warn(`Gagal memberi {kode} ke {player.Name}: {pesanError}`)
return BELUM
end
-- 5. Catat bahwa pembelian ini SUDAH diberikan.
-- Kalau pencatatan gagal, barang tetap SUDAH diberikan, jadi
-- tetap return SELESAI supaya tidak diberikan dua kali.
local berhasilCatat, errCatat = pcall(function()
riwayatPembelian:SetAsync(kunci, true)
end)
if not berhasilCatat then
warn(`Gagal mencatat pembelian {kunci}: {errCatat}`)
end
-- Catatan: koin hasil pembelian baru TERSIMPAN permanen saat data
-- pemain disimpan (keluar / autosave, Pelajaran 10). Kalau server crash
-- sebelum itu, koinnya bisa hilang. Modul seperti ProfileStore
-- menangani ini dengan lebih aman untuk game yang sudah besar.
print(`💎 {player.Name} membeli {kode} ({info.CurrencySpent} Robux)`)
return SELESAI
end
MarketplaceService.ProcessReceipt = prosesPembelian
3-toko-robux.client.luau
- Jenis: LocalScript
- Letakkan di: StarterPlayer -> StarterPlayerScripts
- Butuh: ModuleScript "KonfigProduk" di ReplicatedStorage
local Players = game:GetService("Players")
local MarketplaceService = game:GetService("MarketplaceService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local player = Players.LocalPlayer
local playerGui = player:WaitForChild("PlayerGui")
local KonfigProduk = require(ReplicatedStorage:WaitForChild("KonfigProduk"))
-- ------------------------------------------------------------
-- Panel di kiri tengah
-- ------------------------------------------------------------
local screenGui = Instance.new("ScreenGui")
screenGui.Name = "UITokoRobux"
screenGui.ResetOnSpawn = false
local panel = Instance.new("Frame")
panel.Name = "Panel"
panel.AnchorPoint = Vector2.new(0, 0.5)
panel.Position = UDim2.fromScale(0.02, 0.5)
panel.Size = UDim2.fromScale(0.16, 0.36)
panel.BackgroundColor3 = Color3.fromRGB(40, 25, 60)
panel.BackgroundTransparency = 0.15
panel.Parent = screenGui
Instance.new("UICorner").Parent = panel
local susunan = Instance.new("UIListLayout")
susunan.Padding = UDim.new(0.03, 0)
susunan.SortOrder = Enum.SortOrder.LayoutOrder
susunan.HorizontalAlignment = Enum.HorizontalAlignment.Center
susunan.VerticalAlignment = Enum.VerticalAlignment.Center
susunan.Parent = panel
local function buatTombol(urutan, teks, warna, saatDiklik)
local tombol = Instance.new("TextButton")
tombol.LayoutOrder = urutan
tombol.Size = UDim2.fromScale(0.9, 0.18)
tombol.BackgroundColor3 = warna
tombol.Font = Enum.Font.GothamBold
tombol.TextScaled = true
tombol.TextColor3 = Color3.new(1, 1, 1)
tombol.Text = teks
tombol.Parent = panel
Instance.new("UICorner").Parent = tombol
tombol.Activated:Connect(saatDiklik)
return tombol
end
-- ------------------------------------------------------------
-- Tombol VIP (Game Pass)
-- ------------------------------------------------------------
local tombolVIP = buatTombol(0, "⭐ Beli VIP", Color3.fromRGB(230, 170, 0), function()
local idVIP = KonfigProduk.GamePass.VIP
if idVIP == 0 then
warn("ID Game Pass VIP belum diisi")
return
end
-- Hanya MENAMPILKAN jendela beli resmi Roblox.
-- Barangnya diberikan oleh SERVER (1-gamepass-vip.server.luau).
MarketplaceService:PromptGamePassPurchase(player, idVIP)
end)
-- Sembunyikan tombol VIP kalau pemain sudah VIP.
-- GetAttributeChangedSignal terpicu setiap kali attribute itu berubah.
local function perbaruiTombolVIP()
tombolVIP.Visible = not player:GetAttribute("VIP")
end
perbaruiTombolVIP()
player:GetAttributeChangedSignal("VIP"):Connect(perbaruiTombolVIP)
-- ------------------------------------------------------------
-- Tombol Developer Products
-- ------------------------------------------------------------
for urutan, produk in ipairs(KonfigProduk.Produk) do
buatTombol(urutan, produk.nama, Color3.fromRGB(0, 150, 110), function()
if produk.id == 0 then
warn(`ID produk {produk.kode} belum diisi`)
return
end
-- Barangnya diberikan oleh SERVER lewat ProcessReceipt
MarketplaceService:PromptProductPurchase(player, produk.id)
end)
end
screenGui.Parent = playerGui
Langkah 3: Uji coba
Di Studio, pembelian hanya simulasi: jendela beli muncul, tetapi Robux-mu tidak terpotong. Jadi kamu bisa menguji sepuasnya.
- Tekan Play. Panel 💎 muncul di kiri tengah.
- Klik Beli VIP, lalu Buy. Di Output akan muncul
⭐ ... sekarang VIP!. Larimu jadi lebih cepat dan koin bernilai 2x. - Klik 100 Koin, lalu Buy. Papan skor bertambah 100.
- Klik Skip Stage. Kamu respawn di checkpoint berikutnya.
Catatan: di Studio, VIP tidak tersimpan setelah Stop karena pembeliannya hanya simulasi. Di game sungguhan,
UserOwnsGamePassAsyncakan mengingatnya selamanya.
Tips agar orang mau membeli
- Game-nya harus seru dulu. Monetisasi tidak bisa menyelamatkan game yang membosankan.
- Jangan pay-to-win berlebihan. Pemain gratis yang merasa dicurangi akan pergi, padahal mereka yang membuat server ramai.
- Tawarkan di saat yang tepat. Contohnya Skip Stage yang muncul setelah pemain gagal 5 kali di stage yang sama, bukan pop-up begitu pemain baru masuk.
- Harga bertingkat: sediakan pilihan murah (10–25 Robux) supaya orang mau mencoba membeli pertama kali.
- Pantau Analytics di Creator Dashboard: berapa persen pemain yang membeli, dan produk mana yang paling laku.
Error yang sering muncul
| Masalah | Penyebab |
|---|---|
ID ... belum diisi di Output |
ID di KonfigProduk masih 0 |
| Jendela beli muncul lalu error "item not for sale" | Pass belum diaktifkan Item for Sale, atau ID salah |
| Membeli produk tapi koin tidak bertambah | Ada lebih dari satu script yang mengisi ProcessReceipt, atau DataStore belum diaktifkan (Pelajaran 10) |
| VIP tidak terdeteksi | ID Game Pass di KonfigProduk keliru, misalnya tertukar dengan ID produk |
Latihan
- Produk baru: buat Developer Product
Sembuh Penuhyang memulihkan darah. Tambahkan ID-nya diKonfigProdukdan tulis handler-nya diPENANGAN_PRODUK. - Label VIP: tampilkan tulisan ⭐ VIP di atas kepala pemain VIP. Petunjuk:
BillboardGuiberisiTextLabelyang diletakkan dicharacter.Head. - Skip Stage yang pintar: sembunyikan tombol Skip Stage kalau pemain sudah di stage terakhir. Lalu buat versi lanjutannya: tombol baru muncul setelah pemain mati 3 kali di stage yang sama.
- Game Pass kedua: buat Game Pass
Sepatu Cepat Permanen. Pemiliknya otomatis mendapat WalkSpeed 28 setiap kali respawn.
Kunci jawaban
Coba kerjakan sendiri dulu sebelum membaca bagian ini.
Latihan 1: Developer Product "Sembuh Penuh"
Di KonfigProduk.luau, tambahkan ke array Produk:
local produkBaru = {id = 0, kode = "SembuhPenuh", nama = "❤️ Sembuh Penuh"} -- ganti 0 dengan ID asli
Di file aslinya, tulis bagian {id = ..., kode = ..., nama = ...}, di dalam Produk = { ... }.
Di 2-developer-product.server.luau, tambahkan ke PENANGAN_PRODUK:
local PENANGAN_PRODUK = {
-- ... produk lain ...
SembuhPenuh = function(player)
local character = player.Character
local humanoid = character and character:FindFirstChildOfClass("Humanoid")
if not humanoid or humanoid.Health <= 0 then
-- error -> ProcessReceipt mengembalikan NotProcessedYet,
-- jadi Roblox mencoba lagi nanti. Robux pemain TIDAK hilang sia-sia.
error("pemain sedang tidak hidup")
end
humanoid.Health = humanoid.MaxHealth
end,
}
Pertimbangan: pemain yang membeli tepat saat mati akan mendapatkan efeknya nanti (saat Roblox mencoba lagi), bukan langsung. Alternatifnya, sembunyikan tombol "Sembuh Penuh" di client saat pemain mati.
Latihan 2: Label ⭐ VIP di atas kepala
Di 1-gamepass-vip.server.luau, ganti function terapkanKeCharacter:
local function terapkanKeCharacter(player, character)
if not player:GetAttribute("VIP") then
return
end
local humanoid = character:WaitForChild("Humanoid")
humanoid.WalkSpeed = KECEPATAN_VIP
local kepala = character:WaitForChild("Head")
if kepala:FindFirstChild("LabelVIP") then
return -- jangan dibuat dua kali
end
local billboard = Instance.new("BillboardGui")
billboard.Name = "LabelVIP"
billboard.Size = UDim2.fromOffset(100, 30)
billboard.StudsOffset = Vector3.new(0, 2.5, 0) -- di atas kepala
billboard.AlwaysOnTop = false -- tertutup tembok, lebih natural
billboard.MaxDistance = 60 -- tidak terlihat dari jauh
local label = Instance.new("TextLabel")
label.Size = UDim2.fromScale(1, 1)
label.BackgroundTransparency = 1
label.Font = Enum.Font.GothamBlack
label.TextScaled = true
label.TextColor3 = Color3.fromRGB(255, 215, 0)
label.TextStrokeTransparency = 0
label.Text = "⭐ VIP"
label.Parent = billboard
billboard.Parent = kepala
end
Karena dibuat di server, semua pemain melihat label itu. Ini juga iklan gratis: pemain lain melihat ada VIP, lalu penasaran.
Latihan 3: Skip Stage yang pintar
Di 3-toko-robux.client.luau, simpan tombol berdasarkan kodenya. Ubah loop pembuatan tombol produk:
local tombolProduk = {} -- [kode] = tombol
for urutan, produk in ipairs(KonfigProduk.Produk) do
tombolProduk[produk.kode] = buatTombol(urutan, produk.nama, Color3.fromRGB(0, 150, 110), function()
if produk.id == 0 then
return
end
MarketplaceService:PromptProductPurchase(player, produk.id)
end)
end
Lalu tambahkan di bagian bawah script:
local leaderstats = player:WaitForChild("leaderstats")
local stage = leaderstats:WaitForChild("Stage")
local folderCheckpoint = workspace:WaitForChild("Checkpoints")
local tombolSkip = tombolProduk.SkipStage
local BATAS_MATI = 3
local matiDiStageIni = 0
local function perbaruiTombolSkip()
local jumlahCheckpoint = #folderCheckpoint:GetChildren()
local bukanStageTerakhir = stage.Value < jumlahCheckpoint
local sudahSeringGagal = matiDiStageIni >= BATAS_MATI
tombolSkip.Visible = bukanStageTerakhir and sudahSeringGagal
end
-- Stage naik: hitungan mati di-reset
stage.Changed:Connect(function()
matiDiStageIni = 0
perbaruiTombolSkip()
end)
-- Hitung kematian di stage yang sama
local function pantauCharacter(character)
local humanoid = character:WaitForChild("Humanoid")
humanoid.Died:Connect(function()
matiDiStageIni += 1
perbaruiTombolSkip()
end)
end
player.CharacterAdded:Connect(pantauCharacter)
if player.Character then
pantauCharacter(player.Character)
end
perbaruiTombolSkip()
Ini contoh "menawarkan di saat yang tepat" (tips di PANDUAN). Pemain yang frustrasi karena gagal 3 kali jauh lebih mungkin membeli Skip Stage daripada pemain yang baru masuk.
Catatan: kalau
StreamingEnabledaktif (Pelajaran 16), checkpoint yang jauh belum dimuat di client, sehingga#GetChildren()bisa lebih kecil dari jumlah sebenarnya. Untuk game dengan streaming, simpan jumlah checkpoint sebagai Attribute di folder (folderCheckpoint:GetAttribute("Jumlah")).
Latihan 4: Game Pass kedua
Ubah 1-gamepass-vip.server.luau supaya bisa mengurus banyak Game Pass dengan rapi.
Di KonfigProduk.luau:
local GamePass = {
VIP = 0,
SepatuPermanen = 0, -- ID Game Pass baru
}
Di file aslinya, cukup tambahkan baris SepatuPermanen = 0, di dalam GamePass = { ... }.
Script server, versi yang bisa menampung banyak pass:
local Players = game:GetService("Players")
local MarketplaceService = game:GetService("MarketplaceService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local KonfigProduk = require(ReplicatedStorage:WaitForChild("KonfigProduk"))
-- Setiap pass: nama attribute + efek ke character
local PASS = {
{
id = KonfigProduk.GamePass.VIP,
attribute = "VIP",
efek = function(humanoid)
humanoid.WalkSpeed = math.max(humanoid.WalkSpeed, 24)
end,
},
{
id = KonfigProduk.GamePass.SepatuPermanen,
attribute = "SepatuPermanen",
efek = function(humanoid)
humanoid.WalkSpeed = math.max(humanoid.WalkSpeed, 28)
end,
},
}
local function punyaPass(player, id)
if id == 0 then
return false
end
local berhasil, hasil = pcall(function()
return MarketplaceService:UserOwnsGamePassAsync(player.UserId, id)
end)
return berhasil and hasil
end
local function terapkanSemua(player, character)
local humanoid = character:WaitForChild("Humanoid")
for _, pass in ipairs(PASS) do
if player:GetAttribute(pass.attribute) then
pass.efek(humanoid)
end
end
end
local function berikanPass(player, pass)
player:SetAttribute(pass.attribute, true)
if player.Character then
terapkanSemua(player, player.Character)
end
end
Players.PlayerAdded:Connect(function(player)
player.CharacterAdded:Connect(function(character)
terapkanSemua(player, character)
end)
for _, pass in ipairs(PASS) do
if punyaPass(player, pass.id) then
berikanPass(player, pass)
end
end
end)
MarketplaceService.PromptGamePassPurchaseFinished:Connect(function(player, idPass, dibeli)
if not dibeli then
return
end
for _, pass in ipairs(PASS) do
if pass.id == idPass then
berikanPass(player, pass)
end
end
end)
Kenapa math.max(WalkSpeed, 28)? Pemain yang punya VIP (24) dan Sepatu Permanen (28) harus mendapat yang tercepat (28), bukan tergantung urutan efek mana yang dijalankan terakhir.
Pola yang dipelajari: daripada menyalin seluruh script untuk setiap Game Pass, buat table data berisi semua pass, lalu satu logika yang memprosesnya. Menambah pass ketiga cukup dengan menambah satu entri.
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.