Il pacchetto della funzionalità Bundles offre funzionalità pronte all'uso per vendere collezioni di oggetti ai giocatori a un prezzo scontato. Puoi scegliere se consentire ai giocatori di acquistare i bundle utilizzando una valuta personalizzata di gioco o Robux, quale tipo di bundle desideri utilizzare, quale insieme di oggetti vuoi vendere e come vuoi invitare i giocatori durante il loro gameplay.
Utilizzando le opzioni di personalizzazione del pacchetto, puoi adattare i tuoi bundle per soddisfare gli obiettivi di design e monetizzazione dei tuoi giochi, come ad esempio:
- Mirare a un basso tasso di conversione offrendo pacchetti di avvio scontati che forniscono valore ai nuovi giocatori e incoraggiano le spese precoci.
- Aumentare la profondità di spesa raggruppando oggetti a vari livelli di prezzo per attrarre una gamma di giocatori.
- Monetizzare eventi di operazioni dal vivo (LiveOps) eventi offrendo bundle di oggetti esclusivi a tempo limitato.

Ottieni il pacchetto
Il Creator Store è una scheda del Toolbox che puoi utilizzare per trovare tutte le risorse create da Roblox e dalla comunità di Roblox per l'uso all'interno dei tuoi progetti, inclusi modelli, immagini, mesh, audio, plugin, video e risorse font. Puoi utilizzare il Creator Store per aggiungere una o più risorse direttamente in un gioco aperto, inclusi i pacchetti di funzionalità!
Ogni pacchetto di funzionalità richiede il pacchetto di funzionalità Core per funzionare correttamente. Una volta che le risorse del pacchetto di funzionalità Core e Bundles sono nel tuo inventario, puoi riutilizzarle in qualsiasi progetto sulla piattaforma.
Per ottenere i pacchetti dal tuo inventario nel tuo gioco:
Aggiungi il pacchetto di funzionalità Core e Bundles al tuo inventario all'interno di Studio facendo clic sul link Aggiungi all'inventario nel seguente insieme di componenti.
Dal menu Finestra di Studio o dalla barra degli strumenti della scheda Home, apri il Toolbox.
Nella finestra Toolbox, fai clic sulla scheda Inventario. La visualizzazione I miei modelli viene mostrata.

Fai clic sulla tile Pacchetto di funzionalità Core, quindi sulla tile Pacchetto di funzionalità Bundle. Entrambe le cartelle del pacchetto vengono visualizzate nella finestra Explorer.
Trascina le cartelle del pacchetto in ReplicatedStorage.
Consenti alle chiamate di archiviazione dati di tracciare gli acquisti dei giocatori con i pacchetti.
- Apri la finestra File ⟩ Impostazioni esperienza di Studio.
- Naviga alla scheda Sicurezza, quindi abilita Abilita accesso Studio ai servizi API.
Definisci le valute
Se il tuo gioco ha un proprio sistema di valuta, puoi registrarlo con il pacchetto di funzionalità Core definendolo in ReplicatedStorage.FeaturePackagesCore.Configs.Currencies. C'è un esempio commentato di una valuta Gems già in questo file; sostituiscilo con il tuo.
Gems = {
displayName = "Gems",
symbol = "💎",
icon = nil,
},Lo script Currencies informa il pacchetto di funzionalità Core su alcuni metadati riguardanti la tua valuta:
- (obbligatorio) displayName - Il nome della tua valuta. Se non specifichi un simbolo o un'icona, questo nome viene utilizzato nei pulsanti di acquisto (ad es. "100 Gems").
- (opzionale) symbol - Se hai un carattere di testo da utilizzare come icona per la tua valuta, questo viene utilizzato al posto del displayName nei pulsanti di acquisto (ad es. "💎100").
- (opzionale) icon - Se hai un'icona immagine AssetId per la tua valuta, questa viene utilizzata al posto del displayName nei pulsanti di acquisto (ad es. l'immagine verrà posizionata a sinistra del prezzo "🖼️100").
Una volta che la tua valuta è configurata, devi specificare manualmente il prezzo del bundle, la valuta e l'icona per il display invece che queste informazioni vengano recuperate dal prodotto sviluppatore associato al bundle.
-- Se desideri utilizzare un prodotto sviluppatore, devi fornire un devProductId unico, utilizzato solo da un bundle.
-- Recupereremo il prezzo e l'icona del bundle dal prodotto sviluppatore
pricing = {
priceType = CurrencyTypes.PriceType.Marketplace,
devProductId = 1795621566,
},
-- In alternativa, se desideri utilizzare la valuta di gioco invece di un prodotto sviluppatore, puoi utilizzare quanto segue:
-- Il prezzo qui è nella valuta di gioco, non in Robux
pricing = {
priceType = CurrencyTypes.PriceType.InExperience,
price = 79,
currencyId = "Gems",
icon = 18712203759,
},Devi anche fare riferimento allo script BundlesExample per chiamare setInExperiencePurchaseHandler.
local function awardInExperiencePurchase(
_player: Player,
_bundleId: Types.BundleId,
_currencyId: CurrencyTypes.CurrencyId,
_price: number
)
-- Controlla se il giocatore ha abbastanza valuta per acquistare il bundle
-- Aggiorna i dati del giocatore, fornisci oggetti, ecc.
-- Deduci la valuta dal giocatore
task.wait(2)
return true
end
local function initializePurchaseHandlers()
local bundles = Bundles.getBundles()
for bundleId, bundle in bundles do
-- Il bundle non è associato a un prodotto sviluppatore se non ha un tipo di prezzo di mercato
if not bundle or bundle.pricing.priceType ~= "Marketplace" then
continue
end
Bundles.setPurchaseHandler(bundleId, awardMarketplacePurchase)
receiptHandlers[bundle.pricing.devProductId] = receiptHandler
end
-- Se hai delle valute di gioco che stai utilizzando per i bundle, imposta il gestore qui
for currencyId, _ in Currencies do
Bundles.setInExperiencePurchaseHandler(currencyId, awardInExperiencePurchase)
end
endIn particolare, devi completare awardInExperiencePurchase, che viene chiamato da un ciclo attraverso Currencies all'interno dell'esempio initializePurchaseHandlers (ad es. ogni currencyId è collegato al gestore tramite Bundles.setInExperiencePurchaseHandler(currencyId, awardInExperiencePurchase)).
Definisci i bundle
Tutti i bundle offerti nel tuo gioco possono essere definiti all'interno di ReplicatedStorage.Bundles.Configs.Bundles, con tipi esportati dallo script Types nella stessa cartella.
Se stai utilizzando un devProductId, devi aggiornare il devProductId principale del bundle per corrispondere a quello nel tuo gioco. Questo è ciò che verrà richiesto tramite MarketplaceService per acquistare il bundle stesso. Si consiglia vivamente di utilizzare un nuovo prodotto sviluppatore per il bundle per facilitare il tracciamento delle vendite separate.
Se desideri un bundle con più oggetti, e se questi sono già rappresentati da prodotti sviluppatori nel tuo gioco, non è necessario impostare esplicitamente il prezzo/assetId/nome dell'oggetto, che verrà recuperato tramite le informazioni sul prodotto:
{
itemType = ItemTypes.ItemType.DevProduct,
devProductId = <DEV_PRODUCT_ID>,
metadata = {
caption = {
text = "x1",
color = Color3.fromRGB(236, 201, 74),
} -- La didascalia è facoltativa! Puoi anche omettere questo campo
}
},In alternativa, puoi configurare manualmente i dettagli di quegli oggetti:
{
itemType = ItemTypes.ItemType.Robux,
priceInRobux = 49,
icon = <IMAGE_ASSET_ID>,
metadata = {
caption = {
text = "x1",
color = Color3.fromRGB(236, 201, 74),
} -- La didascalia è facoltativa! Puoi anche omettere questo campo
}
},Ad esempio, il tuo intero bundle potrebbe apparire così:
local starterBundle: Types.RelativeTimeBundle = {
bundleType = Types.BundleType.RelativeTime,
-- Se desideri utilizzare un prodotto sviluppatore, devi fornire un devProductId unico, utilizzato solo da un bundle.
-- Recupereremo il prezzo e l'icona del bundle dal prodotto sviluppatore
pricing = {
priceType = CurrencyTypes.PriceType.Marketplace,
devProductId = <DEV_PRODUCT_ID>,
},
-- In alternativa, se desideri utilizzare la valuta di gioco invece di un prodotto sviluppatore, puoi utilizzare quanto segue:
-- Il prezzo qui è nella valuta di gioco, non in Robux
-- pricing = {
-- priceType = CurrencyTypes.PriceType.InExperience,
-- price = 79,
-- currencyId = <CURRENCY_ID>,
-- icon = <IMAGE_ASSET_ID>,
-- },
includedItems = {
[1] = {
-- L'oggetto stesso non è venduto tramite un prodotto sviluppatore, quindi indica quanto vale in Robux e fornisci un'icona
-- Il priceInRobux aiuta i Bundles a mostrare il valore relativo del prezzo del bundle rispetto alla somma dei suoi contenuti
itemType = ItemTypes.ItemType.Robux,
priceInRobux = 49,
icon = <IMAGE_ASSET_ID>,
-- In alternativa, se questo ha un prodotto sviluppatore, ometti il prezzo e l'icona sopra e imposta solo il devProductId
-- Il prezzo e l'icona verranno recuperati dal prodotto sviluppatore
-- devProductId = <ITEM_DEV_PRODUCT_ID>
-- Ci sono più campi di metadati facoltativi che sono specifici per l'interfaccia utente se necessario
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, -- Una volta acquistato o scaduto, non è più valido anche se il tuo gioco cerca di richiamarlo (onPlayerAdded). Puoi impostarlo su false durante i test in studio.
durationInSeconds = 900, -- 15 minuti
includesOfflineTime = false, -- Conta solo il tempo trascorso nel gioco
metadata = {
displayName = "BUNDLE DI AVVIO",
description = "Risparmia il 75% e inizia in anticipo!",
},
}Integra la logica del server
Dai un'occhiata a ReplicatedStorage.Bundles.Server.Examples.BundlesExample, che mostra come il tuo server interagirà con il pacchetto di funzionalità Bundles e i metodi sopra menzionati nello ModuleScript. I frammenti di codice qui sotto provengono da quel script.
Devi principalmente collegare quattro cose una volta trascinato il pacchetto di funzionalità Bundles nel tuo gioco:
Collega i gestori di acquisto tramite Bundles.setPurchaseHandler per specificare le funzioni da chiamare per assegnare oggetti quando un acquisto viene elaborato.
BundlesExamplelocal function awardMarketplacePurchase(_player: Player, _bundleId: Types.BundleId, _receiptInfo: { [string]: any })-- Aggiorna i dati del giocatore, fornisci oggetti, ecc.-- ... E registra receiptInfo.PurchaseId in modo da poter controllare se l'utente ha già questo bundletask.wait(2)return Enum.ProductPurchaseDecision.PurchaseGrantedendlocal function awardInExperiencePurchase(_player: Player,_bundleId: Types.BundleId,_currencyId: CurrencyTypes.CurrencyId,_price: number)-- Controlla se il giocatore ha abbastanza valuta per acquistare il bundle-- Aggiorna i dati del giocatore, fornisci oggetti, ecc.-- Deduci la valuta dal giocatoretask.wait(2)return trueendlocal function initializePurchaseHandlers()local bundles = Bundles.getBundles()for bundleId, bundle in bundles do-- Il bundle non è associato a un prodotto sviluppatore se non ha un tipo di prezzo di mercatoif not bundle or bundle.pricing.priceType ~= "Marketplace" thencontinueendBundles.setPurchaseHandler(bundleId, awardMarketplacePurchase)receiptHandlers[bundle.pricing.devProductId] = receiptHandlerend-- Se hai delle valute di gioco che stai utilizzando per i bundle, imposta il gestore quifor currencyId, _ in Currencies doBundles.setInExperiencePurchaseHandler(currencyId, awardInExperiencePurchase)endendCollega la tua logica per MarketplaceService.ProcessReceipt, ma questo potrebbe essere fatto altrove se il tuo gioco ha già prodotti sviluppatori in vendita. Fondamentalmente, quando una ricevuta di prodotto sviluppatore viene elaborata, ora chiameranno Bundles.getBundleByDevProduct per controllare se il prodotto appartiene a un bundle. Se sì, lo script chiama quindi Bundles.processReceipt.
BundlesExample-- Elabora la ricevuta dal mercato per determinare se il giocatore deve essere addebitato o menolocal 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] -- Ottieni il gestore per il prodottolocal success, result = pcall(handler, receiptInfo, player) -- Chiama il gestore per controllare se la logica di acquisto ha avuto successoif not success or not result thenwarn("Impossibile elaborare la ricevuta:", 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-- Questo acquisto appartiene a un bundle, lascia che Bundles se ne occupilocal purchaseDecision = Bundles.processReceiptAsync(player, bundleId, receiptInfo)return purchaseDecision == Enum.ProductPurchaseDecision.PurchaseGrantedend-- Questo acquisto non appartiene a un bundle,-- ... Gestisci tutta la tua logica esistente qui se ne haireturn falseendCollega Players.PlayerAdded:Connect(Bundles.OnPlayerAdded) in modo che il pacchetto di funzionalità Bundles riproponga eventuali bundle attivi che non sono ancora scaduti per un giocatore.
READMElocal function onPlayerAdded(player: Player)-- Comunica a Bundles quando il giocatore si unisce in modo che possa ricaricare i loro datiBundles.onPlayerAdded(player)-- Se avevi un bundle di avvio che volevi offrire a tutti i nuovi utenti, potresti richiamarlo qui-- ... Bundles gestirà se il giocatore ha già acquistato o se è scaduto poiché non è ripetibile-- Bundles.promptIfValidAsync(player, "StarterBundle")-- Chiamare questo qui solo per esempio, puoi chiamare questo ogni volta o ovunque tu vogliaonPromptBundleXYZEvent(player)endPropaga i bundle. Anche se questo dipende dal gameplay, l'esempio propone ai giocatori un StarterBundle onPlayerAdded.
La logica del pacchetto di funzionalità Bundles garantisce che ogni giocatore non riceva un'offerta ripetuta se ha già acquistato il bundle, o se ha lasciato scadere l'offerta (in base alla configurazione del bundle).
Ogni volta che desideri proporre un bundle a un giocatore, chiama Bundles.promptIfValidAsync(player, bundleId).
READMElocal function onPromptBundleXYZEvent(player: Player)-- Collega qualsiasi evento di gioco desideri utilizzare per determinare quando un giocatore viene invitato al bundle-- ... Questo sarà ogni volta che hai soddisfatto i tuoi criteri di idoneità per proporre un bundle a un giocatore-- ... Ad esempio, se desideri proporre un bundle quando un giocatore si unisce, o quando un giocatore sale di livellotask.spawn(Bundles.promptIfValidAsync, player, <Some_Bundle_Id>)-- ... Se crei più bundle, utilizzare task.spawn() per avvolgere la chiamata alla funzione sopra ridurrà le discrepanze tra i conteggiend
Considera le seguenti linee guida sulle migliori pratiche per le registrazioni ridondanti di ReceiptIds:
Anche se il pacchetto di funzionalità Bundles registra ReceiptIds per evitare di elaborare la stessa ricevuta due volte, dovresti anche registrare ReceiptIds all'interno delle tue tabelle in modo che, se il flusso di acquisto fallisce dopo che il gestore di acquisto ha già terminato, tu sappia al successivo tentativo di non assegnare nuovamente gli oggetti.
Il pacchetto di funzionalità Bundles non registrerà il ReceiptId se l'acquisto fallisce in qualsiasi fase, quindi dovresti assicurarti di registrare il ReceiptId nelle tue tabelle prima di elaborare la ricevuta come parte del tuo purchaseHandler.
Questa ridondanza aiuta a garantire che tutta la logica di acquisto sia stata gestita in modo appropriato e che il tuo archivio dati e l'archivio dati del pacchetto di funzionalità Bundles raggiungano una coerenza finale, con il tuo archivio dati che funge da fonte di verità.
Configura costanti
Le costanti per il pacchetto di funzionalità Core vivono in due posti:
Le costanti condivise vivono in ReplicatedStorage.FeaturePackagesCore.Configs.SharedConstants.
Le costanti specifiche del pacchetto, in questo caso il pacchetto di funzionalità Bundles, vivono in ReplicatedStorage.Bundles.Configs.Constants.
Le principali cose che potresti voler regolare per soddisfare i requisiti di design del tuo gioco:
- ID delle risorse audio
- Durata dell'effetto di acquisto e colori delle particelle
- Collassabilità del display
Inoltre, puoi trovare stringhe per la traduzione suddivise in un'unica posizione: ReplicatedStorage.FeaturePackagesCore.Configs.TranslationStrings.
Personalizza i componenti dell'interfaccia utente
Modificando gli oggetti del pacchetto, come colori, font e trasparenza, puoi regolare la presentazione visiva dei tuoi inviti ai bundle. Tuttavia, tieni presente che se sposti uno qualsiasi degli oggetti in modo gerarchico, il codice non sarà in grado di trovarli e dovrai apportare modifiche al tuo codice.
Un invito è composto da due componenti di alto livello:
- PromptItem – Il componente individuale ripetuto per ogni oggetto all'interno di un bundle (immagine dell'oggetto, didascalia, nome, prezzo).
- Prompt – La finestra di invito stessa.
Il display è anche composto da due componenti:
- HudItem – Un componente individuale che rappresenta ciascuna opzione di menu nel display.
- Hud – Da riempire programmaticamente con HudItems.
Se desideri avere un maggiore controllo sul display, invece di utilizzare semplicemente l'interfaccia utente HUD esistente all'interno di ReplicatedStorage.Bundles.Objects.BundlesGui, puoi spostare le cose per soddisfare i tuoi requisiti di design. Assicurati solo di aggiornare il comportamento dello script client nello script ReplicatedStorage.Bundles.Client.UIController.
Riferimento API
Tipi
RelativeTime
Una volta che il bundle RelativeTime è offerto a un giocatore, rimane disponibile fino a quando la durata del tempo non scade. Questo tipo viene visualizzato nel display del giocatore e viene automaticamente proposto nelle sessioni future fino a quando il bundle non scade o il giocatore non lo acquista.
Un esempio comune di questo tipo di bundle è un'offerta di pacchetto di avvio usa e getta che viene visualizzata a tutti i nuovi giocatori per 24 ore.
| Nome | Tipo | Descrizione |
|---|---|---|
| includeOfflineTime | bool | (Opzionale) Se non impostato, solo il tempo trascorso nel gioco verrà conteggiato per la durata rimanente dell'offerta. |
| singleUse | bool | (Opzionale) Se non impostato, l'acquisto può essere riattivato dopo essere stato acquistato o scaduto. Se impostato, una volta acquistato o scaduto la prima volta, non sarà mai più proposto, anche se chiami Bundles.promptIfValidAsync con il bundleId. |
FixedTime
Una volta che il bundle FixedTime è offerto a un giocatore, rimane disponibile fino alla fine dell'ora coordinata universale (UTC) impostata. Questo tipo viene visualizzato nel display del giocatore e viene automaticamente proposto nelle sessioni future fino a quando il bundle non scade o il giocatore non lo acquista.
Un esempio comune di questo tipo di bundle è un'offerta festiva disponibile solo per un determinato mese.
OneTime
Un bundle OneTime è disponibile solo nel momento in cui viene offerto a un giocatore. Non viene visualizzato nel display del giocatore e, una volta che un giocatore chiude l'invito, non può essere riaperto fino a quando non viene proposto di nuovo dal server.
Un esempio comune di questo tipo di bundle è un'offerta per acquistare più valuta di gioco nel momento in cui un giocatore esaurisce.