Paket Bundles

*Konten ini diterjemahkan menggunakan AI (Beta) dan mungkin mengandung kesalahan. Untuk melihat halaman ini dalam bahasa Inggris, klik di sini.

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:

  1. Tambahkan paket fitur Core dan Bundles ke inventaris Anda di dalam Studio dengan mengklik tautan Add to Inventory dalam set komponen berikut.

  2. Dari menu Window Studio atau toolbar tab Home, buka Toolbox.

  3. Di jendela Toolbox, klik tab Inventory. Penyortiran My Models ditampilkan.

    Jendela Toolbox Studio dengan tab Inventory yang disorot.
  4. Klik ubin Feature Package Core, lalu ubin Bundle Feature Package. Kedua folder paket ditampilkan di jendela Explorer.

  5. Seret folder paket ke dalam ReplicatedStorage.

  6. Izinkan panggilan penyimpanan data untuk melacak pembelian pemain dengan paket tersebut.

    1. Buka jendela FileExperience Settings di Studio.
    2. 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.

Mata Uang
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.

Bundles
-- 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.

BundlesExample
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
end

Secara 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:

README
{
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:

README
{
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:

README
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:

  1. Hubungkan handler pembelian melalui Bundles.setPurchaseHandler untuk menentukan fungsi yang akan dipanggil untuk memberikan item saat pembelian sedang diproses.

    BundlesExample
    local 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 ini
    task.wait(2)
    return Enum.ProductPurchaseDecision.PurchaseGranted
    end
    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
    end
  2. Hubungkan 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 tidak
    local function processReceipt(receiptInfo): Enum.ProductPurchaseDecision
    local userId, productId = receiptInfo.PlayerId, receiptInfo.ProductId
    local player = Players:GetPlayerByUserId(userId)
    if not player then
    return Enum.ProductPurchaseDecision.NotProcessedYet
    end
    local handler = receiptHandlers[productId] -- Dapatkan handler untuk produk
    local success, result = pcall(handler, receiptInfo, player) -- Panggil handler untuk memeriksa apakah logika pembelian berhasil
    if not success or not result then
    warn("Gagal memproses tanda terima:", receiptInfo, result)
    return Enum.ProductPurchaseDecision.NotProcessedYet
    end
    return Enum.ProductPurchaseDecision.PurchaseGranted
    end
    local function receiptHandler(receiptInfo: { [string]: any }, player: Player)
    local bundleId, _bundle = Bundles.getBundleByProductId(receiptInfo.ProductId)
    if bundleId then
    -- Pembelian ini termasuk dalam bundle, biarkan Bundles menanganinya
    local purchaseDecision = Bundles.processReceiptAsync(player, bundleId, receiptInfo)
    return purchaseDecision == Enum.ProductPurchaseDecision.PurchaseGranted
    end
    -- Pembelian ini tidak termasuk dalam bundle,
    -- ... Tangani semua logika yang ada di sini jika Anda memiliki
    return false
    end
  3. Hubungkan Players.PlayerAdded:Connect(Bundles.OnPlayerAdded) sehingga paket fitur Bundles meminta kembali bundle aktif yang belum kedaluwarsa untuk pemain.

    README
    local function onPlayerAdded(player: Player)
    -- Beri tahu Bundles saat pemain bergabung sehingga dapat memuat ulang data mereka
    Bundles.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 inginkan
    onPromptBundleXYZEvent(player)
    end
  4. Minta 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).

    README
    local 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 level
    task.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 mundur
    end

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.

NamaTipeDeskripsi
includeOfflineTimebool(Opsional) Jika tidak diatur, hanya waktu yang dihabiskan dalam game yang akan dihitung menuju durasi tawaran yang tersisa.
singleUsebool(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.

©2026 Roblox Corporation. Roblox, logo Roblox, dan Powering Imagination termasuk dalam merek dagang kami yang terdaftar dan tidak terdaftar di AS dan negara lainnya.