Pakiet funkcyjny Bundles oferuje gotową funkcjonalność do sprzedaży kolekcji przedmiotów graczom ze zniżką. Możesz wybrać, czy zezwolić graczom na zakup bundli za pomocą niestandardowej waluty w doświadczeniu lub Robuxów, jaki typ bundla chcesz użyć, jaki zestaw przedmiotów chcesz sprzedać oraz jak chcesz zachęcać graczy podczas ich rozgrywki.
Korzystając z opcji dostosowywania pakietu, możesz dostosować swoje bundlby, aby spełniały cele projektowe i monetizacyjne Twoich doświadczeń, takie jak:
- Celowanie w niski wskaźnik konwersji poprzez oferowanie zniżkowych pakietów startowych, które zapewniają wartość nowym graczom i zachęcają do wczesnego wydawania pieniędzy.
- Zwiększanie głębi wydatków poprzez bundlowanie przedmiotów w różnych przedziałach cenowych, aby przyciągnąć różne grupy graczy.
- Monetyzacja operacji na żywo (LiveOps) wydarzeń poprzez oferowanie ograniczonych czasowo bundli ekskluzywnych przedmiotów.

Uzyskaj pakiet
Creator Store to zakładka Toolbox, którą możesz wykorzystać do znalezienia wszystkich zasobów stworzonych przez Roblox i społeczność Roblox do użytku w swoich projektach, w tym modelach, obrazach, siatkach, audio, wtyczkach, filmach i czcionkach. Możesz użyć Creator Store, aby dodać jeden lub więcej zasobów bezpośrednio do otwartego doświadczenia, w tym pakiety funkcji!
Każdy pakiet funkcji wymaga funkcji Core, aby działał poprawnie. Po dodaniu zasobów pakietu Core i Bundles do swojego ekwipunku możesz je ponownie wykorzystać w dowolnym projekcie na platformie.
Aby uzyskać pakiety z ekwipunku do swojego doświadczenia:
Dodaj pakiet funkcji Core i Bundles do swojego ekwipunku w Studio, klikając link Dodaj do ekwipunku w poniższym zestawie komponentów.
Z menu Window w Studio lub pasku narzędzi na zakładce Home, otwórz Toolbox.
W oknie Toolbox kliknij zakładkę Ekwipunek. Wyświetli się sortowanie Moje modele.

Kliknij kafelek Pakiet funkcji Core, a następnie kafelek Pakiet funkcji Bundles. Oba foldery pakietów będą widoczne w oknie Explorer.
Przeciągnij foldery pakietów do ReplicatedStorage.
Zezwól na wywołania do przechowywania danych, aby śledzić zakupy graczy z pakietami.
- Otwórz okno File ⟩ Ustawienia doświadczenia w Studio.
- Przejdź do zakładki Bezpieczeństwo, a następnie włącz Włącz dostęp Studio do usług API.
Zdefiniuj waluty
Jeśli twoje doświadczenie ma własny system walutowy, możesz je zarejestrować za pomocą pakietu funkcji Core, definiując je w ReplicatedStorage.FeaturePackagesCore.Configs.Currencies. Jest tam zakomentowany przykład waluty Gemsy, który możesz zastąpić swoją własną.
WalutyGems = {displayName = "Gems",symbol = "💎",icon = nil,},
Skrypt Waluty informuje pakiet funkcji Core o pewnych metadanych dotyczących twojej waluty:
- (wymagane) displayName - Nazwa twojej waluty. Jeśli nie określisz symbolu lub ikony, ta nazwa jest używana w przyciskach zakupu (tj. "100 Gems").
- (opcjonalne) symbol - Jeśli masz znak tekstowy, który chcesz użyć jako ikonę dla swojej waluty, to jest używane zamiast displayName w przyciskach zakupu (tj. "💎100").
- (opcjonalne) icon - Jeśli masz AssetId obrazu ikony dla swojej waluty, to jest używane zamiast displayName w przyciskach zakupu (tj. obraz będzie umieszczony po lewej stronie ceny "🖼️100")
Po skonfigurowaniu waluty, musisz ręcznie określić cenę bundla, walutę i ikonę dla wyświetlania zamiast ściągania tych informacji z powiązanego produktu dewelopera bundla.
Bundles-- Jeśli chcesz użyć produktu dewelopera, musisz podać unikalne devProductId, jedynie używane przez jeden bundel.-- Pobierzemy cenę bundla i ikonę z produktu deweloperapricing = {priceType = CurrencyTypes.PriceType.Marketplace,devProductId = 1795621566,},-- W przeciwnym razie, jeśli chcesz używać waluty w doświadczeniu zamiast produktu dewelopera, możesz użyć poniższego:-- Cena tutaj jest w walucie w doświadczeniu, a nie w Robuxpricing = {priceType = CurrencyTypes.PriceType.InExperience,price = 79,currencyId = "Gems",icon = 18712203759,},
Musisz również odwołać się do skryptu BundlesExample, aby wywołać setInExperiencePurchaseHandler.
BundlesExample
local function awardInExperiencePurchase(
_player: Player,
_bundleId: Types.BundleId,
_currencyId: CurrencyTypes.CurrencyId,
_price: number
)
-- Sprawdź, czy gracz ma wystarczająco dużo waluty, aby kupić bundel
-- Zaktualizuj dane gracza, daj przedmioty itd.
-- Odlicz walutę od gracza
task.wait(2)
return true
end
local function initializePurchaseHandlers()
local bundles = Bundles.getBundles()
for bundleId, bundle in bundles do
-- Bundel nie jest powiązany z produktem dewelopera, jeśli nie ma ceny rynkowej
if not bundle or bundle.pricing.priceType ~= "Marketplace" then
continue
end
Bundles.setPurchaseHandler(bundleId, awardMarketplacePurchase)
receiptHandlers[bundle.pricing.devProductId] = receiptHandler
end
-- Jeśli masz jakiekolwiek waluty w doświadczeniu, które używasz dla bundli, ustaw handler tutaj
for currencyId, _ in Currencies do
Bundles.setInExperiencePurchaseHandler(currencyId, awardInExperiencePurchase)
end
end
Specjalnie, musisz wypełnić awardInExperiencePurchase, który jest wywoływany przez pętlę przez Currencies wewnątrz przykładowego initializePurchaseHandlers (tj. każdy currencyId jest powiązany z handler przez Bundles.setInExperiencePurchaseHandler(currencyId, awardInExperiencePurchase)).
Zdefiniuj bundle
Wszystkie bundle, które można oferować w twoim doświadczeniu, mogą być zdefiniowane w ReplicatedStorage.Bundles.Configs.Bundles, a typy są eksportowane z skryptu Types w tym samym folderze.
Jeśli korzystasz z devProductId, musisz zaktualizować główne devProductId bundla, aby pasowało do tego, które masz w swoim doświadczeniu. To będzie wywoływanie przez MarketplaceService do zakupu bundla. Należy zdecydowanie zalecić używanie nowego produktu dewelopera dla bundla, aby ułatwić śledzenie oddzielnych sprzedaży.
Jeśli chcesz bundę z wieloma przedmiotami, a jeśli już są one reprezentowane przez produkty dewelopera w twoim doświadczeniu, nie musisz jawnie ustawiać ceny przedmiotów/assetId/nazwa, które będą pobierane przez informacje o produkcie:
README{itemType = ItemTypes.ItemType.DevProduct,devProductId = <DEV_PRODUCT_ID>,metadata = {caption = {text = "x1",color = Color3.fromRGB(236, 201, 74),} -- Napisy są opcjonalne! Możesz również pominąć to pole}},
W przeciwnym razie możesz ręcznie skonfigurować szczegóły tych przedmiotów:
README{itemType = ItemTypes.ItemType.Robux,priceInRobux = 49,icon = <IMAGE_ASSET_ID>,metadata = {caption = {text = "x1",color = Color3.fromRGB(236, 201, 74),} -- Napisy są opcjonalne! Możesz również pominąć to pole}},
Na przykład, twój cały bundel będzie prawdopodobnie wyglądać tak:
READMElocal starterBundle: Types.RelativeTimeBundle = {bundleType = Types.BundleType.RelativeTime,-- Jeśli chcesz użyć produktu dewelopera, musisz podać unikalne devProductId, używane jedynie przez jeden bundel.-- Pobierzemy cenę bundla i ikonę z produktu deweloperapricing = {priceType = CurrencyTypes.PriceType.Marketplace,devProductId = <DEV_PRODUCT_ID>,},-- W przeciwnym razie, jeśli chcesz używać waluty w doświadczeniu zamiast produktu dewelopera, możesz użyć poniższego:-- Cena tutaj jest walutą w doświadczeniu, a nie Robux-- pricing = {-- priceType = CurrencyTypes.PriceType.InExperience,-- price = 79,-- currencyId = <CURRENCY_ID>,-- icon = <IMAGE_ASSET_ID>,-- },includedItems = {[1] = {-- Przedmiot sam w sobie nie jest sprzedawany poprzez produkt dewelopera, więc wskaź ile jest wart w Robuxach i podaj ikonę-- priceInRobux pomaga Bundles pokazać względną wartość ceny bundla w porównaniu z sumą jego zawartościitemType = ItemTypes.ItemType.Robux,priceInRobux = 49,icon = <IMAGE_ASSET_ID>,-- Alternatywnie, jeśli to ma produkt dewelopera, pomiń cenę i ikonę powyżej, a po prostu ustaw devProductId-- Cena i ikona będą pobierane z produktu dewelopera-- devProductId = <ITEM_DEV_PRODUCT_ID>-- Istnieją więcej opcjonalnych pól metadanych, które są specyficzne dla UI, jeśli to potrzebnemetadata = {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, -- Po zakupie lub wygaszeniu, nie jest już ważny, nawet jeśli twoje doświadczenie spróbuje wyświetlić (onPlayerAdded). Możesz ustawić to na false podczas testowania w studio.durationInSeconds = 900, -- 15 minutincludesOfflineTime = false, -- Liczy się tylko czas spędzony w doświadczeniumetadata = {displayName = "PAKET STARTOWY",description = "Zaoszczędź 75% i zyskaj przewagę!",},}
Zintegruj logikę serwera
Spójrz na ReplicatedStorage.Bundles.Server.Examples.BundlesExample, który pokazuje, jak twój serwer będzie współpracował z pakietem funkcji Bundles i powyższymi metodami w ModuleScript. Poniższe fragmenty pochodzą z tego skryptu.
Musisz głównie podłączyć cztery rzeczy po przeciągnięciu pakietu funkcji Bundles do swojego doświadczenia:
Połącz procedury zakupu przez Bundles.setPurchaseHandler, aby określić funkcje do wywołania w celu przyznania przedmiotów podczas przetwarzania zakupu.
BundlesExamplelocal function awardMarketplacePurchase(_player: Player, _bundleId: Types.BundleId, _receiptInfo: { [string]: any })-- Zaktualizuj dane gracza, daj przedmioty itd.-- ... I zarejestruj receiptInfo.PurchaseId, aby można było sprawdzić, czy użytkownik już ma ten bundeltask.wait(2)return Enum.ProductPurchaseDecision.PurchaseGrantedendlocal function awardInExperiencePurchase(_player: Player,_bundleId: Types.BundleId,_currencyId: CurrencyTypes.CurrencyId,_price: number)-- Sprawdź, czy gracz ma wystarczająco dużo waluty, aby kupić bundel-- Zaktualizuj dane gracza, daj przedmioty itd.-- Odlicz walutę od graczatask.wait(2)return trueendlocal function initializePurchaseHandlers()local bundles = Bundles.getBundles()for bundleId, bundle in bundles do-- Bundel nie jest powiązany z produktem dewelopera, jeśli nie ma ceny rynkowejif not bundle or bundle.pricing.priceType ~= "Marketplace" thencontinueendBundles.setPurchaseHandler(bundleId, awardMarketplacePurchase)receiptHandlers[bundle.pricing.devProductId] = receiptHandlerend-- Jeśli masz jakiekolwiek waluty w doświadczeniu, które używasz dla bundli, ustaw handler tutajfor currencyId, _ in Currencies doBundles.setInExperiencePurchaseHandler(currencyId, awardInExperiencePurchase)endendPołącz swoją logikę z MarketplaceService.ProcessReceipt, ale może to być zrobione gdzie indziej, jeśli twoje doświadczenie ma już produkty dewelopera na sprzedaż. W zasadzie, gdy paragon produktu dewelopera jest przetwarzany, teraz wywołują Bundles.getBundleByDevProduct, aby sprawdzić, czy produkt należy do bundla. Jeśli tak, skrypt wywołuje Bundles.processReceipt.
BundlesExample-- Przetwarzaj paragon z rynku, aby określić, czy gracz musi być obciążony, czy nielocal 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] -- Uzyskaj handler dla produktulocal success, result = pcall(handler, receiptInfo, player) -- Wywołaj handler, aby sprawdzić, czy logika zakupu jest skutecznaif not success or not result thenwarn("Niepowodzenie przetwarzania paragonu:", 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-- Ten zakup należy do bundla, pozwól Bundles to obsłużyćlocal purchaseDecision = Bundles.processReceiptAsync(player, bundleId, receiptInfo)return purchaseDecision == Enum.ProductPurchaseDecision.PurchaseGrantedend-- Ten zakup nie należy do bundla,-- ... Obsłuż całą swoją istniejącą logikę tutaj, jeśli posiadaszreturn falseendPołącz Players.PlayerAdded:Connect(Bundles.OnPlayerAdded), aby pakiet funkcji Bundles ponownie wyświetlał wszystkie aktywne bundli, które jeszcze nie wygasły dla gracza.
READMElocal function onPlayerAdded(player: Player)-- Powiadom Bundles, gdy gracz dołączy, aby mógł przeładować ich daneBundles.onPlayerAdded(player)-- Jeśli miałeś jakiś pakiet startowy, który chciałeś oferować wszystkim nowym użytkownikom, możesz to tutaj wyświetlić-- ... Bundles obsłuży, jeśli gracz już go kupił lub jeśli wygasł, ponieważ nie jest powtarzalny-- Bundles.promptIfValidAsync(player, "StarterBundle")-- Wywołanie tego tutaj tylko jako przykład, możesz to wywołać whenever lub wherever chceszonPromptBundleXYZEvent(player)endWyświetlaj bundli. Chociaż to zależy od rozgrywki, przykład wyświetla graczom pakiet startowy onPlayerAdded.
Logika pakietu Bundles zapewnia, że każdy gracz nie otrzyma oferty ponownie, jeśli już kupił bundel, ani jeśli oferta już wygasła (na podstawie konfiguracji bundla).
Kiedy chcesz wyświetlić bundel graczowi, wywołaj Bundles.promptIfValidAsync(player, bundleId).
READMElocal function onPromptBundleXYZEvent(player: Player)-- Podłącz dowolne wydarzenie doświadczenia, które chcesz użyć, aby określić, kiedy gracz otrzymuje ofertę bundla-- ... To stanie się kiedykolwiek spełniasz kryteria kwalifikacji do zaproszenia gracza do bundla-- ... Na przykład, jeśli chcesz wyświetlić bundel, gdy gracz dołącza, lub gdy gracz osiąga wyższy poziomtask.spawn(Bundles.promptIfValidAsync, player, <Some_Bundle_Id>)-- ... Jeśli tworzysz wiele bundli, użycie task.spawn() do owijania powyższego wywołania funkcji zminimalizuje różnice między odliczeniamiend
Rozważ następujące zalecenia dotyczące redundantnego rejestrowania ReceiptIds:
Chociaż pakiet funkcji Bundles rejestruje ReceiptIds, aby uniknąć przetwarzania tego samego paragonu dwa razy, powinieneś również rejestrować ReceiptIds w swoich tabelach, aby na przyszłych ponowieniach wiedzieć, by nie przyznawać przedmiotów ponownie.
Pakiet funkcji Bundles nie zarejestruje ReceiptId, jeśli zakup nie powiedzie się na żadnym etapie, więc powinieneś upewnić się, że rejestrujesz ReceiptId w swoich tabelach, zanim przetworzysz paragon jako część swojego purchaseHandler.
Ta redundancja pomaga zapewnić, że wszystkie logiki zakupu zostały odpowiednio obsłużone i że twoje przechowalnia danych i przechowalnia danych pakietu Bundles osiągną ostateczną spójność, z twoją przechowalnią danych jako źródłem prawdy.
Skonfiguruj stałe
Stałe dla pakietu funkcji Core znajdują się w dwóch miejscach:
Wspólne stałe znajdują się w ReplicatedStorage.FeaturePackagesCore.Configs.SharedConstants.
Stałe specyficzne dla pakietu, w tym przypadku pakietu funkcji Bundles, znajdują się w ReplicatedStorage.Bundles.Configs.Constants.
Główne rzeczy, które możesz chcieć dostosować, aby spełniały wymagania projektowe twojego doświadczenia:
- Identyfikatory aktywów dźwiękowych
- Czas trwania efektu zakupu i kolory cząsteczek
- Zwijalność wyświetlania
Dodatkowo możesz znaleźć ciągi do tłumaczenia rozdzielone w jednym miejscu: ReplicatedStorage.FeaturePackagesCore.Configs.TranslationStrings.
Dostosuj komponenty UI
Poprzez modyfikację obiektów pakietu, takich jak kolory, czcionka i przezroczystość, możesz dostosować wizualną prezentację swoich zaproszeń na bundle. Jednak pamiętaj, że jeżeli przeniesiesz jakiekolwiek obiekty w hierarchii, kod nie będzie mógł ich znaleźć i będziesz musiał dostosować swój kod.
Zaproszenie składa się z dwóch komponentów o wysokim poziomie:
- PromptItem – Indywidualny komponent powtarzany dla każdego przedmiotu w bundlu (obraz przedmiotu, podpis, nazwa, cena).
- Prompt – Samo okno zaproszenia.
Wyświetlanie również składa się z dwóch komponentów:
- HudItem – Oddzielny komponent, który reprezentuje każdą opcję w wyświetlaniu.
- Hud – Do uzupełnienia programowo przez HudItems.
Jeśli chcesz mieć większą kontrolę nad wyświetlaniem, zamiast korzystać tylko z istniejącego UI HUD w ReplicatedStorage.Bundles.Objects.BundlesGui, możesz przenieść elementy, aby spełniały własne wymagania projektowe. Upewnij się tylko, że zaktualizujesz zachowanie skryptu klienta w skrypcie ReplicatedStorage.Bundles.Client.UIController.
Dokumentacja API
Typy
RelativeTime
Gdy bundel RelativeTime zostanie zaoferowany graczowi, pozostaje dostępny, dopóki czas trwania nie minie. Ten typ wyświetla się w wyświetlaniu gracza i automatycznie wyświetla się w kolejnych sesjach, dopóki bundel nie wygasł lub gracz go nie zakupi.
Typowym przykładem tego typu bundla jest oferta pakietu startowego jednokrotnego użytku, która wyświetla się wszystkim nowym graczom przez 24 godziny.
| Nazwa | Typ | Opis |
|---|---|---|
| includeOfflineTime | bool | (Opcjonalne) Jeśli nie jest ustawione, tylko czas spędzony w doświadczeniu będzie się liczył do pozostałego czasu oferty. |
| singleUse | bool | (Opcjonalne) Jeśli nie jest ustawione, zakup można ponownie aktywować po jego zakupie lub wygaśnięciu. Jeśli ustawione, po zakupie lub wygaśnięciu po raz pierwszy, nie będzie można go ponownie wyświetlić, nawet jeśli wywołasz Bundles.promptIfValidAsync z bundleId. |
FixedTime
Gdy bundel FixedTime zostanie zaoferowany graczowi, pozostaje dostępny do końca ustalonego czasu uniwersalnego koordynowanego (UTC). Ten typ wyświetla się w wyświetlaniu gracza i automatycznie wyświetla się w kolejnych sesjach, dopóki bundel nie wygasł lub gracz go nie zakupi.
Typowym przykładem tego typu bundla jest oferta świąteczna, która jest dostępna tylko przez dany miesiąc.
OneTime
Bundel OneTime jest dostępny tylko w momencie, gdy zostaje zaoferowany graczowi. Nie wyświetla się w wyświetlaniu gracza, a po zamknięciu zaproszenia nie może zostać ponownie otwarte, aż zostanie ponownie wywołane przez serwer.
Typowym przykładem tego typu bundla jest oferta zakupu większej ilości waluty w doświadczeniu w momencie wyczerpania.