Das Bundles-Feature-Paket bietet sofort einsatzbereite Funktionen, um Sammlungen von Gegenständen zu einem Rabatt an Spieler zu verkaufen. Sie können wählen, ob Sie es den Spielern erlauben möchten, Bundles mit einer benutzerdefinierten In-Game-Währung oder Robux zu kaufen, welchen Bundle-Typ Sie verwenden möchten, welche Gegenstände Sie verkaufen möchten und wie Sie die Spieler während ihres Spiels ansprechen möchten.
Mit den Anpassungsoptionen des Pakets können Sie Ihre Bundles an die Design- und Monetarisierungsziele Ihrer Spiele anpassen, wie zum Beispiel:
- Zielgerichtete Konversionsrate durch das Angebot von rabattierten Starter-Paketen, die neuen Spielern einen Mehrwert bieten und frühe Ausgaben fördern.
- Erhöhung der Ausgabetiefe durch Bündelung von Gegenständen zu verschiedenen Preisniveaus, um eine Vielzahl von Spielern anzusprechen.
- Monetarisierung von Live-Operationen (LiveOps) Events durch das Angebot von zeitlich begrenzten Bundles mit exklusiven Gegenständen.

Paket erhalten
Der Creator Store ist ein Tab des Toolbox, den Sie verwenden können, um alle von Roblox und der Roblox-Community für Ihre Projekte erstellten Assets zu finden, einschließlich Modell-, Bild-, Mesh-, Audio-, Plugin-, Video- und Schriftarten-Assets. Sie können den Creator Store verwenden, um ein oder mehrere Assets direkt in ein offenes Spiel hinzuzufügen, einschließlich Feature-Paketen!
Jedes Feature-Paket benötigt das Core-Feature-Paket, um ordnungsgemäß zu funktionieren. Sobald die Core- und Bundles-Feature-Paket-Assets in Ihrem Inventar sind, können Sie sie in jedem Projekt auf der Plattform wiederverwenden.
Um die Pakete von Ihrem Inventar in Ihr Spiel zu bekommen:
Fügen Sie das Core- und Bundles-Feature-Paket zu Ihrem Inventar innerhalb von Studio hinzu, indem Sie auf den Link In Inventar hinzufügen in der folgenden Komponentengruppe klicken.
Öffnen Sie im Fenster-Menü oder in der Startseite-Tab-Leiste den Toolbox.
Klicken Sie im Toolbox-Fenster auf den Tab Inventar. Die Sortierung Meine Modelle wird angezeigt.

Klicken Sie auf die Kachel Feature-Paket Core und dann auf die Kachel Bundle-Feature-Paket. Beide Paketordner werden im Explorer-Fenster angezeigt.
Ziehen Sie die Paketordner in ReplicatedStorage.
Erlauben Sie Datenbankaufrufe, um die Käufe der Spieler mit den Paketen zu verfolgen.
- Öffnen Sie das Datei ⟩ Erlebnis-Einstellungen-Fenster von Studio.
- Navigieren Sie zum Tab Sicherheit und aktivieren Sie Studio-Zugriff auf API-Dienste aktivieren.
Währungen definieren
Wenn Ihr Spiel ein eigenes Währungssystem hat, können Sie diese mit dem Core-Feature-Paket registrieren, indem Sie sie in ReplicatedStorage.FeaturePackagesCore.Configs.Currencies definieren. In dieser Datei gibt es bereits ein auskommentiertes Beispiel für eine Gems-Währung; ersetzen Sie es durch Ihre eigene.
Gems = {
displayName = "Edelsteine",
symbol = "💎",
icon = nil,
},Das Currencies-Skript informiert das Core-Feature-Paket über einige Metadaten zu Ihrer Währung:
- (erforderlich) displayName - Der Name Ihrer Währung. Wenn Sie kein Symbol oder Icon angeben, wird dieser Name in den Kauf-Buttons verwendet (d.h. "100 Edelsteine").
- (optional) symbol - Wenn Sie ein Textzeichen haben, das als Icon für Ihre Währung verwendet werden soll, wird dies anstelle des displayName in den Kauf-Buttons verwendet (d.h. "💎100").
- (optional) icon - Wenn Sie ein AssetId-Bild-Icon für Ihre Währung haben, wird dies anstelle des displayName in den Kauf-Buttons verwendet (d.h. das Bild wird links vom Preis "🖼️100" platziert).
Sobald Ihre Währung eingerichtet ist, müssen Sie manuell den Preis, die Währung und das Icon des Bundles für die Anzeige angeben, anstatt dass diese Informationen vom zugehörigen Entwicklerprodukt des Bundles abgerufen werden.
-- Wenn Sie ein Entwicklerprodukt verwenden möchten, müssen Sie eine eindeutige devProductId angeben, die nur von einem Bundle verwendet wird.
-- Wir werden den Bundle-Preis und das Icon vom Entwicklerprodukt abrufen
pricing = {
priceType = CurrencyTypes.PriceType.Marketplace,
devProductId = 1795621566,
},
-- Andernfalls, wenn Sie stattdessen In-Game-Währung anstelle eines Entwicklerprodukts verwenden möchten, können Sie Folgendes verwenden:
-- Der Preis hier ist in der In-Game-Währung, nicht in Robux
pricing = {
priceType = CurrencyTypes.PriceType.InExperience,
price = 79,
currencyId = "Gems",
icon = 18712203759,
},Sie müssen auch das BundlesExample-Skript referenzieren, um setInExperiencePurchaseHandler aufzurufen.
local function awardInExperiencePurchase(
_player: Player,
_bundleId: Types.BundleId,
_currencyId: CurrencyTypes.CurrencyId,
_price: number
)
-- Überprüfen Sie, ob der Spieler genügend Währung hat, um das Bundle zu kaufen
-- Aktualisieren Sie die Spieldaten, geben Sie Gegenstände, usw.
-- Ziehen Sie die Währung vom Spieler ab
task.wait(2)
return true
end
local function initializePurchaseHandlers()
local bundles = Bundles.getBundles()
for bundleId, bundle in bundles do
-- Das Bundle ist nicht mit einem Entwicklerprodukt verbunden, wenn es keinen Marktplatz-Preis-Typ hat
if not bundle or bundle.pricing.priceType ~= "Marketplace" then
continue
end
Bundles.setPurchaseHandler(bundleId, awardMarketplacePurchase)
receiptHandlers[bundle.pricing.devProductId] = receiptHandler
end
-- Wenn Sie In-Game-Währungen verwenden, die Sie für Bundles verwenden, setzen Sie den Handler hier
for currencyId, _ in Currencies do
Bundles.setInExperiencePurchaseHandler(currencyId, awardInExperiencePurchase)
end
endInsbesondere müssen Sie awardInExperiencePurchase ausfüllen, das durch eine Schleife durch Currencies innerhalb des Beispiel-initializePurchaseHandlers aufgerufen wird (d.h. jede currencyId ist über Bundles.setInExperiencePurchaseHandler(currencyId, awardInExperiencePurchase) mit dem Handler verbunden).
Bundles definieren
Alle in Ihrem Spiel angebotenen Bundles können innerhalb von ReplicatedStorage.Bundles.Configs.Bundles definiert werden, mit Typen, die aus dem Types-Skript im selben Ordner exportiert werden.
Wenn Sie eine devProductId verwenden, müssen Sie die Haupt-devProductId des Bundles aktualisieren, um mit der in Ihrem Spiel übereinzustimmen. Dies wird über MarketplaceService zum Kauf des Bundles aufgefordert. Es wird dringend empfohlen, ein neues Entwicklerprodukt für das Bundle zu verwenden, um separate Verkäufe leichter verfolgen zu können.
Wenn Sie ein Bundle mit mehreren Gegenständen möchten und diese bereits durch Entwicklerprodukte in Ihrem Spiel dargestellt werden, müssen Sie den Preis/assetId/Namen der Gegenstände nicht explizit festlegen, da diese über die Produktinformationen abgerufen werden:
{
itemType = ItemTypes.ItemType.DevProduct,
devProductId = <DEV_PRODUCT_ID>,
metadata = {
caption = {
text = "x1",
color = Color3.fromRGB(236, 201, 74),
} -- Die Beschriftung ist optional! Sie können dieses Feld auch weglassen
}
},Andernfalls können Sie diese Einzelheiten manuell konfigurieren:
{
itemType = ItemTypes.ItemType.Robux,
priceInRobux = 49,
icon = <IMAGE_ASSET_ID>,
metadata = {
caption = {
text = "x1",
color = Color3.fromRGB(236, 201, 74),
} -- Die Beschriftung ist optional! Sie können dieses Feld auch weglassen
}
},Zum Beispiel wird Ihr gesamtes Bundle wahrscheinlich so aussehen:
local starterBundle: Types.RelativeTimeBundle = {
bundleType = Types.BundleType.RelativeTime,
-- Wenn Sie ein Entwicklerprodukt verwenden möchten, müssen Sie eine eindeutige devProductId angeben, die nur von einem Bundle verwendet wird.
-- Wir werden den Bundle-Preis und das Icon vom Entwicklerprodukt abrufen
pricing = {
priceType = CurrencyTypes.PriceType.Marketplace,
devProductId = <DEV_PRODUCT_ID>,
},
-- Andernfalls, wenn Sie stattdessen In-Game-Währung anstelle eines Entwicklerprodukts verwenden möchten, können Sie Folgendes verwenden:
-- Der Preis hier ist in der In-Game-Währung, nicht in Robux
-- pricing = {
-- priceType = CurrencyTypes.PriceType.InExperience,
-- price = 79,
-- currencyId = <CURRENCY_ID>,
-- icon = <IMAGE_ASSET_ID>,
-- },
includedItems = {
[1] = {
-- Der Gegenstand selbst wird nicht über ein Entwicklerprodukt verkauft, also geben Sie an, wie viel er in Robux wert ist und geben Sie ein Icon an
-- Der priceInRobux hilft Bundles, den relativen Wert des Bundle-Preises im Vergleich zur Summe seiner Inhalte anzuzeigen
itemType = ItemTypes.ItemType.Robux,
priceInRobux = 49,
icon = <IMAGE_ASSET_ID>,
-- Alternativ, wenn dies ein Entwicklerprodukt hat, lassen Sie den Preis und das Icon oben weg und setzen Sie einfach die devProductId
-- Der Preis und das Icon werden vom Entwicklerprodukt abgerufen
-- devProductId = <ITEM_DEV_PRODUCT_ID>
-- Es gibt mehr optionale Metadatenfelder, die UI-spezifisch sind, falls erforderlich
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, -- Nach dem Kauf oder Ablauf nicht mehr gültig, auch wenn Ihr Spiel versucht, es anzuzeigen (onPlayerAdded). Sie können dies während des Testens im Studio auf false setzen.
durationInSeconds = 900, -- 15 Minuten
includesOfflineTime = false, -- Nur die im Spiel verstrichene Zeit zählt
metadata = {
displayName = "STARTER BUNDLE",
description = "Sparen Sie 75% und starten Sie durch!",
},
}Serverlogik integrieren
Werfen Sie einen Blick auf ReplicatedStorage.Bundles.Server.Examples.BundlesExample, das zeigt, wie Ihr Server mit dem Bundles-Feature-Paket und den oben genannten Methoden im ModuleScript interagiert. Die folgenden Snippets stammen aus diesem Skript.
Sie müssen hauptsächlich vier Dinge anschließen, nachdem Sie das Bundles-Feature-Paket in Ihr Spiel gezogen haben:
Verbinden Sie Kauf-Handler über Bundles.setPurchaseHandler, um die Funktionen anzugeben, die aufgerufen werden sollen, um Gegenstände zu vergeben, wenn ein Kauf verarbeitet wird.
BundlesExamplelocal function awardMarketplacePurchase(_player: Player, _bundleId: Types.BundleId, _receiptInfo: { [string]: any })-- Aktualisieren Sie die Spieldaten, geben Sie Gegenstände, usw.-- ... UND zeichnen Sie receiptInfo.PurchaseId auf, damit wir überprüfen können, ob der Benutzer dieses Bundle bereits hattask.wait(2)return Enum.ProductPurchaseDecision.PurchaseGrantedendlocal function awardInExperiencePurchase(_player: Player,_bundleId: Types.BundleId,_currencyId: CurrencyTypes.CurrencyId,_price: number)-- Überprüfen Sie, ob der Spieler genügend Währung hat, um das Bundle zu kaufen-- Aktualisieren Sie die Spieldaten, geben Sie Gegenstände, usw.-- Ziehen Sie die Währung vom Spieler abtask.wait(2)return trueendlocal function initializePurchaseHandlers()local bundles = Bundles.getBundles()for bundleId, bundle in bundles do-- Das Bundle ist nicht mit einem Entwicklerprodukt verbunden, wenn es keinen Marktplatz-Preis-Typ hatif not bundle or bundle.pricing.priceType ~= "Marketplace" thencontinueendBundles.setPurchaseHandler(bundleId, awardMarketplacePurchase)receiptHandlers[bundle.pricing.devProductId] = receiptHandlerend-- Wenn Sie In-Game-Währungen verwenden, die Sie für Bundles verwenden, setzen Sie den Handler hierfor currencyId, _ in Currencies doBundles.setInExperiencePurchaseHandler(currencyId, awardInExperiencePurchase)endendVerbinden Sie Ihre Logik für MarketplaceService.ProcessReceipt, dies kann jedoch an anderer Stelle erfolgen, wenn Ihr Spiel bereits Entwicklerprodukte zum Verkauf hat. Im Wesentlichen, wenn ein Entwicklerproduktbeleg verarbeitet wird, wird jetzt Bundles.getBundleByDevProduct aufgerufen, um zu überprüfen, ob das Produkt zu einem Bundle gehört. Wenn ja, ruft das Skript dann Bundles.processReceipt auf.
BundlesExample-- Beleg vom Marktplatz verarbeiten, um zu bestimmen, ob der Spieler berechnet werden muss oder nichtlocal 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] -- Holen Sie sich den Handler für das Produktlocal success, result = pcall(handler, receiptInfo, player) -- Rufen Sie den Handler auf, um zu überprüfen, ob die Kauf-Logik erfolgreich istif not success or not result thenwarn("Beleg konnte nicht verarbeitet werden:", 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-- Dieser Kauf gehört zu einem Bundle, lassen Sie Bundles es behandelnlocal purchaseDecision = Bundles.processReceiptAsync(player, bundleId, receiptInfo)return purchaseDecision == Enum.ProductPurchaseDecision.PurchaseGrantedend-- Dieser Kauf gehört nicht zu einem Bundle,-- ... Behandeln Sie hier Ihre gesamte vorhandene Logik, falls Sie welche habenreturn falseendVerbinden Sie Players.PlayerAdded:Connect(Bundles.OnPlayerAdded), damit das Bundles-Feature-Paket alle aktiven Bundles, die noch nicht abgelaufen sind, für einen Spieler erneut anzeigt.
READMElocal function onPlayerAdded(player: Player)-- Informieren Sie Bundles, wenn der Spieler beitritt, damit es seine Daten neu laden kannBundles.onPlayerAdded(player)-- Wenn Sie ein Starter-Bundle hatten, das Sie allen neuen Benutzern anbieten wollten, könnten Sie das hier ansprechen-- ... Bundles wird behandeln, ob der Spieler es bereits gekauft hat oder ob es abgelaufen ist, da es nicht wiederholbar ist-- Bundles.promptIfValidAsync(player, "StarterBundle")-- Dies hier nur als Beispiel aufrufen, Sie können dies jederzeit oder überall aufrufen, wo Sie möchtenonPromptBundleXYZEvent(player)endBundles ansprechen. Während dies vom Gameplay abhängt, zeigt das Beispiel die Spieler mit einem StarterBundle onPlayerAdded an.
Die Logik des Bundles-Feature-Pakets stellt sicher, dass jeder Spieler kein wiederholtes Angebot erhält, wenn er das Bundle bereits gekauft hat oder wenn das Angebot bereits abgelaufen ist (basierend auf der Bundle-Konfiguration).
Wann immer Sie ein Bundle einem Spieler anbieten möchten, rufen Sie Bundles.promptIfValidAsync(player, bundleId) auf.
READMElocal function onPromptBundleXYZEvent(player: Player)-- Verbinden Sie jedes Spielereignis, das Sie verwenden möchten, um zu bestimmen, wann ein Spieler das Bundle angeboten bekommt-- ... Dies wird immer dann sein, wenn Sie Ihre Berechtigungskriterien erfüllt haben, um einem Spieler das Bundle anzubieten-- ... Zum Beispiel, wenn Sie ein Bundle anbieten möchten, wenn ein Spieler beitritt oder wenn ein Spieler aufsteigttask.spawn(Bundles.promptIfValidAsync, player, <Some_Bundle_Id>)-- ... Wenn Sie mehrere Bundles erstellen, wird die Verwendung von task.spawn(), um den obigen Funktionsaufruf zu umschließen, Diskrepanzen zwischen Countdown zu minimierenend
Berücksichtigen Sie die folgenden bewährten Praktiken zur redundanten Aufzeichnung von ReceiptIds:
Während das Bundles-Feature-Paket ReceiptIds aufzeichnet, um zu vermeiden, dass derselbe Beleg zweimal verarbeitet wird, sollten Sie auch ReceiptIds in Ihren Tabellen aufzeichnen, damit Sie im Falle eines Fehlers im Kaufablauf nach Abschluss des Kaufhandlers wissen, dass Sie bei einem späteren Versuch keine Gegenstände erneut vergeben.
Das Bundles-Feature-Paket wird die ReceiptId nicht aufzeichnen, wenn der Kauf in einem Schritt fehlschlägt, daher sollten Sie sicherstellen, dass Sie die ReceiptId in Ihren Tabellen aufzeichnen, bevor Sie den Beleg als Teil Ihres purchaseHandlers verarbeiten.
Diese Redundanz hilft sicherzustellen, dass alle Kauf-Logik angemessen behandelt wurde und dass Ihre Datenbank und die Datenbank des Bundles-Feature-Pakets letztendlich konsistent sind, wobei Ihre Datenbank die Quelle der Wahrheit ist.
Konstanten konfigurieren
Konstanten für das Core-Feature-Paket befinden sich an zwei Stellen:
Gemeinsame Konstanten befinden sich in ReplicatedStorage.FeaturePackagesCore.Configs.SharedConstants.
Paket-spezifische Konstanten, in diesem Fall das Bundles-Feature-Paket, befinden sich in ReplicatedStorage.Bundles.Configs.Constants.
Die Hauptsachen, die Sie möglicherweise anpassen möchten, um die Designanforderungen Ihres Spiels zu erfüllen:
- Sound-Asset-IDs
- Dauer und Partikelfarben der Kauf-Effekte
- Zusammenklappbarkeit der Heads-up-Anzeige
Darüber hinaus finden Sie Strings zur Übersetzung an einem Ort aufgeteilt: ReplicatedStorage.FeaturePackagesCore.Configs.TranslationStrings.
UI-Komponenten anpassen
Durch die Modifizierung der Paketobjekte, wie Farben, Schriftarten und Transparenz, können Sie die visuelle Präsentation Ihrer Bundle-Angebote anpassen. Beachten Sie jedoch, dass, wenn Sie eines der Objekte hierarchisch verschieben, der Code sie nicht finden kann und Sie Anpassungen an Ihrem Code vornehmen müssen.
Ein Prompt besteht aus zwei hochrangigen Komponenten:
- PromptItem – Die einzelne Komponente, die für jeden Gegenstand innerhalb eines Bundles wiederholt wird (Gegenstandsbild, Beschriftung, Name, Preis).
- Prompt – Das Prompt-Fenster selbst.
Die Heads-up-Anzeige besteht ebenfalls aus zwei Komponenten:
- HudItem – Eine einzelne Komponente, die jede Menüoption in der Heads-up-Anzeige darstellt.
- Hud – Um programmgesteuert mit HudItems gefüllt zu werden.
Wenn Sie mehr Kontrolle über die Heads-up-Anzeige haben möchten, anstatt nur die vorhandene HUD-UI innerhalb von ReplicatedStorage.Bundles.Objects.BundlesGui zu verwenden, können Sie die Dinge umstellen, um Ihren eigenen Designanforderungen gerecht zu werden. Stellen Sie nur sicher, dass Sie das Verhalten des Clientskripts im ReplicatedStorage.Bundles.Client.UIController-Skript aktualisieren.
API-Referenz
Typen
RelativeTime
Sobald das RelativeTime-Bundle einem Spieler angeboten wird, bleibt es verfügbar, bis die Zeitdauer abläuft. Dieser Typ wird in der Heads-up-Anzeige des Spielers angezeigt und fordert automatisch in zukünftigen Sitzungen an, bis das Bundle abläuft oder der Spieler es kauft.
Ein häufiges Beispiel für diesen Bundle-Typ ist ein einmaliges Starter-Paket-Angebot, das allen neuen Spielern für 24 Stunden angezeigt wird.
| Name | Typ | Beschreibung |
|---|---|---|
| includeOfflineTime | bool | (Optional) Wenn nicht festgelegt, zählt nur die im Spiel verbrachte Zeit zur verbleibenden Angebotsdauer. |
| singleUse | bool | (Optional) Wenn nicht festgelegt, kann der Kauf nach dem Kauf oder Ablauf reaktiviert werden. Wenn festgelegt, wird es nach dem ersten Kauf oder Ablauf niemals wieder angeboten, selbst wenn Sie Bundles.promptIfValidAsync mit der bundleId aufrufen. |
FixedTime
Sobald das FixedTime-Bundle einem Spieler angeboten wird, bleibt es bis zum Ende der festgelegten koordinierten Weltzeit (UTC) verfügbar. Dieser Typ wird in der Heads-up-Anzeige des Spielers angezeigt und fordert automatisch in zukünftigen Sitzungen an, bis das Bundle abläuft oder der Spieler es kauft.
Ein häufiges Beispiel für diesen Bundle-Typ ist ein Feiertagsangebot, das nur für einen bestimmten Monat verfügbar ist.
OneTime
Ein OneTime-Bundle ist nur in dem Moment verfügbar, in dem es einem Spieler angeboten wird. Es wird nicht in der Heads-up-Anzeige des Spielers angezeigt, und sobald ein Spieler das Prompt schließt, kann es nicht erneut geöffnet werden, bis es erneut vom Server angeboten wird.
Ein häufiges Beispiel für diesen Bundle-Typ ist ein Angebot, mehr In-Game-Währung zu kaufen, in dem Moment, in dem ein Spieler kein Geld mehr hat.