Paket fitur Bundel menawarkan fungsi keluar dari kotak untuk menjual koleksi item kepada pemain dengan diskon.Anda dapat memilih apakah akan mengizinkan pemain untuk membeli bundel menggunakan mata uang khusus dalam pengalaman atau Robux, jenis bundel yang ingin Anda gunakan, barang apa yang ingin Anda jual, dan bagaimana Anda ingin meminta pemain selama gameplaymereka.
Menggunakan opsi kustomisasi paket, Anda dapat menyesuaikan bundel Anda untuk memenuhi tujuan desain dan monetarisasi pengalaman Anda, seperti:
- Menargetkan tingkat konversi rendah dengan menawarkan paket pemula diskon yang memberikan nilai kepada pemain baru dan mendorong pengeluaran awal.
- Meningkatkan biaya pengeluaran dengan menggabungkan item di berbagai titik harga untuk menarik berbagai pemain.
- Monetarisasi operasi langsung (LiveOps) peristiwa dengan menawarkan bundel waktu terbatas dari item eksklusif.

Dapatkan paket
Toko Pencipta 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 Toko Pencipta untuk menambahkan satu atau lebih aset langsung ke pengalaman terbuka, termasuk paket fitur!
Setiap paket fitur memerlukan paket fitur Inti untuk berfungsi dengan benar.Setelah Inti dan Bundel fitur paket aset sudah ada di inventaris Anda, Anda dapat menggunakannya kembali di proyek apa pun di platform.
Untuk mendapatkan paket dari inventaris Anda ke pengalaman Anda:
Tambahkan paket fitur Inti dan Bundel ke inventaris Anda dalam Studio dengan mengklik tautan Tambahkan ke Inventaris dalam set komponen berikut.
Di bilah alat, pilih tab Tampilkan.
Klik Toolbox . Jendela Toolbox ditampilkan.
Di jendela Toolbox , klik tab Inventaris . Tampilan urutan Model Saya ditampilkan.
Klik tombol Paket Fitur Inti , lalu tombol Paket Fitur Bundel .Kedua folder paket ditampilkan di jendela Explorer .
Seret folder paket ke dalam ReplicatedStorage .
Izinkan panggilan penyimpanan data untuk melacak pembelian pemain dengan paket.
- Di tab Rumah di bilah alat, pilih Pengaturan Permainan .
- Navigasikan ke tab Keamanan , lalu aktifkan Aktifkan Akses Studio ke Layanan API .
Definisikan mata uang
Jika pengalaman Anda memiliki sistem mata uang sendiri, Anda dapat mendaftarkan mereka dengan paket fitur Inti dengan menentukannya di ReplicatedStorage.FeaturePackagesCore.Configs.Currencies .Ada contoh komentar dari mata uang Permata yang sudah ada di file ini; gantikan dengan milik Anda sendiri.
Mata uang
Gems = {displayName = "Gems",symbol = "💎",icon = nil,},
Skrip Currencies memberitahu paket fitur Inti beberapa metadata tentang mata uang Anda:
- (diperlukan) displayName - Nama mata uang Anda. Jika Anda tidak menyebutkan simbol atau ikon, nama ini digunakan dalam tombol pembelian (yaitu "100 Permata").
- (opsional) symbol - Jika Anda memiliki karakter teks untuk digunakan sebagai ikon untuk mata uang Anda, ini digunakan sebagai gantinya displayName pada tombol pembelian (yaitu "💎100").
- (opsional) icon - Jika Anda memiliki ikon gambar AssetId untuk mata uang Anda, ini digunakan sebagai gantinya displayName pada tombol pembelian (yaitugambar akan ditempatkan di sebelah kiri harga "🖼️100")
Setelah mata uang Anda ditetapkan, Anda perlu secara manual menentukan harga bundel, mata uang, dan ikon untuk tampilan kepala atas alih-alih informasi tersebut diambil dari produk pengembang terkait bundel.
Kelompok
-- Jika Anda ingin menggunakan produk dev, Anda harus memberikan devProductId unik, hanya digunakan oleh satu bundel.-- Kami akan mengambil harga bundel dan ikon dari produk pengembangpricing = {priceType = CurrencyTypes.PriceType.Marketplace,devProductId = 1795621566,},-- Jika tidak, jika Anda ingin menggunakan mata uang dalam pengalaman alih-alih produk dev, Anda dapat menggunakan yang berikut:-- Harga di sini adalah dalam mata uang pengalaman, bukan Robuxpricing = {priceType = CurrencyTypes.PriceType.InExperience,price = 79,currencyId = "Gems",icon = 18712203759,},
Anda juga perlu merujuk pada skrip BundlesExample untuk memanggil setInExperiencePurchaseHandler .
Contoh Bundel
local function awardInExperiencePurchase(
_player: Player,
_bundleId: Types.BundleId,
_currencyId: CurrencyTypes.CurrencyId,
_price: number
)
-- Periksa apakah pemain memiliki mata uang yang cukup untuk membeli bundel
-- 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 pengalaman yang Anda gunakan untuk bundel, atur penangan di sini
for currencyId, _ in Currencies do
Bundles.setInExperiencePurchaseHandler(currencyId, awardInExperiencePurchase)
end
end
Secara khusus, Anda perlu mengisi awardInExperiencePurchase , yang dipanggil oleh loop melalui Currencies di dalam contoh initializePurchaseHandlers (yaitusetiap currencyId terhubung ke penangan melalui Bundles.setInExperiencePurchaseHandler(currencyId, awardInExperiencePurchase) ).
Definisikan bundel
Semua bundel yang dapat ditawarkan dalam pengalaman Anda dapat didefinisikan dalam ReplicatedStorage.Bundles.Configs.Bundles , dengan jenis diekspor dari skrip Types di folder yang sama.
Jika Anda menggunakan devProductId , Anda perlu memperbarui bundel utama devProductId untuk cocok dengan yang ada di pengalaman Anda.Inilah yang akan diminta melalui MarketplaceService untuk membeli bundel itu sendiri. Sangat disarankan untuk menggunakan produk pengembang baru untuk bundel agar lebih mudah melacak penjualan terpisah. Jika Anda ingin paket dengan beberapa item, dan jika ini sudah diwakili oleh produk pengembang dalam pengalaman Anda, Anda tidak perlu secara eksplisit mengatur harga item/assetId/nama, yang akan diambil melalui informasi produk:
README
{itemType = ItemTypes.ItemType.DevProduct,devProductId = <DEV_PRODUCT_ID>,metadata = {caption = {text = "x1",color = Color3.fromRGB(236, 201, 74),} -- Keterangan opsional! Anda juga dapat menghilangkan bidang ini}},
Jika tidak, Anda dapat secara manual mengkonfigurasi detail item tersebut:
README
{itemType = ItemTypes.ItemType.Robux,priceInRobux = 49,icon = <IMAGE_ASSET_ID>,metadata = {caption = {text = "x1",color = Color3.fromRGB(236, 201, 74),} -- Keterangan opsional! Anda juga dapat meninggalkan menghilangkan bidang ini}},
Sebagai contoh, seluruh bundel Anda kemungkinan akan terlihat seperti ini:
README
local starterBundle: Types.RelativeTimeBundle = {bundleType = Types.BundleType.RelativeTime,-- Jika Anda ingin menggunakan produk dev, Anda harus memberikan devProductId unik, hanya digunakan oleh satu bundel.-- Kami akan mengambil harga bundel dan ikon dari produk pengembangpricing = {priceType = CurrencyTypes.PriceType.Marketplace,devProductId = <DEV_PRODUCT_ID>,},-- Jika tidak, jika Anda ingin menggunakan mata uang dalam pengalaman alih-alih produk dev, Anda dapat menggunakan yang berikut:-- Harga di sini adalah dalam mata uang pengalaman, bukan Robux-- harga = {-- priceType = CurrencyTypes.PriceType.InExperience,-- harga = 79,-- currencyId = <CURRENCY_ID>,-- ikon = <IMAGE_ASSET_ID>,-- },includedItems = {[1] = {-- Item itu sendiri tidak dijual melalui produk pengembang, jadi indikasikan berapa nilainya dalam Robux dan berikan ikon-- HargaInRobux membantu Bundel menunjukkan nilai relatif harga bundel vs. total kontennyaitemType = ItemTypes.ItemType.Robux,priceInRobux = 49,icon = <IMAGE_ASSET_ID>,-- Alternatifnya, jika ini memiliki produk dev meninggalkan harga dan ikon di atas dan hanya menetapkan devProductId-- Harga dan ikon akan diambil dari produk pengembang-- produk devProductId = <ITEM_DEV_PRODUCT_ID>-- Ada lebih banyak bidang metadata opsional yang spesifik UI jika diperlukanmetadata = {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 bahkan jika pengalaman Anda mencoba meminta (onPlayerAdded). Anda dapat membuat ini palsu saat menguji di studio.durationInSeconds = 900, -- 14 menitincludesOfflineTime = false, -- Hanya hitung waktu yang terlalu di pengalamanmetadata = {displayName = "STARTER BUNDLE",description = "Save 75% and get a head start!",},}
Integrasikan logika server
Lihat ReplicatedStorage.Bundles.Server.Examples.BundlesExample , yang menunjukkan bagaimana server Anda akan berinteraksi dengan paket fitur Bundel dan metode di atas di ModuleScript .Potongan di bawah ini berasal dari skrip itu.
Anda terutama perlu menghubungkan empat hal setelah menyeret paket fitur Bundel ke dalam pengalaman Anda:
Hubungkan penangan pembelian melalui Bundles.setPurchaseHandler untuk menentukan fungsi yang akan dipanggil untuk menghadiahkan item saat pembelian diproses.
Contoh Bundellocal function awardMarketplacePurchase(_player: Player, _bundleId: Types.BundleId, _receiptInfo: { [string]: any })-- Perbarui data pemain, berikan item, dll-- ... DAN catat penerimaan informasi pembelian ID sehingga kami dapat memeriksa apakah pengguna sudah memiliki bundel initask.wait(2)return Enum.ProductPurchaseDecision.PurchaseGrantedendlocal function awardInExperiencePurchase(_player: Player,_bundleId: Types.BundleId,_currencyId: CurrencyTypes.CurrencyId,_price: number)-- Periksa apakah pemain memiliki mata uang yang cukup untuk membeli bundel-- 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 pengalaman yang Anda gunakan untuk bundel, atur penangan di sinifor currencyId, _ in Currencies doBundles.setInExperiencePurchaseHandler(currencyId, awardInExperiencePurchase)endendHubungkan logika Anda untuk MarketplaceService.ProcessReceipt , tetapi ini mungkin dilakukan di tempat lain jika pengalaman Anda sudah memiliki produk pengembang untuk penjualan.Pada dasarnya, ketika terjadi penerimaan produk pengembang, mereka sekarang akan memanggil Bundles.getBundleByDevProduct untuk memeriksa apakah produk tersebut milik bundel.Jika demikian, skrip kemudian memanggil Bundles.processReceipt .
Contoh Bundel-- Proses 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 penangan untuk produklocal success, result = pcall(handler, receiptInfo, player) -- Hubungi penangan untuk memeriksa apakah logika pembelian berhasilif not success or not result thenwarn("Failed to process receipt:", 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 bundel, biarkan Bundel menanganinyalocal purchaseDecision = Bundles.processReceiptAsync(player, bundleId, receiptInfo)return purchaseDecision == Enum.ProductPurchaseDecision.PurchaseGrantedend-- Pembelian ini tidak termasuk dalam bundel,-- ... Tangani semua logika yang ada di sini jika Anda memiliki apa punreturn falseendHubungkan Players.PlayerAdded:Connect(Bundles.OnPlayerAdded) sehingga paket fitur Bundel meminta ulang bundel aktif yang belum kedaluwarsa untuk pemain.
READMElocal function onPlayerAdded(player: Player)-- Beritahu Bundel ketika pemain bergabung sehingga dapat memuat ulang data merekaBundles.onPlayerAdded(player)-- Jika Anda memiliki beberapa bundel pemula yang ingin Anda tawarkan kepada semua pengguna baru, Anda bisa meminta di sini-- ... Bundel akan menangani jika pemain sudah membelinya atau jika sudah kedaluwarsa karena tidak dapat diulang-- Bundles.promptIfValidAsync(pemain, "StarterBundle")-- Memanggil ini di sini hanya sebagai contoh, Anda dapat memanggil ini kapan saja atau di mana pun Anda mauonPromptBundleXYZEvent(player)endPaket cepat. Sementara ini tergantung pada gameplay, contoh meminta pemain dengan StarterBundle onPlayerAdded .
Logik paket fitur Bundel memastikan setiap pemain tidak mendapatkan tawaran ulang jika mereka sudah membeli bundel, atau jika mereka membiarkan tawaran itu kedaluwarsa (berdasarkan konfigurasi bundel).
Kapan pun Anda ingin meminta bundel kepada pemain, hubungi Bundles.promptIfValidAsync(player, bundleId) .
READMElocal function onPromptBundleXYZEvent(player: Player)-- Hubungkan peristiwa pengalaman apa pun yang ingin Anda gunakan untuk menentukan kapan seorang pemain diminta bundel-- ... Ini akan terjadi setiap kali Anda memenuhi kriteria kelayakan untuk meminta pemain bundel-- ... Misalnya, jika Anda ingin meminta bundel saat pemain bergabung, atau saat pemain naik leveltask.spawn(Bundles.promptIfValidAsync, player, <Some_Bundle_Id>)-- ... Jika membuat beberapa bundel, menggunakan task.spawn() untuk membungkus panggilan fungsi di atas akan mengurangi ketidakcocokan antara hitung mundurend
Pertimbangkan panduan praktik terbaik berikut tentang rekaman redundan dari ReceiptIds:
Sementara paket fitur Bundel melakukan perekaman ReceiptIds untuk menghindari memproses invoice yang sama dua kali, Anda juga harus mencatat ReceiptIds di dalam tabel Anda sehingga jika aliran pembelian gagal setelah penangan pembelian mereka sudah selesai, Anda tahu pada pencobaan berikutnya untuk tidak memberikan item lagi.
Paket fitur Bundel tidak akan merekam ReceiptId jika pembelian gagal di setiap langkah, jadi Anda harus memastikan bahwa Anda mencatat ReceiptId di tabel Anda sebelum memproses pembayaran sebagai bagian dari pengelola pembelian Anda.
Redundansi ini membantu memastikan bahwa semua logika pembelian telah ditangani dengan tepat dan bahwa penyimpanan data toko data dan paket fitur Bundel mencapai konsistensi akhir, dengan penyimpanan data toko data Anda menjadi sumber kebenaran.
Konfigurasi konstan
Konstan untuk paket fitur Inti hidup di dua tempat:
Konstan bersama hidup di ReplicatedStorage.FeaturePackagesCore.Configs.SharedConstants .
Konstan khusus paket, dalam hal ini paket fitur Bundel hidup di ReplicatedStorage.Bundles.Configs.Constants .
Hal utama yang mungkin ingin Anda sesuaikan untuk memenuhi persyaratan desain pengalaman Anda:
- AssetID suara
- Durasi efek pembelian dan warna partikel
- Peringatan penampilan kolapsibilitas kepala
Selain itu, Anda dapat menemukan string untuk terjemahan 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 bundel prompt Anda.Namun, ingatlah bahwa jika Anda memindahkan salah satu objek di sekitar hierarkis, kode tidak akan dapat menemukannya, dan Anda perlu melakukan penyesuaian pada kode Anda.
Sebuah prompt terdiri dari dua komponen tingkat tinggi:
- PromptItem – Komponen individu diulang untuk setiap item dalam bundel (gambar item, keterangan, nama, harga).
- Prompt – Jendela prompt itu sendiri.
Tampilan kepala juga terdiri dari dua komponen:
- HudItem – Komponen individu yang mewakili setiap opsi menu dalam tampilan kepala.
- Hud – Untuk diisi secara programatik dengan HudItems .
Jika Anda ingin memiliki kontrol lebih besar atas tampilan kepala, bukan hanya menggunakan HUD UI yang ada dalam ReplicatedStorage.Bundles.Objects.BundlesGui , Anda dapat memindahkan hal-hal untuk memenuhi persyaratan desain Anda sendiri.Pastikan saja untuk memperbarui perilaku skrip klien di skrip ReplicatedStorage.Bundles.Client.UIController.
Referensi API
Jenis
Waktu Relatif
Setelah bundel RelativeTime diterima oleh pemain, tetap tersedia sampai waktu berakhir.Jenis ini ditampilkan di layar tampilan kepala pemain, dan secara otomatis meminta pada sesi masa depan sampai bundel berakhir atau pemain membelinya.
Contoh umum dari jenis bundel ini adalah penawaran paket pemula satu kali penggunaan yang ditampilkan kepada semua pemain baru selama 24 jam.Untuk praktik terbaik industri tentang cara menerapkan bundel paket pemula, lihat Desain Paket Pemula.
Nama | Jenis | Deskripsi |
---|---|---|
includeOfflineTime | bool | (Opsi) Jika tidak atur, hanya waktu yang dihabiskan dalam pengalaman yang akan dihitung menuju durasi tawaran yang tersisa. |
singleUse | bool | (Opsi) Jika tidak diatur, pembelian dapat diaktifkan kembali setelah dibeli atau kedaluwarsa.Jika diatur, setelah dibeli atau kedaluwarsa pertama kali, tidak akan dapat diaktifkan lagi, bahkan jika Anda memanggil Bundles.promptIfValidAsync dengan bundelId. |
Waktu Tetap
Setelah bundel FixedTime diterima oleh pemain, tetap tersedia sampai akhir waktu universal terkoordinasi (UTC).Jenis ini ditampilkan di layar tampilan kepala pemain, dan secara otomatis meminta pada sesi masa depan sampai bundel berakhir atau pemain membelinya.
Contoh umum dari jenis bundel ini adalah penawaran liburan yang hanya tersedia untuk bulan tertentu.
Satu Kali
Sebuah bundel OneTime hanya tersedia pada saat ditawarkan kepada pemain.Ini tidak ditampilkan di layar kepala atas pemain, dan setelah seorang pemain menutup prompt, prompt tidak dapat dibuka kembali sampai diminta oleh server lagi.
Contoh umum dari jenis bundel ini adalah penawaran untuk membeli lebih banyak mata uang pengalaman saat pemain kehabisan.