El paquete de características Bundles ofrece funcionalidad fuera de la caja para vender colecciones de artículos a jugadores con descuento.Puedes elegir si quieres permitir que los jugadores compren paquetes usando una moneda personalizada en la experiencia o Robux, qué tipo de paquete quieres usar, qué conjunto de artículos quieres vender y cómo quieres solicitar a los jugadores durante su juego.
Al usar las opciones de personalización del paquete, puedes ajustar tus paquetes para cumplir con los objetivos de diseño y monetización de tus experiencias, como:
- Apuntar a una tasa de conversión baja de por ofrecer paquetes de inicio descuentados que proporcionen valor a los nuevos jugadores y alentar el gasto temprano.
- Aumentar la profundidad de gasto al agrupar artículos en varios puntos de precio para apelar a una gama de jugadores
- Monetizar operaciones en vivo (LiveOps) eventos ofreciendo paquetes de tiempo limitado de artículos exclusivos.

Obtener paquete
La tienda de creadores es una pestaña de la caja de herramientas que puedes usar para encontrar todos los recursos que se crean por Roblox y la comunidad de Roblox para su uso dentro de tus proyectos, incluidos aplicación de modelado, imágenes, mallas, sonido, audio, complemento, vídeo y fuentes.Puedes usar la Tienda de creadores para agregar uno o más recursos directamente a una experiencia abierta, incluidos paquetes de funciones!
Cada paquete de características requiere que el paquete de características Núcleo funcione correctamente.Una vez que los recursos del paquete de características Núcleo y Paquetes estén dentro de su inventario, puede volver a utilizarlos en cualquier proyecto en la plataforma.
Para obtener los paquetes de su inventario en su experiencia:
Añade el paquete de características Núcleo y Paquetes al inventario dentro de Studio haciendo clic en el enlace Añadir al inventario en el siguiente conjunto de componentes.
En la barra de herramientas, seleccione la pestaña Ver.
Haga clic en Caja de herramientas . Se muestra la ventana Caja de herramientas .
En la ventana Caja de herramientas , haga clic en la pestaña Inventario . Se muestran los ordenados Mis modelos .
Haga clic en el azulejo mosaicode características principal, luego en el azulejo mosaicode características de paquete.Ambas carpetas de paquete se muestran en la ventana Explorador .
Arrastra las carpetas del paquete a ReplicatedStorage .
Permita las llamadas al almacén de datos para rastrear las compras de jugadores con los paquetes.
- En la pestaña Inicio de la barra de herramientas, seleccione Configuración del juego .
- Navegue hasta la pestaña Seguridad , luego habilite Habilitar el acceso de Studio a los servicios de API .
Define monedas
Si tu experiencia tiene su propio sistema de moneda, puedes registrarlos con el paquete de características Núcleo al definirlos en >.Hay un ejemplo comentado de una moneda de gemas ya en este archivo; reemplázalo con el en posesión.
Monedas
Gems = {displayName = "Gems",symbol = "💎",icon = nil,},
El script Currencies le dice al paquete de características Núcleo algunos metadatos sobre su moneda:
- (requerido) displayName - El nombre de tu moneda. Si no especificas un símbolo o ícono, este nombre se usa en los botones de compra (es decir, "100 gemas").
- (opcional) symbol - Si tiene un personaje de texto para usar como icono para su moneda, se usa en lugar del displayName en los botones de compra (es decir, "💎100").
- (opcional) icon - Si tiene un icono de imagen AssetId para su moneda, se usa en lugar del displayName en los botones de compra (es decir,la imagen se colocará a la izquierda del precio "🖼️100")
Una vez que tu moneda esté configurada, debes especificar manualmente el precio, la moneda y el icono del paquete para la pantalla de aviso en lugar de que esa información se obtenga del producto asociado del desarrollador del paquete.
Paquetes
-- Si desea utilizar un producto de desarrollador, debe proporcionar un ID de producto único, solo utilizado por un paquete.-- Obtendremos el precio del paquete y el icono del producto del desarrolladorpricing = {priceType = CurrencyTypes.PriceType.Marketplace,devProductId = 1795621566,},-- De lo contrario, si quieres usar la moneda en experiencia en lugar de un producto de desarrollador, puedes usar lo siguiente en su lugar:-- El precio aquí está en la moneda de experiencia, no en Robuxpricing = {priceType = CurrencyTypes.PriceType.InExperience,price = 79,currencyId = "Gems",icon = 18712203759,},
También necesitas referenciar el BundlesExample script para llamar setInExperiencePurchaseHandler .
Ejemplo de paquetes
local function awardInExperiencePurchase(
_player: Player,
_bundleId: Types.BundleId,
_currencyId: CurrencyTypes.CurrencyId,
_price: number
)
-- Compruebe si el jugador tiene suficiente moneda para comprar el paquete
-- Actualizar datos del jugador, dar artículos, etc.
-- Deducir la moneda del jugador
task.wait(2)
return true
end
local function initializePurchaseHandlers()
local bundles = Bundles.getBundles()
for bundleId, bundle in bundles do
-- El paquete no está asociado con un producto de desarrollador si no tiene introducirde precio de mercado
if not bundle or bundle.pricing.priceType ~= "Marketplace" then
continue
end
Bundles.setPurchaseHandler(bundleId, awardMarketplacePurchase)
receiptHandlers[bundle.pricing.devProductId] = receiptHandler
end
-- Si tienes alguna moneda en experiencia que estás usando para paquetes, establece el manejador aquí
for currencyId, _ in Currencies do
Bundles.setInExperiencePurchaseHandler(currencyId, awardInExperiencePurchase)
end
end
En particular, necesitas completar awardInExperiencePurchase , que se llama a través de un bucle a través de Currencies dentro del ejemplo initializePurchaseHandlers (es decir,cada currencyId está conectado al manipulador a través de Bundles.setInExperiencePurchaseHandler(currencyId, awardInExperiencePurchase) ).
Define paquetes
Todos los paquetes ofrecibles en tu experiencia se pueden definir dentro de ReplicatedStorage.Bundles.Configs.Bundles , con tipos exportados del script Types en la misma carpeta.
Si estás usando un devProductId , necesitas actualizar el principal devProductId del paquete para coincidir con el de tu experiencia.Esto es lo que se solicitará a través de MarketplaceService para comprar el paquete en sí. Se recomienda encarecidamente utilizar un nuevo producto de desarrollador para el paquete para facilitar el seguimiento de ventas separadas. Si desea un paquete con múltiples artículos, y si estos ya están representados por productos de desarrollador en su experiencia, no necesita establecer explícitamente el precio del artículo/assetId/name, que se recuperará a través de la información del producto:
LEERME
{itemType = ItemTypes.ItemType.DevProduct,devProductId = <DEV_PRODUCT_ID>,metadata = {caption = {text = "x1",color = Color3.fromRGB(236, 201, 74),} -- La descripción es opcional! También puedes omitir este campo}},
De lo contrario, puedes configurar manualmente esos detalles del artículo:
LEERME
{itemType = ItemTypes.ItemType.Robux,priceInRobux = 49,icon = <IMAGE_ASSET_ID>,metadata = {caption = {text = "x1",color = Color3.fromRGB(236, 201, 74),} -- La descripción es opcional! También puedes omitir este campo}},
Por ejemplo, todo tu paquete probablemente se verá así:
LEERME
local starterBundle: Types.RelativeTimeBundle = {bundleType = Types.BundleType.RelativeTime,-- Si desea utilizar un producto de desarrollador, debe proporcionar un ID de producto único, solo utilizado por un paquete.-- Obtendremos el precio del paquete y el icono del producto del desarrolladorpricing = {priceType = CurrencyTypes.PriceType.Marketplace,devProductId = <DEV_PRODUCT_ID>,},-- De lo contrario, si quieres usar la moneda en experiencia en lugar de un producto de desarrollador, puedes usar lo siguiente en su lugar:-- El precio aquí está en la moneda de experiencia, no en Robux-- precios = {-- priceType = Tipo de precio = Tipo de moneda.PriceType.InExperience,-- precio = 79,-- currencyId = <CURRENCY_ID>,-- icón = <IMAGE_ASSET_ID>,-- },includedItems = {[1] = {-- El artículo en sí no se vende a través de un producto de desarrollador, por lo que indique cuánto vale en Robux y dé un ícono-- El precioInRobux ayuda a los paquetes a mostrar el valor relativo del precio del paquete vs. la suma de su contenidoitemType = ItemTypes.ItemType.Robux,priceInRobux = 49,icon = <IMAGE_ASSET_ID>,-- Alternativamente, si esto tiene un producto de desarrollador elimine el precio y el icono de arriba y solo establezca el devProductId-- El precio y el icono se recuperarán del producto del desarrollador-- idProductoDev = <ITEM_DEV_PRODUCT_ID>-- Hay más campos de metadatos opcionales que son específicos de la interfaz de usuario si es necesariometadata = {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 vez comprado o expirado, ya no es válido incluso si tu experiencia intenta solicitar (onPlayerAdded). Puedes hacer esto falso durante la prueba en el estudio.durationInSeconds = 900, -- 15 minutosincludesOfflineTime = false, -- Solo cuente el tiempo transcurrido en la experienciametadata = {displayName = "STARTER BUNDLE",description = "Save 75% and get a head start!",},}
Integrar lógica del servidor
Echa un vistazo a ReplicatedStorage.Bundles.Server.Examples.BundlesExample, que muestra cómo interactuará tu servidor con el paquete de características Bundles y los métodos anteriores en el ModuleScript.Los fragmentos siguientes son de ese script.
Necesitas principalmente conectar cuatro cosas una vez que arrastres el paquete de características Bundles a tu experiencia:
Conecte los manipuladores de compras a través de Bundles.setPurchaseHandler para especificar las funciones a llamar para otorgar artículos de recompensa cuando se esté procesando una compra.
Ejemplo de paqueteslocal function awardMarketplacePurchase(_player: Player, _bundleId: Types.BundleId, _receiptInfo: { [string]: any })-- Actualizar datos del jugador, dar artículos, etc.-- ... Y registro de recibo de información de compra para que podamos verificar si el usuario ya tiene este paquetetask.wait(2)return Enum.ProductPurchaseDecision.PurchaseGrantedendlocal function awardInExperiencePurchase(_player: Player,_bundleId: Types.BundleId,_currencyId: CurrencyTypes.CurrencyId,_price: number)-- Compruebe si el jugador tiene suficiente moneda para comprar el paquete-- Actualizar datos del jugador, dar artículos, etc.-- Deducir la moneda del jugadortask.wait(2)return trueendlocal function initializePurchaseHandlers()local bundles = Bundles.getBundles()for bundleId, bundle in bundles do-- El paquete no está asociado con un producto de desarrollador si no tiene introducirde precio de mercadoif not bundle or bundle.pricing.priceType ~= "Marketplace" thencontinueendBundles.setPurchaseHandler(bundleId, awardMarketplacePurchase)receiptHandlers[bundle.pricing.devProductId] = receiptHandlerend-- Si tienes alguna moneda en experiencia que estás usando para paquetes, establece el manejador aquífor currencyId, _ in Currencies doBundles.setInExperiencePurchaseHandler(currencyId, awardInExperiencePurchase)endendConecta tu lógica para MarketplaceService.ProcessReceipt , pero esto se puede hacer en otro lugar si tu experiencia ya tiene productos de desarrollador a la venta.Esencialmente, cuando se esté procesando un recibo de producto de desarrollador, ahora llamarán Bundles.getBundleByDevProduct para verificar si el producto pertenece a un paquete.Si lo hace, el script luego llama Bundles.processReceipt .
Ejemplo de paquetes-- Procesar recibo del mercado para determinar si el jugador debe ser cargado o nolocal 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] -- Obtener el manejador del productolocal success, result = pcall(handler, receiptInfo, player) -- Llamar al manipulador para verificar si la lógica de compra es exitosaif not success or not result thenwarn("Failed to process receipt:", 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-- Esta compra pertenece a un paquete, deje que los paquetes lo manejenlocal purchaseDecision = Bundles.processReceiptAsync(player, bundleId, receiptInfo)return purchaseDecision == Enum.ProductPurchaseDecision.PurchaseGrantedend-- Esta compra no pertenece a un paquete,-- ... Manejar toda tu lógica existente aquí si tienes algunareturn falseendConecte Players.PlayerAdded:Connect(Bundles.OnPlayerAdded) para que el paquete de características de paquetes reindique cualquier paquete activo que aún no haya expirado para un jugador.
LEERMElocal function onPlayerAdded(player: Player)-- Dile a los paquetes cuándo se une un jugador para que pueda recargar sus datosBundles.onPlayerAdded(player)-- Si tuvieras un paquete de inicio que quieras ofrecer a todos los nuevos usuarios, podrías solicitarlo aquí-- ... los paquetes se encargarán si el jugador ya lo ha comprado o si expiró desde que no es repetible-- Bundles.promptIfValidAsync(jugador, "StarterBundle")-- Llamar esto aquí solo por ejemplo, puedes llamar esto cuando quieras o donde quierasonPromptBundleXYZEvent(player)endPaquetes de promoción. Aunque esto depende del juego, el ejemplo invita a los jugadores con un paquete de inicio onPlayerAdded .
La lógica del paquete de funciones Bundles garantiza que cada jugador no reciba una oferta repetida si ya ha comprado el paquete, o si deja que la oferta expire (basada en la configuración del paquete).
Cada vez que quieras solicitar un paquete a un jugador, llama a Bundles.promptIfValidAsync(player, bundleId) .
LEERMElocal function onPromptBundleXYZEvent(player: Player)-- Conecta cualquier evento de experiencia que quieras usar para determinar cuándo se le solicita al jugador que obtenga el paquete-- ... Esto será cada vez que hayas cumplido con tus criterios de elegibilidad para solicitarle a un jugador el paquete-- ... Por ejemplo, si quieres solicitar un paquete cuando un jugador se una o cuando un jugador suba de niveltask.spawn(Bundles.promptIfValidAsync, player, <Some_Bundle_Id>)-- ... Si se crean múltiples paquetes, usar task.regeneración() para envolver la llamada de función anterior minimizará las discrepancias entre los recuentosend
Tenga en cuenta la siguiente guía de mejores prácticas sobre grabaciones redundantes de ReceiptIds:
Mientras que el paquete de características Paquetes registra ReceiptIds para evitar procesar el mismo recibo dos veces, también deberías estar registrando ReceiptIds dentro de tus tablas para que si el flujo de compra falla después de que su manipulador de compras ya haya terminado, sepas en el siguiente intento no otorgar artículos nuevamente.
El paquete de características Paquetes no registrará el ID de recibo si la compra falla en cualquier paso, por lo que debe asegurarse de que esté registrando el ID de recibo en sus tablas antes de procesar el recibo como parte de su manejador de compras.
Esta redundancia ayuda a garantizar que toda la lógica de compra se haya manejado apropiadamente y que el almacén de datos de tu almacén de datos y el paquete de características de paquetes alcancen la consistencia eventual, con tu almacén de datos siendo la fuente de verdad.
Configurar constantes
Las constantes para el paquete de características Núcleo viven en dos lugares:
Las constantes compartidas viven en ReplicatedStorage.FeaturePackagesCore.Configs.SharedConstants .
Las constantes específicas del paquete, en este caso el paquete de características Bundles , viven en ReplicatedStorage.Bundles.Configs.Constants .
Las principales cosas que quizás quieras ajustar para cumplir con los requisitos de diseño de tu experiencia:
- ID de sonido
- Duración del efecto de compra y colores de partículas
- Colapso de la visualización de cabezas
Además, puedes encontrar cadenas para la traducción separadas en una ubicación: ReplicatedStorage.FeaturePackagesCore.Configs.TranslationStrings .
Personalizar componentes de UI
Al modificar los objetos del paquete, como colores, fuente y transparencia, puedes ajustar la presentación visual de tus solicitudes de paquetes.Sin embargo, tenga en cuenta que si mueve cualquiera de los objetos alrededor jerárquicamente, el código no podrá encontrarlos y tendrá que hacer ajustes a su código.
Un mensaje de advertencia se compone de dos componentes de alto nivel:
- PromptItem – El componente individual se repite por cada artículo dentro de un paquete (imagen del artículo, subtítulo, nombre, precio).
- Prompt – La ventana de confirmación en sí.
La pantalla de advertencia también se compone de dos componentes:
- HudItem – Un componente individual que representa cada opción de menú en la pantalla de cabecera.
- Hud – Para ser llenado con programáticamente con HudItems .
Si desea tener un mayor control sobre la pantalla de cabeceras, en lugar de utilizar solo la interfaz de usuario HUD existente dentro de ReplicatedStorage.Bundles.Objects.BundlesGui , puede mover las cosas para cumplir con sus propios requisitos de diseño.Solo asegúrate de actualizar el comportamiento del script del cliente en el ReplicatedStorage.Bundles.Client.UIController script.
Referencia de API
Tipos
Tiempo relativo
Una vez que el paquete RelativeTime se ofrezca a un jugador, permanece disponible hasta que se agote el tiempo.Este tipo se muestra en la pantalla de visualización en la cabeza del jugador y se muestra automáticamente en las sesiones futuras hasta que el paquete expire o el jugador lo compre.
Un ejemplo común de este tipo de paquete es una oferta de paquete de inicio de un solo uso que se muestra a todos los nuevos jugadores durante 24 horas.Para las mejores prácticas de la industria sobre cómo implementar paquetes de inicio, vea Diseño del paquete de inicio.
Nombre | Tipo | Descripción |
---|---|---|
includeOfflineTime | bool | (Opcional) Si no se establecer, solo el tiempo que se pasa en la experiencia contará para la duración restante de la oferta. |
singleUse | bool | (Opcional) Si no se establecer, la compra se puede reactivar después de que se haya comprado o expirado.Si se establecer, una vez comprada o expirada la primera vez, no se volverá a solicitar nunca, incluso si llama Bundles.promptIfValidAsync con el ID del paquete. |
Tiempo fijo
Una vez que se ofrezca el paquete FixedTime al jugador, permanece disponible hasta el final del tiempo universal coordinado (UTC).Este tipo se muestra en la pantalla de visualización en la cabeza del jugador y se muestra automáticamente en las sesiones futuras hasta que el paquete expire o el jugador lo compre.
Un ejemplo común de este tipo de paquete es una oferta de vacaciones que solo está disponible para un mes determinado.
Una vez
Un paquete OneTime solo está disponible en el momento en que se ofrece a un jugador.No se muestra en la pantalla de cabeceras del jugador, y una vez que un jugador cierra la ventana emergente, no se puede volver a abrir hasta que sea solicitado por el servidor nuevamente.
Un ejemplo común de este tipo de paquete es una oferta para comprar más moneda en experiencia en el momento en que un jugador se queda sin ella.