Paket fitur Bundles menawarkan fungsionalitas siap pakai untuk menjual koleksi item kepada pemain dengan diskon. Anda dapat memilih apakah akan mengizinkan pemain membeli bundle menggunakan mata uang dalam game yang disesuaikan atau Robux, jenis bundle mana yang ingin Anda gunakan, set item apa yang ingin Anda jual, dan bagaimana Anda ingin meminta pemain selama permainan mereka.
Dengan opsi kustomisasi paket ini, Anda dapat menyesuaikan bundle Anda untuk memenuhi tujuan desain dan monetisasi permainan Anda, seperti:
- Menargetkan metrik tingkat konversi yang rendah dengan menawarkan paket starter diskon yang memberikan nilai kepada pemain baru dan mendorong pengeluaran awal.
- Meningkatkan kedalaman pengeluaran dengan menggabungkan item pada berbagai titik harga untuk menarik berbagai pemain.
- Memonetisasi operasi langsung (LiveOps) acara dengan menawarkan bundle item eksklusif dalam waktu terbatas.

Dapatkan paket
Creator Store adalah tab dari Toolbox yang dapat Anda gunakan untuk menemukan semua aset yang dibuat oleh Roblox dan komunitas Roblox untuk digunakan dalam proyek Anda, termasuk model, gambar, mesh, audio, plugin, video, dan aset font. Anda dapat menggunakan Creator Store untuk menambahkan satu atau lebih aset langsung ke dalam game yang terbuka, termasuk paket fitur!
Setiap paket fitur memerlukan paket fitur Core agar berfungsi dengan baik. Setelah aset paket fitur Core dan Bundles ada dalam inventaris Anda, Anda dapat menggunakannya kembali di proyek mana pun di platform.
Untuk mendapatkan paket dari inventaris Anda ke dalam game:
Tambahkan paket fitur Core dan Bundles ke inventaris Anda di dalam Studio dengan mengklik tautan Add to Inventory dalam set komponen berikut.
Dari menu Window Studio atau toolbar tab Home, buka Toolbox.
Di jendela Toolbox, klik tab Inventory. Penyortiran My Models ditampilkan.

Klik ubin Feature Package Core, lalu ubin Bundle Feature Package. Kedua folder paket ditampilkan di jendela Explorer.
Seret folder paket ke dalam ReplicatedStorage.
Izinkan panggilan penyimpanan data untuk melacak pembelian pemain dengan paket tersebut.
- Buka jendela File ⟩ Experience Settings di Studio.
- Navigasikan ke tab Security, lalu aktifkan Enable Studio Access to API Services.
Definisikan mata uang
Jika game Anda memiliki sistem mata uang sendiri, Anda dapat mendaftarkannya dengan paket fitur Core dengan mendefinisikannya di ReplicatedStorage.FeaturePackagesCore.Configs.Currencies. Sudah ada contoh mata uang Gems yang dikomentari dalam file ini; ganti dengan milik Anda sendiri.
Gems = {
displayName = "Gems",
symbol = "💎",
icon = nil,
},Skrip Currencies memberi tahu paket fitur Core beberapa metadata tentang mata uang Anda:
- (diperlukan) displayName - Nama mata uang Anda. Jika Anda tidak menentukan simbol atau ikon, nama ini digunakan dalam tombol pembelian (misalnya "100 Gems").
- (opsional) symbol - Jika Anda memiliki karakter teks untuk digunakan sebagai ikon untuk mata uang Anda, ini digunakan sebagai pengganti displayName dalam tombol pembelian (misalnya "💎100").
- (opsional) icon - Jika Anda memiliki ikon gambar AssetId untuk mata uang Anda, ini digunakan sebagai pengganti displayName dalam tombol pembelian (misalnya gambar akan ditempatkan di sebelah kiri harga "🖼️100")
Setelah mata uang Anda disiapkan, Anda perlu secara manual menentukan harga bundle, mata uang, dan ikon untuk tampilan heads up alih-alih informasi tersebut diambil dari produk pengembang yang terkait dengan bundle.
-- Jika Anda ingin menggunakan produk pengembang, Anda harus memberikan devProductId yang unik, hanya digunakan oleh satu bundle.
-- Kami akan mengambil harga dan ikon bundle dari produk pengembang
pricing = {
priceType = CurrencyTypes.PriceType.Marketplace,
devProductId = 1795621566,
},
-- Jika tidak, jika Anda ingin menggunakan mata uang dalam game alih-alih produk pengembang, Anda dapat menggunakan yang berikut:
-- Harga di sini adalah dalam mata uang dalam game, bukan Robux
pricing = {
priceType = CurrencyTypes.PriceType.InExperience,
price = 79,
currencyId = "Gems",
icon = 18712203759,
},Anda juga perlu merujuk skrip BundlesExample untuk memanggil setInExperiencePurchaseHandler.
local function awardInExperiencePurchase(
_player: Player,
_bundleId: Types.BundleId,
_currencyId: CurrencyTypes.CurrencyId,
_price: number
)
-- Periksa apakah pemain memiliki cukup mata uang untuk membeli bundle
-- Perbarui data pemain, berikan item, dll.
-- Kurangi mata uang dari pemain
task.wait(2)
return true
end
local function initializePurchaseHandlers()
local bundles = Bundles.getBundles()
for bundleId, bundle in bundles do
-- Bundle tidak terkait dengan produk pengembang jika tidak memiliki jenis harga pasar
if not bundle or bundle.pricing.priceType ~= "Marketplace" then
continue
end
Bundles.setPurchaseHandler(bundleId, awardMarketplacePurchase)
receiptHandlers[bundle.pricing.devProductId] = receiptHandler
end
-- Jika Anda memiliki mata uang dalam game yang Anda gunakan untuk bundle, atur handler di sini
for currencyId, _ in Currencies do
Bundles.setInExperiencePurchaseHandler(currencyId, awardInExperiencePurchase)
end
endSecara khusus, Anda perlu mengisi awardInExperiencePurchase, yang dipanggil oleh loop melalui Currencies di dalam contoh initializePurchaseHandlers (misalnya setiap currencyId terhubung ke handler melalui Bundles.setInExperiencePurchaseHandler(currencyId, awardInExperiencePurchase)).
Definisikan bundle
Semua bundle yang dapat ditawarkan dalam game Anda dapat didefinisikan dalam ReplicatedStorage.Bundles.Configs.Bundles, dengan tipe yang diekspor dari skrip Types di folder yang sama.
Jika Anda menggunakan devProductId, Anda perlu memperbarui devProductId utama bundle untuk mencocokkan yang ada di game Anda. Ini yang akan diminta melalui MarketplaceService untuk membeli bundle itu sendiri. Sangat disarankan untuk menggunakan produk pengembang baru untuk bundle agar lebih mudah melacak penjualan terpisah.
Jika Anda menginginkan bundle dengan beberapa item, dan jika item-item ini sudah diwakili oleh produk pengembang di game Anda, Anda tidak perlu secara eksplisit mengatur harga item/assetId/nama, yang akan diambil melalui informasi produk:
{
itemType = ItemTypes.ItemType.DevProduct,
devProductId = <DEV_PRODUCT_ID>,
metadata = {
caption = {
text = "x1",
color = Color3.fromRGB(236, 201, 74),
} -- Keterangan adalah opsional! Anda juga dapat menghilangkan bidang ini
}
},Jika tidak, Anda dapat mengonfigurasi detail item tersebut secara manual:
{
itemType = ItemTypes.ItemType.Robux,
priceInRobux = 49,
icon = <IMAGE_ASSET_ID>,
metadata = {
caption = {
text = "x1",
color = Color3.fromRGB(236, 201, 74),
} -- Keterangan adalah opsional! Anda juga dapat menghilangkan bidang ini
}
},Sebagai contoh, seluruh bundle Anda kemungkinan akan terlihat seperti ini:
local starterBundle: Types.RelativeTimeBundle = {
bundleType = Types.BundleType.RelativeTime,
-- Jika Anda ingin menggunakan produk pengembang, Anda harus memberikan devProductId yang unik, hanya digunakan oleh satu bundle.
-- Kami akan mengambil harga dan ikon bundle dari produk pengembang
pricing = {
priceType = CurrencyTypes.PriceType.Marketplace,
devProductId = <DEV_PRODUCT_ID>,
},
-- Jika tidak, jika Anda ingin menggunakan mata uang dalam game alih-alih produk pengembang, Anda dapat menggunakan yang berikut:
-- Harga di sini adalah dalam mata uang dalam game, bukan Robux
-- pricing = {
-- priceType = CurrencyTypes.PriceType.InExperience,
-- price = 79,
-- currencyId = <CURRENCY_ID>,
-- icon = <IMAGE_ASSET_ID>,
-- },
includedItems = {
[1] = {
-- Item itu sendiri tidak dijual melalui produk pengembang, jadi tunjukkan berapa banyak nilainya dalam Robux dan berikan ikon
-- priceInRobux membantu Bundles menunjukkan nilai relatif dari harga bundle vs. jumlah isinya
itemType = ItemTypes.ItemType.Robux,
priceInRobux = 49,
icon = <IMAGE_ASSET_ID>,
-- Sebagai alternatif, jika ini memiliki produk pengembang, hilangkan harga dan ikon di atas dan cukup atur devProductId
-- Harga dan ikon akan diambil dari produk pengembang
-- devProductId = <ITEM_DEV_PRODUCT_ID>
-- Ada lebih banyak bidang metadata opsional yang spesifik UI jika diperlukan
metadata = {
caption = {
text = "x1",
color = Color3.fromRGB(236, 201, 74),
},
},
},
[2] = {
itemType = ItemTypes.ItemType.Robux,
priceInRobux = 99,
icon = <IMAGE_ASSET_ID>,
metadata = {
caption = {
text = "x1",
color = Color3.fromRGB(236, 201, 74),
},
},
},
[3] = {
itemType = ItemTypes.ItemType.Robux,
priceInRobux = 149,
icon = <IMAGE_ASSET_ID>,
metadata = {
caption = {
text = "x1",
color = Color3.fromRGB(236, 201, 74),
},
},
},
},
singleUse = true, -- Setelah dibeli atau kedaluwarsa, tidak lagi valid meskipun game Anda mencoba untuk meminta (onPlayerAdded). Anda dapat membuat ini false saat menguji di studio.
durationInSeconds = 900, -- 15 menit
includesOfflineTime = false, -- Hanya menghitung waktu yang berlalu dalam game
metadata = {
displayName = "BUNDLE PEMULA",
description = "Hemat 75% dan dapatkan awal yang baik!",
},
}Integrasikan logika server
Lihat ReplicatedStorage.Bundles.Server.Examples.BundlesExample, yang menunjukkan bagaimana server Anda akan berinteraksi dengan paket fitur Bundles dan metode di atas pada ModuleScript. Potongan kode di bawah ini berasal dari skrip tersebut.
Anda terutama perlu menghubungkan empat hal setelah menyeret paket fitur Bundles ke dalam game Anda:
Hubungkan handler pembelian melalui Bundles.setPurchaseHandler untuk menentukan fungsi yang akan dipanggil untuk memberikan item saat pembelian sedang diproses.
BundlesExamplelocal function awardMarketplacePurchase(_player: Player, _bundleId: Types.BundleId, _receiptInfo: { [string]: any })-- Perbarui data pemain, berikan item, dll.-- ... DAN catat receiptInfo.PurchaseId sehingga kami dapat memeriksa apakah pengguna sudah memiliki bundle initask.wait(2)return Enum.ProductPurchaseDecision.PurchaseGrantedendlocal function awardInExperiencePurchase(_player: Player,_bundleId: Types.BundleId,_currencyId: CurrencyTypes.CurrencyId,_price: number)-- Periksa apakah pemain memiliki cukup mata uang untuk membeli bundle-- Perbarui data pemain, berikan item, dll.-- Kurangi mata uang dari pemaintask.wait(2)return trueendlocal function initializePurchaseHandlers()local bundles = Bundles.getBundles()for bundleId, bundle in bundles do-- Bundle tidak terkait dengan produk pengembang jika tidak memiliki jenis harga pasarif not bundle or bundle.pricing.priceType ~= "Marketplace" thencontinueendBundles.setPurchaseHandler(bundleId, awardMarketplacePurchase)receiptHandlers[bundle.pricing.devProductId] = receiptHandlerend-- Jika Anda memiliki mata uang dalam game yang Anda gunakan untuk bundle, atur handler di sinifor currencyId, _ in Currencies doBundles.setInExperiencePurchaseHandler(currencyId, awardInExperiencePurchase)endendHubungkan logika Anda untuk MarketplaceService.ProcessReceipt, tetapi ini mungkin dilakukan di tempat lain jika game Anda sudah memiliki produk pengembang untuk dijual. Pada dasarnya, ketika tanda terima produk pengembang sedang diproses, mereka sekarang akan memanggil Bundles.getBundleByDevProduct untuk memeriksa apakah produk tersebut termasuk dalam bundle. Jika iya, skrip kemudian memanggil Bundles.processReceipt.
BundlesExample-- Proses tanda terima dari pasar untuk menentukan apakah pemain perlu dikenakan biaya atau tidaklocal function processReceipt(receiptInfo): Enum.ProductPurchaseDecisionlocal userId, productId = receiptInfo.PlayerId, receiptInfo.ProductIdlocal player = Players:GetPlayerByUserId(userId)if not player thenreturn Enum.ProductPurchaseDecision.NotProcessedYetendlocal handler = receiptHandlers[productId] -- Dapatkan handler untuk produklocal success, result = pcall(handler, receiptInfo, player) -- Panggil handler untuk memeriksa apakah logika pembelian berhasilif not success or not result thenwarn("Gagal memproses tanda terima:", receiptInfo, result)return Enum.ProductPurchaseDecision.NotProcessedYetendreturn Enum.ProductPurchaseDecision.PurchaseGrantedendlocal function receiptHandler(receiptInfo: { [string]: any }, player: Player)local bundleId, _bundle = Bundles.getBundleByProductId(receiptInfo.ProductId)if bundleId then-- Pembelian ini termasuk dalam bundle, biarkan Bundles menanganinyalocal purchaseDecision = Bundles.processReceiptAsync(player, bundleId, receiptInfo)return purchaseDecision == Enum.ProductPurchaseDecision.PurchaseGrantedend-- Pembelian ini tidak termasuk dalam bundle,-- ... Tangani semua logika yang ada di sini jika Anda memilikireturn falseendHubungkan Players.PlayerAdded:Connect(Bundles.OnPlayerAdded) sehingga paket fitur Bundles meminta kembali bundle aktif yang belum kedaluwarsa untuk pemain.
READMElocal function onPlayerAdded(player: Player)-- Beri tahu Bundles saat pemain bergabung sehingga dapat memuat ulang data merekaBundles.onPlayerAdded(player)-- Jika Anda memiliki beberapa bundle starter yang ingin Anda tawarkan kepada semua pengguna baru, Anda dapat meminta itu di sini-- ... Bundles akan menangani jika pemain sudah membelinya atau jika sudah kedaluwarsa karena tidak dapat diulang-- Bundles.promptIfValidAsync(player, "StarterBundle")-- Memanggil ini di sini hanya sebagai contoh, Anda dapat memanggil ini kapan saja atau di mana saja yang Anda inginkanonPromptBundleXYZEvent(player)endMinta bundle. Sementara ini tergantung pada gameplay, contoh meminta pemain dengan StarterBundle onPlayerAdded.
Logika paket fitur Bundles memastikan setiap pemain tidak mendapatkan tawaran ulang jika mereka sudah membeli bundle, atau jika mereka membiarkan tawaran tersebut kedaluwarsa (berdasarkan konfigurasi bundle).
Kapan pun Anda ingin meminta bundle kepada pemain, panggil Bundles.promptIfValidAsync(player, bundleId).
READMElocal function onPromptBundleXYZEvent(player: Player)-- Hubungkan acara game apa pun yang ingin Anda gunakan untuk menentukan kapan pemain diminta bundle-- ... Ini akan terjadi kapan pun Anda telah memenuhi kriteria kelayakan Anda untuk meminta pemain bundle-- ... Misalnya, jika Anda ingin meminta bundle ketika pemain bergabung, atau ketika pemain naik leveltask.spawn(Bundles.promptIfValidAsync, player, <Some_Bundle_Id>)-- ... Jika membuat beberapa bundle, menggunakan task.spawn() untuk membungkus panggilan fungsi di atas akan meminimalkan ketidaksesuaian antara hitungan mundurend
Pertimbangkan panduan praktik terbaik berikut tentang pencatatan redundan ReceiptIds:
Sementara paket fitur Bundles mencatat ReceiptIds untuk menghindari pemrosesan tanda terima yang sama dua kali, Anda juga harus mencatat ReceiptIds di dalam tabel Anda sehingga jika alur pembelian gagal setelah handler pembelian mereka selesai, Anda tahu pada percobaan berikutnya untuk tidak memberikan item lagi.
Paket fitur Bundles tidak akan mencatat ReceiptId jika pembelian gagal pada langkah mana pun, jadi Anda harus memastikan bahwa Anda mencatat ReceiptId di dalam tabel Anda sebelum memproses tanda terima sebagai bagian dari purchaseHandler Anda.
Redundansi ini membantu memastikan bahwa semua logika pembelian telah ditangani dengan tepat dan bahwa penyimpanan data Anda dan penyimpanan data paket fitur Bundles mencapai konsistensi akhir, dengan penyimpanan data Anda menjadi sumber kebenaran.
Konfigurasi konstanta
Konstanta untuk paket fitur Core berada di dua tempat:
Konstanta bersama berada di ReplicatedStorage.FeaturePackagesCore.Configs.SharedConstants.
Konstanta spesifik paket, dalam hal ini paket fitur Bundles, berada di ReplicatedStorage.Bundles.Configs.Constants.
Hal utama yang mungkin ingin Anda sesuaikan untuk memenuhi persyaratan desain game Anda:
- ID aset suara
- Durasi efek pembelian dan warna partikel
- Kemampuan kolaps tampilan heads up
Selain itu, Anda dapat menemukan string untuk terjemahan yang dibagi menjadi satu lokasi: ReplicatedStorage.FeaturePackagesCore.Configs.TranslationStrings.
Kustomisasi komponen UI
Dengan memodifikasi objek paket, seperti warna, font, dan transparansi, Anda dapat menyesuaikan presentasi visual dari permintaan bundle Anda. Namun, ingatlah bahwa jika Anda memindahkan salah satu objek secara hierarkis, kode tidak akan dapat menemukannya, dan Anda perlu melakukan penyesuaian pada kode Anda.
Sebuah permintaan terdiri dari dua komponen tingkat tinggi:
- PromptItem – Komponen individu yang diulang untuk setiap item dalam bundle (gambar item, keterangan, nama, harga).
- Prompt – Jendela permintaan itu sendiri.
Tampilan heads up juga terdiri dari dua komponen:
- HudItem – Komponen individu yang mewakili setiap opsi menu dalam tampilan heads up.
- Hud – Diisi secara programatis dengan HudItems.
Jika Anda ingin memiliki kontrol lebih besar atas tampilan heads up, alih-alih hanya menggunakan UI HUD yang ada di dalam ReplicatedStorage.Bundles.Objects.BundlesGui, Anda dapat memindahkan hal-hal untuk memenuhi persyaratan desain Anda sendiri. Pastikan untuk memperbarui perilaku skrip klien dalam skrip ReplicatedStorage.Bundles.Client.UIController.
Referensi API
Tipe
RelativeTime
Setelah bundle RelativeTime ditawarkan kepada pemain, bundle tersebut tetap tersedia hingga durasi waktu habis. Tipe ini ditampilkan di tampilan heads up pemain, dan secara otomatis meminta di sesi mendatang hingga bundle kedaluwarsa atau pemain membelinya.
Contoh umum dari tipe bundle ini adalah tawaran paket starter sekali pakai yang ditampilkan kepada semua pemain baru selama 24 jam.
| Nama | Tipe | Deskripsi |
|---|---|---|
| includeOfflineTime | bool | (Opsional) Jika tidak diatur, hanya waktu yang dihabiskan dalam game yang akan dihitung menuju durasi tawaran yang tersisa. |
| singleUse | bool | (Opsional) Jika tidak diatur, pembelian dapat diaktifkan kembali setelah dibeli atau kedaluwarsa. Jika diatur, setelah dibeli atau kedaluwarsa untuk pertama kalinya, itu tidak akan dapat diminta lagi, bahkan jika Anda memanggil Bundles.promptIfValidAsync dengan bundleId. |
FixedTime
Setelah bundle FixedTime ditawarkan kepada pemain, bundle tersebut tetap tersedia hingga akhir waktu universal terkoordinasi (UTC) yang ditetapkan. Tipe ini ditampilkan di tampilan heads up pemain, dan secara otomatis meminta di sesi mendatang hingga bundle kedaluwarsa atau pemain membelinya.
Contoh umum dari tipe bundle ini adalah tawaran liburan yang hanya tersedia untuk bulan tertentu.
OneTime
Bundle OneTime hanya tersedia pada saat ditawarkan kepada pemain. Bundle ini tidak ditampilkan di tampilan heads up pemain, dan setelah pemain menutup permintaan, tidak dapat dibuka kembali hingga diminta oleh server lagi.
Contoh umum dari tipe bundle ini adalah tawaran untuk membeli lebih banyak mata uang dalam game pada saat pemain kehabisan.