Pack de Bundles

*Ce contenu est traduit en utilisant l'IA (Beta) et peut contenir des erreurs. Pour consulter cette page en anglais, clique ici.

Le pack de fonctionnalités Bundles offre une fonctionnalité prête à l'emploi pour vendre des collections d'objets aux joueurs à un prix réduit. Vous pouvez choisir de permettre aux joueurs d'acheter des bundles en utilisant une monnaie personnalisée dans l'expérience ou des Robux, quel type de bundle vous souhaitez utiliser, quel ensemble d'objets vous souhaitez vendre et comment vous souhaitez inviter les joueurs pendant leur gameplay.

En utilisant les options de personnalisation du pack, vous pouvez adapter vos bundles pour atteindre les objectifs de conception et de monétisation de vos expériences, tels que :

  • Cibler un faible taux de conversion en offrant des packs de démarrage à prix réduit qui apportent une valeur aux nouveaux joueurs et encouragent les dépenses précoces.
  • Augmenter la profondeur de dépense en regroupant des objets à différents prix pour séduire un éventail de joueurs.
  • Monétiser les opérations en direct (LiveOps) événements en proposant des bundles à durée limitée d'objets exclusifs.

Obtenir le pack

La Boutique Créateur est un onglet de la Boîte à outils que vous pouvez utiliser pour trouver tous les actifs créés par Roblox et la communauté Roblox pour utilisation dans vos projets, y compris des modèles, des images, des maillages, de l'audio, des plugins, des vidéos et des actifs de police. Vous pouvez utiliser la Boutique Créateur pour ajouter un ou plusieurs actifs directement dans une expérience ouverte, y compris les packs de fonctionnalités !

Chaque pack de fonctionnalités nécessite le pack de fonctionnalités Core pour fonctionner correctement. Une fois que les actifs des packs de fonctionnalités Core et Bundles sont dans votre inventaire, vous pouvez les réutiliser dans n'importe quel projet sur la plateforme.

Pour obtenir les packages de votre inventaire dans votre expérience :

  1. Ajoutez le pack de fonctionnalités Core et le pack de fonctionnalités Bundles à votre inventaire dans Studio en cliquant sur le lien Ajouter à l'inventaire dans le jeu d'éléments suivant.

  2. Depuis le menu Fenêtre de Studio ou la barre d'outils de l'onglet Accueil, ouvrez la Boîte à outils.

  3. Dans la fenêtre Boîte à outils, cliquez sur l'onglet Inventaire. Le tri Mes modèles s'affiche.

    Fenêtre de la Boîte à outils de Studio avec l'onglet Inventaire en surbrillance.
  4. Cliquez sur le carreau Pack de fonctionnalités Core, puis sur le carreau Pack de fonctionnalités Bundles. Les deux dossiers de packs s'affichent dans la fenêtre Explorateur.

  5. Faites glisser les dossiers de packs dans ReplicatedStorage.

  6. Autorisez les appels de stockage de données à suivre les achats des joueurs avec les packs.

    1. Ouvrez la fenêtre FichierParamètres de l'expérience de Studio.
    2. Allez à l'onglet Sécurité, puis activez Activer l'accès Studio aux services API.

Définir les monnaies

Si votre expérience a son propre système de monnaies, vous pouvez les enregistrer avec le pack de fonctionnalités Core en les définissant dans ReplicatedStorage.FeaturePackagesCore.Configs.Currencies. Il y a un exemple commenté d'une monnaie Gems déjà présent dans ce fichier ; remplacez-le par le vôtre.

Monnaies

Gems = {
displayName = "Gems",
symbol = "💎",
icon = nil,
},

Le script Currencies informe le pack de fonctionnalités Core de certaines métadonnées concernant votre monnaie :

  • (obligatoire) displayName - Le nom de votre monnaie. Si vous ne spécifiez pas de symbole ou d'icône, ce nom est utilisé dans les boutons d'achat (c'est-à-dire "100 Gems").
  • (optionnel) symbol - Si vous avez un caractère de texte à utiliser comme icône pour votre monnaie, cela sera utilisé à la place du displayName dans les boutons d'achat (c'est-à-dire "💎100").
  • (optionnel) icon - Si vous avez un icône image AssetId pour votre monnaie, cela sera utilisé à la place du displayName dans les boutons d'achat (c'est-à-dire que l'image sera placée à gauche du prix "🖼️100").

Une fois votre monnaie configurée, vous devez spécifier manuellement le prix du bundle, la monnaie et l'icône pour l'affichage tête haute au lieu que cette information soit récupérée à partir du produit développeur associé au bundle.

Bundles

-- Si vous souhaitez utiliser un produit développeur, vous devez fournir un devProductId unique, utilisé uniquement par un bundle.
-- Nous récupérerons le prix du bundle et l'icône à partir du produit développeur
pricing = {
priceType = CurrencyTypes.PriceType.Marketplace,
devProductId = 1795621566,
},
-- Sinon, si vous souhaitez utiliser une monnaie en expérience au lieu d'un produit développeur, vous pouvez utiliser cela à la place :
-- Le prix ici est dans la monnaie en expérience, pas en Robux
pricing = {
priceType = CurrencyTypes.PriceType.InExperience,
price = 79,
currencyId = "Gems",
icon = 18712203759,
},

Vous devez également référencer le script BundlesExample pour appeler setInExperiencePurchaseHandler.

BundlesExample

local function awardInExperiencePurchase(
_player: Player,
_bundleId: Types.BundleId,
_currencyId: CurrencyTypes.CurrencyId,
_price: number
)
-- Vérifiez si le joueur a suffisamment de monnaie pour acheter le bundle
-- Mettez à jour les données du joueur, donnez des objets, etc.
-- Déduisez la monnaie du joueur
task.wait(2)
return true
end
local function initializePurchaseHandlers()
local bundles = Bundles.getBundles()
for bundleId, bundle in bundles do
-- Le bundle n'est pas associé à un produit développeur s'il n'a pas de type de prix de marché
if not bundle or bundle.pricing.priceType ~= "Marketplace" then
continue
end
Bundles.setPurchaseHandler(bundleId, awardMarketplacePurchase)
receiptHandlers[bundle.pricing.devProductId] = receiptHandler
end
-- Si vous avez des monnaies en expérience que vous utilisez pour les bundles, définissez le gestionnaire ici
for currencyId, _ in Currencies do
Bundles.setInExperiencePurchaseHandler(currencyId, awardInExperiencePurchase)
end
end

Spécifiquement, vous devez remplir awardInExperiencePurchase, qui est appelé par une boucle à travers Currencies dans la fonction d'exemple initializePurchaseHandlers (c'est-à-dire que chaque currencyId est connecté au gestionnaire via Bundles.setInExperiencePurchaseHandler(currencyId, awardInExperiencePurchase)).

Définir les bundles

Tous les bundles offerts dans votre expérience peuvent être définis dans ReplicatedStorage.Bundles.Configs.Bundles, avec des types exportés depuis le script Types dans le même dossier.

Si vous utilisez un devProductId, vous devez mettre à jour le devProductId principal du bundle pour qu'il corresponde à celui de votre expérience. C'est ce qui sera proposé via MarketplaceService pour acheter le bundle lui-même. Il est fortement recommandé d'utiliser un nouveau produit développeur pour le bundle afin de faciliter le suivi des ventes séparées.

Si vous souhaitez un bundle avec plusieurs objets, et si ceux-ci sont déjà représentés par des produits développeurs dans votre expérience, vous n'avez pas besoin de spécifier explicitement le prix de l'objet/assetId/nom, qui sera récupéré via les informations du produit :

README

{
itemType = ItemTypes.ItemType.DevProduct,
devProductId = <DEV_PRODUCT_ID>,
metadata = {
caption = {
text = "x1",
color = Color3.fromRGB(236, 201, 74),
} -- La légende est optionnelle ! Vous pouvez également omettre ce champ
}
},

Sinon, vous pouvez configurer manuellement ces détails d'objets :

README

{
itemType = ItemTypes.ItemType.Robux,
priceInRobux = 49,
icon = <IMAGE_ASSET_ID>,
metadata = {
caption = {
text = "x1",
color = Color3.fromRGB(236, 201, 74),
} -- La légende est optionnelle ! Vous pouvez également omettre ce champ
}
},

Par exemple, votre bundle entier ressemblera probablement à ceci :

README

local starterBundle: Types.RelativeTimeBundle = {
bundleType = Types.BundleType.RelativeTime,
-- Si vous souhaitez utiliser un produit développeur, vous devez fournir un devProductId unique, utilisé uniquement par un bundle.
-- Nous récupérerons le prix du bundle et l'icône à partir du produit développeur
pricing = {
priceType = CurrencyTypes.PriceType.Marketplace,
devProductId = <DEV_PRODUCT_ID>,
},
-- Sinon, si vous souhaitez utiliser une monnaie en expérience au lieu d'un produit développeur, vous pouvez utiliser cela à la place :
-- Le prix ici est dans la monnaie en expérience, pas en Robux
-- pricing = {
-- priceType = CurrencyTypes.PriceType.InExperience,
-- price = 79,
-- currencyId = <CURRENCY_ID>,
-- icon = <IMAGE_ASSET_ID>,
-- },
includedItems = {
[1] = {
-- L'objet lui-même n'est pas vendu via un produit développeur, donc indiquez combien il vaut en Robux et donnez une icône
-- Le priceInRobux aide Bundles à montrer la valeur relative du prix du bundle par rapport à la somme de son contenu
itemType = ItemTypes.ItemType.Robux,
priceInRobux = 49,
icon = <IMAGE_ASSET_ID>,
-- Alternativement, si cela a un produit développeur, laissez de côté le prix et l'icône ci-dessus et définissez simplement le devProductId
-- Le prix et l'icône seront récupérés à partir du produit développeur
-- devProductId = <ITEM_DEV_PRODUCT_ID>
-- Il existe d'autres champs de métadonnées optionnels qui sont spécifiques à l'interface utilisateur si nécessaire
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, -- Une fois acheté ou expiré, ne sera plus valide même si votre expérience essaie de proposer (onPlayerAdded). Vous pouvez mettre cela à false pendant les tests dans studio.
durationInSeconds = 900, -- 15 minutes
includesOfflineTime = false, -- Ne compte que le temps écoulé dans l'expérience
metadata = {
displayName = "BUNDLE DE DÉMARRAGE",
description = "Économisez 75 % et obtenez un bon départ !",
},
}

Intégrer la logique serveur

Jetez un œil à ReplicatedStorage.Bundles.Server.Examples.BundlesExample, qui montre comment votre serveur interagira avec le pack de fonctionnalités Bundles et les méthodes ci-dessus sur ModuleScript. Les extraits ci-dessous proviennent de ce script.

Vous devez principalement relier quatre éléments après avoir glissé le pack de fonctionnalités Bundles dans votre expérience :

  1. Connectez les gestionnaires d'achats via Bundles.setPurchaseHandler pour spécifier les fonctions à appeler pour récompenser des objets lorsque l'achat est en cours de traitement.

    BundlesExample

    local function awardMarketplacePurchase(_player: Player, _bundleId: Types.BundleId, _receiptInfo: { [string]: any })
    -- Mettez à jour les données du joueur, donnez des objets, etc.
    -- ... ET enregistrez receiptInfo.PurchaseId afin que nous puissions vérifier si l'utilisateur possède déjà ce bundle
    task.wait(2)
    return Enum.ProductPurchaseDecision.PurchaseGranted
    end
    local function awardInExperiencePurchase(
    _player: Player,
    _bundleId: Types.BundleId,
    _currencyId: CurrencyTypes.CurrencyId,
    _price: number
    )
    -- Vérifiez si le joueur a suffisamment de monnaie pour acheter le bundle
    -- Mettez à jour les données du joueur, donnez des objets, etc.
    -- Déduisez la monnaie du joueur
    task.wait(2)
    return true
    end
    local function initializePurchaseHandlers()
    local bundles = Bundles.getBundles()
    for bundleId, bundle in bundles do
    -- Le bundle n'est pas associé à un produit développeur s'il n'a pas de type de prix de marché
    if not bundle or bundle.pricing.priceType ~= "Marketplace" then
    continue
    end
    Bundles.setPurchaseHandler(bundleId, awardMarketplacePurchase)
    receiptHandlers[bundle.pricing.devProductId] = receiptHandler
    end
    -- Si vous avez des monnaies en expérience que vous utilisez pour les bundles, définissez le gestionnaire ici
    for currencyId, _ in Currencies do
    Bundles.setInExperiencePurchaseHandler(currencyId, awardInExperiencePurchase)
    end
    end
  2. Connectez votre logique pour MarketplaceService.ProcessReceipt, mais cela pourrait être fait ailleurs si votre expérience a déjà des produits développeurs à vendre. Essentiellement, lorsqu'un reçu de produit développeur est en cours de traitement, ils appeleront maintenant Bundles.getBundleByDevProduct pour vérifier si le produit appartient à un bundle. Si c'est le cas, le script appelle alors Bundles.processReceipt.

    BundlesExample

    -- Traitez le reçu du marché pour déterminer si le joueur doit être facturé ou non
    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] -- Obtenez le gestionnaire pour le produit
    local success, result = pcall(handler, receiptInfo, player) -- Appelez le gestionnaire pour vérifier si la logique d'achat a réussi
    if not success or not result then
    warn("Échec du traitement du reçu :", 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
    -- Cet achat appartient à un bundle, laissez Bundles s'en occuper
    local purchaseDecision = Bundles.processReceiptAsync(player, bundleId, receiptInfo)
    return purchaseDecision == Enum.ProductPurchaseDecision.PurchaseGranted
    end
    -- Cet achat n'appartient pas à un bundle,
    -- ... Gérez toute votre logique existante ici si vous en avez
    return false
    end
  3. Connectez Players.PlayerAdded:Connect(Bundles.OnPlayerAdded) afin que le pack de fonctionnalités Bundles redémarre les générations de bundles actifs qui n'ont pas encore expiré pour un joueur.

    README

    local function onPlayerAdded(player: Player)
    -- Informez Bundles lorsque le joueur rejoint afin qu'il puisse recharger ses données
    Bundles.onPlayerAdded(player)
    -- Si vous aviez un bundle de démarrage que vous souhaitiez offrir à tous les nouveaux utilisateurs, vous pourriez le proposer ici
    -- ... Bundles gérera si le joueur l'a déjà acheté ou s'il a expiré puisqu'il n'est pas répétable
    -- Bundles.promptIfValidAsync(player, "StarterBundle")
    -- Appel de ceci juste à titre d'exemple, vous pouvez appeler cela à tout moment ou n'importe où
    onPromptBundleXYZEvent(player)
    end
  4. Proposez des bundles. Bien que cela dépende du gameplay, l'exemple propose aux joueurs un StarterBundle onPlayerAdded.

    • La logique du pack de fonctionnalités Bundles garantit que chaque joueur ne reçoive pas une offre répétée s'il a déjà acheté le bundle ou si l'offre a déjà expiré (en fonction de la configuration du bundle).

    • Chaque fois que vous souhaitez proposer un bundle à un joueur, appelez Bundles.promptIfValidAsync(player, bundleId).

    README

    local function onPromptBundleXYZEvent(player: Player)
    -- Connectez l'événement d'expérience que vous souhaitez utiliser pour déterminer quand un joueur reçoit une proposition de bundle
    -- ... Cela sera chaque fois que vous avez rempli vos critères d'éligibilité pour proposer le bundle à un joueur
    -- ... Par exemple, si vous souhaitez proposer un bundle lorsqu'un joueur rejoint, ou lorsqu'un joueur monte de niveau
    task.spawn(Bundles.promptIfValidAsync, player, <Some_Bundle_Id>)
    -- ... Si vous créez plusieurs bundles, utiliser task.spawn() pour envelopper l'appel de la fonction ci-dessus minimisera les écarts entre les compteurs
    end

Considérez les meilleures pratiques suivantes concernant les enregistrements redondants de ReceiptIds :

  • Bien que le pack de fonctionnalités Bundles enregistre les ReceiptIds pour éviter le traitement du même reçu deux fois, vous devriez également enregistrer les ReceiptIds dans vos tables pour que si le flux d'achat échoue après que son gestionnaire d'achats a déjà terminé, vous sachiez lors de la tentative subséquente de ne pas attribuer à nouveau des objets.

  • Le pack de fonctionnalités Bundles ne déduira pas le ReceiptId si l'achat échoue à n'importe quelle étape, donc vous devez vous assurer que vous enregistrez le ReceiptId dans vos tables avant de traiter le reçu en tant que partie de votre purchaseHandler.

  • Cette redondance aide à garantir que toute la logique d'achat a été correctement traitée et que votre magasin de données et le magasin de données du pack de fonctionnalités Bundles atteignent une cohérence éventuelle, votre magasin de données étant la source de vérité.

Configurer les constantes

Les constantes pour le pack de fonctionnalités Core résident à deux endroits :

  • Les constantes partagées résident dans ReplicatedStorage.FeaturePackagesCore.Configs.SharedConstants.

  • Les constantes spécifiques au pack, dans ce cas le pack de fonctionnalités Bundles, se trouvent dans ReplicatedStorage.Bundles.Configs.Constants.

Les principales choses que vous pourriez vouloir ajuster pour répondre aux exigences de conception de votre expérience :

  • Identifiants d'actifs sonores
  • Durée de l'effet d'achat et couleurs des particules
  • Collapsibilité de l'affichage tête haute

De plus, vous pouvez trouver des chaînes pour traduction regroupées à un endroit : ReplicatedStorage.FeaturePackagesCore.Configs.TranslationStrings.

Personnaliser les composants UI

En modifiant les objets du pack, tels que les couleurs, la police et la transparence, vous pouvez ajuster la présentation visuelle de vos invites de bundle. Cependant, gardez à l'esprit que si vous déplacez des objets dans la hiérarchie, le code ne pourra pas les trouver, et vous devrez apporter des ajustements à votre code.

Une invite est composée de deux composants de haut niveau :

  • PromptItem – Le composant individuel répété pour chaque objet d'un bundle (image de l'objet, légende, nom, prix).
  • Prompt – La fenêtre d'invite elle-même.

L'affichage tête haute est également composé de deux composants :

  • HudItem – Un composant individuel qui représente chaque option de menu dans l'affichage tête haute.
  • Hud – À remplir programmatique avec HudItems.

Si vous voulez avoir un meilleur contrôle sur l'affichage tête haute, au lieu d'utiliser simplement l'interface utilisateur HUD existante dans ReplicatedStorage.Bundles.Objects.BundlesGui, vous pouvez déplacer des éléments pour répondre à vos propres exigences de conception. Assurez-vous simplement de mettre à jour le comportement du script client dans le script ReplicatedStorage.Bundles.Client.UIController.

Référence API

Types

RelativeTime

Une fois que le bundle RelativeTime est proposé à un joueur, il reste disponible jusqu'à ce que la durée s'écoule. Ce type s'affiche sur l'affichage tête haute du joueur et est automatiquement proposé lors des sessions futures jusqu'à ce que le bundle expire ou que le joueur l'achète.

Un exemple courant de ce type de bundle est une offre de pack de démarrage à usage unique qui s'affiche à tous les nouveaux joueurs pendant 24 heures.

NomTypeDescription
includeOfflineTimebool(Optionnel) Si ce n'est pas défini, seul le temps passé dans l'expérience comptera pour la durée restante de l'offre.
singleUsebool(Optionnel) Si ce n'est pas défini, l'achat peut être réactivé après son achat ou son expiration.

Si défini, une fois acheté ou expiré pour la première fois, il ne sera plus jamais proposable, même si vous appelez Bundles.promptIfValidAsync avec le bundleId.

FixedTime

Une fois que le bundle FixedTime est proposé à un joueur, il reste disponible jusqu'à la fin de l'heure universelle coordonnée (UTC) définie. Ce type s'affiche sur l'affichage tête haute du joueur et est automatiquement proposé lors des sessions futures jusqu'à ce que le bundle expire ou que le joueur l'achète.

Un exemple courant de ce type de bundle est une offre de vacances qui n'est disponible que pour un mois donné.

OneTime

Un bundle OneTime n'est disponible que au moment où il est proposé à un joueur. Il ne s'affiche pas sur l'affichage tête haute du joueur, et une fois qu'un joueur ferme l'invite, elle ne peut pas être rouverte jusqu'à ce qu'elle soit à nouveau proposée par le serveur.

Un exemple courant de ce type de bundle est une offre pour acheter plus de monnaie en expérience au moment où un joueur en manque.

©2026 Société Roblox. Roblox, le logo Roblox et Powering Imagination font partie de nos marques déposées aux États-Unis et dans d'autres pays.