Paquet 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 paquet de fonctionnalités Bundles offre une fonctionnalité prête à l'emploi pour vendre des collections d'objets aux joueurs à prix réduit. Vous pouvez choisir de permettre aux joueurs d'acheter des bundles en utilisant une monnaie personnalisée dans le jeu ou des Robux, quel type de bundle vous souhaitez utiliser, quel ensemble d'objets vous souhaitez vendre et comment vous souhaitez inciter les joueurs pendant leur gameplay.

En utilisant les options de personnalisation du paquet, vous pouvez adapter vos bundles pour répondre aux objectifs de conception et de monétisation de vos jeux, tels que :

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

Obtenir le paquet

Le Creator Store est un onglet de la Toolbox que vous pouvez utiliser pour trouver tous les actifs créés par Roblox et la communauté Roblox pour une utilisation dans vos projets, y compris des modèles, des images, des maillages, des audio, des plugins, des vidéos et des polices. Vous pouvez utiliser le Creator Store pour ajouter un ou plusieurs actifs directement dans un jeu ouvert, y compris des paquets de fonctionnalités !

Chaque paquet de fonctionnalités nécessite le paquet de fonctionnalités Core pour fonctionner correctement. Une fois que les actifs des paquets 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 paquets de votre inventaire dans votre jeu :

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

  2. Dans le menu Fenêtre de Studio ou la barre d'outils de l'onglet Accueil, ouvrez la Toolbox.

  3. Dans la fenêtre Toolbox, cliquez sur l'onglet Inventaire. Le tri Mes modèles s'affiche.

    Fenêtre Toolbox de Studio avec l'onglet Inventaire en surbrillance.
  4. Cliquez sur le carreau Paquet de fonctionnalités Core, puis sur le carreau Paquet de fonctionnalités Bundles. Les deux dossiers de paquets s'affichent dans la fenêtre Explorer.

  5. Faites glisser les dossiers de paquets dans ReplicatedStorage.

  6. Autorisez les appels au magasin de données pour suivre les achats des joueurs avec les paquets.

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

Définir les devises

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

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

Le script Currencies indique au paquet de fonctionnalités Core certaines métadonnées sur votre devise :

  • (obligatoire) displayName - Le nom de votre devise. 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 texte à utiliser comme icône pour votre devise, cela est utilisé à la place du displayName dans les boutons d'achat (c'est-à-dire "💎100").
  • (optionnel) icon - Si vous avez un AssetId d'image icône pour votre devise, cela est 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 devise configurée, vous devez spécifier manuellement le prix du bundle, la devise et l'icône pour l'affichage au lieu que ces informations soient récupérées à 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 allons récupérer le prix et l'icône du bundle à partir du produit développeur
pricing = {
priceType = CurrencyTypes.PriceType.Marketplace,
devProductId = 1795621566,
},
-- Sinon, si vous souhaitez utiliser une devise dans le jeu au lieu d'un produit développeur, vous pouvez utiliser ce qui suit :
-- Le prix ici est en devise dans le jeu, 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 devises pour acheter le bundle
-- Mettez à jour les données du joueur, donnez des objets, etc.
-- Déduisez la devise 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 devises dans le jeu 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 à l'intérieur de l'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 jeu 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 jeu. C'est ce qui sera demandé 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 jeu, vous n'avez pas besoin de définir explicitement le prix/l'assetId/le nom de l'objet, qui seront récupérés 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'objet :

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 allons récupérer le prix et l'icône du bundle à partir du produit développeur
pricing = {
priceType = CurrencyTypes.PriceType.Marketplace,
devProductId = <DEV_PRODUCT_ID>,
},
-- Sinon, si vous souhaitez utiliser une devise dans le jeu au lieu d'un produit développeur, vous pouvez utiliser ce qui suit :
-- Le prix ici est en devise dans le jeu, 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'UI 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é, il n'est plus valide même si votre jeu essaie de le proposer (onPlayerAdded). Vous pouvez mettre cela à false pendant les tests dans le studio.
durationInSeconds = 900, -- 15 minutes
includesOfflineTime = false, -- Ne comptez que le temps écoulé dans le jeu
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 paquet de fonctionnalités Bundles et les méthodes ci-dessus sur le ModuleScript. Les extraits ci-dessous proviennent de ce script.

Vous devez principalement connecter quatre choses une fois que vous avez glissé le paquet de fonctionnalités Bundles dans votre jeu :

  1. Connectez les gestionnaires d'achat via Bundles.setPurchaseHandler pour spécifier les fonctions à appeler pour attribuer des objets lorsqu'un 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 a 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 devises pour acheter le bundle
    -- Mettez à jour les données du joueur, donnez des objets, etc.
    -- Déduisez la devise 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 devises dans le jeu 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 peut être fait ailleurs si votre jeu a déjà des produits développeurs à vendre. Essentiellement, lorsqu'un reçu de produit développeur est en cours de traitement, ils appelleront maintenant Bundles.getBundleByDevProduct pour vérifier si le produit appartient à un bundle. Si c'est le cas, le script appelle ensuite 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 est réussie
    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 paquet de fonctionnalités Bundles redemande tous les 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 s'occupera de vérifier si le joueur a déjà acheté ou si c'est expiré puisque ce n'est pas répétable
    -- Bundles.promptIfValidAsync(player, "StarterBundle")
    -- Appeler cela ici juste à titre d'exemple, vous pouvez appeler cela quand vous le souhaitez
    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 paquet de fonctionnalités Bundles garantit que chaque joueur ne reçoit pas une offre répétée s'il a déjà acheté le bundle, ou s'il a laissé l'offre expirer (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 n'importe quel événement de jeu que vous souhaitez utiliser pour déterminer quand un joueur reçoit le bundle
    -- ... Cela sera chaque fois que vous aurez rempli vos critères d'éligibilité pour proposer un 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, utilisez task.spawn() pour envelopper l'appel de fonction ci-dessus minimisera les écarts entre les compte à rebours
    end

Considérez les conseils de bonnes pratiques suivants sur les enregistrements redondants des ReceiptIds :

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

  • Le paquet de fonctionnalités Bundles n'enregistrera 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 dans le cadre de votre purchaseHandler.

  • Cette redondance aide à garantir que toute la logique d'achat a été correctement gérée et que votre magasin de données et le magasin de données du paquet 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 paquet de fonctionnalités Core se trouvent à deux endroits :

  • Les constantes partagées se trouvent dans ReplicatedStorage.FeaturePackagesCore.Configs.SharedConstants.

  • Les constantes spécifiques au paquet, dans ce cas le paquet 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 jeu :

  • Identifiants d'actifs sonores
  • Durée de l'effet d'achat et couleurs des particules
  • Capacité de réduction de l'affichage

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

Personnaliser les composants UI

En modifiant les objets du paquet, 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 l'un 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 dans un bundle (image de l'objet, légende, nom, prix).
  • Prompt – La fenêtre d'invite elle-même.

L'affichage est également composé de deux composants :

  • HudItem – Un composant individuel qui représente chaque option de menu dans l'affichage.
  • Hud – À remplir de manière programmatique avec HudItems.

Si vous souhaitez avoir un meilleur contrôle sur l'affichage, au lieu d'utiliser simplement l'UI HUD existante dans ReplicatedStorage.Bundles.Objects.BundlesGui, vous pouvez déplacer les é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 définie expire. Ce type s'affiche sur l'affichage du joueur et invite automatiquement 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 non défini, seul le temps passé dans le jeu comptera pour la durée restante de l'offre.
singleUsebool(Optionnel) Si non défini, l'achat peut être réactivé après son achat ou son expiration.

Si défini, une fois acheté ou expiré la première fois, il ne pourra plus jamais être proposé, 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 du joueur et invite automatiquement 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 pendant 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 du joueur, et une fois qu'un joueur ferme l'invite, elle ne peut pas être rouverte jusqu'à ce qu'elle soit proposée à nouveau par le serveur.

Un exemple courant de ce type de bundle est une offre pour acheter plus de devises dans le jeu 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.