Gói hàng các gói

*Nội dung này được dịch bằng AI (Beta) và có thể có lỗi. Để xem trang này bằng tiếng Anh, hãy nhấp vào đây.

Gói tính năng Bündel cung cấp chức năng bên ngoài hộp để bán bộ sưu tập các mặt hàng cho người chơi với giảm giá.Bạn có thể chọn có nên cho phép người chơi mua gói bằng tiền tệ trong kinh nghiệm tùy chỉnh hoặc Robux, kiểu gói nào bạn muốn sử dụng, những món đồ nào bạn muốn bán và cách bạn muốn yêu cầu người chơi trong quá trình chơi trò trải nghiệm trò chơi.

Sử dụng các tùy chọn tùy chỉnh của gói, bạn có thể tùy chỉnh các gói của mình để phù hợp với mục tiêu thiết kế và hóa đơn của trải nghiệm của bạn, chẳng hạn như:

  • Xác định tỷ lệ chuyển đổi thấp bằng cách cung cấp các gói khởi động giảm giá cung cấp giá trị cho các người chơi mới và khuyến khích chi tiêu sớm.
  • Tăng chi tiêu sâu bằng cách bộ hóa các mặt hàng ở các điểm giá khác nhau để kêu gọi một loạt người chơi.
  • Monetize các hoạt động trực tiếp (LiveOps) sự kiện bằng cách cung cấp các gói giới hạn thời gian của các vật phẩm độc quyền.

Nhận gói

Cửa hàng Nhà sáng tạo là một tab của Hộp công cụ mà bạn có thể sử dụng để tìm tất cả các tài sản được tạo bởi Roblox và cộng đồng Roblox để sử dụng trong các dự án của bạn, bao gồm mô hình, hình ảnh, khối, âm thanh, plugin, video và tài sản phông chữ.Bạn có thể sử dụng Cửa hàng Nhà sáng tạo để thêm một hoặc nhiều tài sản trực tiếp vào trải nghiệm mở, bao gồm cả gói tính năng!

Mỗi gói tính năng yêu cầu gói tính năng Trung tâm để hoạt động đúng cách.Một khi các tài nguyên gói tính năng Trung tâmGói đã có trong kho của bạn, bạn có thể sử dụng lại chúng trong bất kỳ dự án nào trên nền tảng.

Để lấy các gói từ kho lưu trữ của bạn vào trải nghiệm của bạn:

  1. Thêm gói tính năng Trung tâmGói phần mềm vào kho đồ trong Studio bằng cách nhấp vào liên kết Thêm vào kho đồ trong bộ thành phần sau.

  2. Trong thanh công cụ, hãy chọn tab Xem.

  3. Nhấp vào Hộp công cụ . Cửa sổ Hộp công cụ hiển thị.

    Studio's View tab with the Toolbox tool highlighted.
  4. Trong cửa sổ Hộp công cụ , nhấp vào tab Tồn kho . Các mô hình của tôi xếp hiển thị.

    Studio's Toolbox window with the Inventory tab highlighted.
  5. Nhấp vào ô Tính năng gói chính , sau đó ô Gói tính năng bộ .Cả hai thư mục gói hiển thị trong cửa sổ Explorer .

  6. Kéo các thư mục gói vào ReplicatedStorage .

  7. Cho phép các cuộc gọi lưu trữ dữ liệu theo dõi việc mua hàng của người chơi với các gói.

    1. Trong tab Trang chủ của thanh công cụ, hãy chọn Cài đặt trò chơi .
    2. Di chuyển đến tab Bảo mật , sau đó bật Bật truy cập Studio vào Dịch vụ API .

Xác định tiền tệ

Nếu trải nghiệm của bạn có hệ thống tiền tệ riêng, bạn có thể đăng ký những người có chức năng Core bằng cách xác định chúng trong ReplicatedStorage.FeaturePackagesCore.Configs.Currencies .Có một ví dụ bị bình luận về tiền tệ Gems đã có trong file này; thay thế nó bằng của sở hữubạn.

Tiền tệ

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

Tập lệnh Currencies cho biết gói tính năng Trung tâm có một số thông tin về tiền tệ của bạn:

  • (bắt buộc) displayName - Tên tiền tệ của bạn. Nếu bạn không đặt biểu tượng hoặc biểu tượng, tên này được sử dụng trong nút mua hàng (tức là "100 đá quý").
  • (tùy chọn) symbol - Nếu bạn có một nhân vật văn bản để sử dụng làm biểu tượng cho tiền tệ của bạn, nó được sử dụng thay cho displayName trong nút mua hàng (tức là "💎100").
  • (tùy chọn) icon - Nếu bạn có một biểu tượng hình ảnh AssetId cho tiền tệ của mình, nó được sử dụng thay cho displayName trong nút mua hàng (tức làhình ảnh sẽ được đặt bên trái của giá "🖼️100")

Khi tiền tệ của bạn được thiết lập, bạn cần phải thủ công xác định giá, tiền tệ và biểu tượng của gói cho hiển thị nổi bật thay vì thông tin được lấy từ sản phẩm phát triển liên quan của gói.

Các gói

-- Nếu bạn muốn sử dụng một sản phẩm phát triển, bạn phải cung cấp một devProductId duy nhất, chỉ được sử dụng bởi một gói.
-- Chúng tôi sẽ lấy giá gói và biểu tượng từ sản phẩm của nhà phát triển
pricing = {
priceType = CurrencyTypes.PriceType.Marketplace,
devProductId = 1795621566,
},
-- Nếu không, nếu bạn muốn sử dụng tiền tệ trong kinh nghiệm thay vì sản phẩm phát triển, bạn có thể sử dụng cái sau:
-- Giá ở đây là trong tiền tệ trong kinh nghiệm, không phải Robux
pricing = {
priceType = CurrencyTypes.PriceType.InExperience,
price = 79,
currencyId = "Gems",
icon = 18712203759,
},

Bạn cũng cần tham chiếu đến BundlesExample kịch bản để gọi setInExperiencePurchaseHandler .

Ví dụ về gói hàng

local function awardInExperiencePurchase(
_player: Player,
_bundleId: Types.BundleId,
_currencyId: CurrencyTypes.CurrencyId,
_price: number
)
-- Kiểm tra xem người chơi có đủ tiền tệ để mua gói không
-- Cập nhật dữ liệu người chơi, trao vật phẩm, v.v.
-- Trừ tiền tệ khỏi người chơi
task.wait(2)
return true
end
local function initializePurchaseHandlers()
local bundles = Bundles.getBundles()
for bundleId, bundle in bundles do
-- Gói không liên quan đến sản phẩm của nhà phát triển nếu nó không có loại giá thị trường
if not bundle or bundle.pricing.priceType ~= "Marketplace" then
continue
end
Bundles.setPurchaseHandler(bundleId, awardMarketplacePurchase)
receiptHandlers[bundle.pricing.devProductId] = receiptHandler
end
-- Nếu bạn có bất kỳ tiền tệ trong kinh nghiệm nào mà bạn đang sử dụng cho các gói, hãy đặt người xử lý ở đây
for currencyId, _ in Currencies do
Bundles.setInExperiencePurchaseHandler(currencyId, awardInExperiencePurchase)
end
end

Cụ thể, bạn cần phải hoàn thành awardInExperiencePurchase , được gọi bởi một vòng lặp thông qua Currencies bên trong ví dụ initializePurchaseHandlers (tức làmỗi currencyId được kết nối với người xử lý thông qua Bundles.setInExperiencePurchaseHandler(currencyId, awardInExperiencePurchase) ).

Xác định các gói

Tất cả các gói có thể cung cấp trong trải nghiệm của bạn có thể được xác định trong ReplicatedStorage.Bundles.Configs.Bundles , với các loại được xuất từ kịch bản Types trong cùng một thư mục.

Nếu bạn đang sử dụng một devProductId , bạn cần cập nhật chính của gói devProductId để phù hợp với một trong những gì bạn trải nghiệm.Đây là những gì sẽ được yêu cầu thông qua MarketplaceService để mua gói chính. Nên mạnh mẽ sử dụng sản phẩm nhà phát triển mới cho gói để làm cho việc theo dõi bán hàng riêng biệt dễ dàng hơn. Nếu bạn muốn một gói với nhiều mặt hàng, và nếu chúng đã được đại diện bởi các sản phẩm của nhà phát triển trong trải nghiệm của bạn, bạn không cần phải đặt giá mặt hàng/assetId/tên cụ thể, sẽ được lấy từ thông tin sản phẩm:

ĐỌC TÀI LIỆU

{
itemType = ItemTypes.ItemType.DevProduct,
devProductId = <DEV_PRODUCT_ID>,
metadata = {
caption = {
text = "x1",
color = Color3.fromRGB(236, 201, 74),
} -- Phụ đề là tùy chọn! Bạn cũng có thể bỏ trường này
}
},

Nếu không, bạn có thể tùy chỉnh thủ công các chi tiết của vật phẩm đó:

ĐỌC TÀI LIỆU

{
itemType = ItemTypes.ItemType.Robux,
priceInRobux = 49,
icon = <IMAGE_ASSET_ID>,
metadata = {
caption = {
text = "x1",
color = Color3.fromRGB(236, 201, 74),
} -- Phụ đề là tùy chọn! Bạn cũng có thể bỏ lượt trượt trường này
}
},

Ví dụ, toàn bộ gói của bạn có thể trông như thế này:

ĐỌC TÀI LIỆU

local starterBundle: Types.RelativeTimeBundle = {
bundleType = Types.BundleType.RelativeTime,
-- Nếu bạn muốn sử dụng một sản phẩm phát triển, bạn phải cung cấp một devProductId duy nhất, chỉ được sử dụng bởi một gói.
-- Chúng tôi sẽ lấy giá gói và biểu tượng từ sản phẩm của nhà phát triển
pricing = {
priceType = CurrencyTypes.PriceType.Marketplace,
devProductId = <DEV_PRODUCT_ID>,
},
-- Nếu không, nếu bạn muốn sử dụng tiền tệ trong kinh nghiệm thay vì sản phẩm phát triển, bạn có thể sử dụng cái sau:
-- Giá ở đây là trong tiền tệ trong kinh nghiệm, không phải Robux
-- giá = {
-- priceType = Loại tiền tệ.PriceType.InExperience,
-- giá = 79,
-- currencyId = <CURRENCY_ID>,
-- biểu tượng = <IMAGE_ASSET_ID>,
-- },
includedItems = {
[1] = {
-- Vật phẩm chính không được bán thông qua sản phẩm nhà phát triển, vì vậy chỉ ra số tiền nó đáng giá bao nhiêu trong Robux và cung cấp một biểu tượng
-- Giá trong Robux giúp Các gói hiển thị giá trị tương đối của giá gói so với tổng số nội dung của nó
itemType = ItemTypes.ItemType.Robux,
priceInRobux = 49,
icon = <IMAGE_ASSET_ID>,
-- Ngoài ra, nếu điều này có sản phẩm phát triển bỏ giá và biểu tượng phía trên và chỉ cần đặt devProductId
-- Giá và biểu tượng sẽ được lấy từ sản phẩm của nhà phát triển
-- devProductId = <ITEM_DEV_PRODUCT_ID>
-- Có thêm các trường metadata tùy chọn khác có giao diện người dùng cụ thể nếu cần
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, -- Một khi đã mua hoặc hết hạn, không còn hợp lệ ngay cả khi trải nghiệm của bạn cố gắng kích hoạt (onPlayerAdded). Bạn có thể làm cho điều này sai trong khi kiểm tra trong studio.
durationInSeconds = 900, -- 15 phút
includesOfflineTime = false, -- Chỉ tính thời gian trôi qua trong trải nghiệm
metadata = {
displayName = "STARTER BUNDLE",
description = "Save 75% and get a head start!",
},
}

Tích hợp logic máy chủ

Hãy xem xét ReplicatedStorage.Bundles.Server.Examples.BundlesExample, cho thấy cách máy chủ của bạn sẽ tương tác với gói tính năng Bundles và các phương pháp trên ModuleScript .Các đoạn dưới đây là từ kịch bản đó.

Chủ yếu bạn cần kết nối bốn thứ sau khi kéo gói tính năng Bundles vào trải nghiệm của bạn:

  1. Kết nối xử lý việc mua hàng thông qua Bundles.setPurchaseHandler để xác định các chức năng để gọi để trao giải vật phẩm khi một lần mua đang được xử lý.

    Ví dụ về gói hàng

    local function awardMarketplacePurchase(_player: Player, _bundleId: Types.BundleId, _receiptInfo: { [string]: any })
    -- Cập nhật dữ liệu người chơi, trao vật phẩm, v.v.
    -- ... VÀ ghi nhận thông tin nhận hàng recordInfo.PurchaseId để chúng tôi có thể kiểm tra xem người dùng đã có gói này chưa
    task.wait(2)
    return Enum.ProductPurchaseDecision.PurchaseGranted
    end
    local function awardInExperiencePurchase(
    _player: Player,
    _bundleId: Types.BundleId,
    _currencyId: CurrencyTypes.CurrencyId,
    _price: number
    )
    -- Kiểm tra xem người chơi có đủ tiền tệ để mua gói không
    -- Cập nhật dữ liệu người chơi, trao vật phẩm, v.v.
    -- Trừ tiền tệ khỏi người chơi
    task.wait(2)
    return true
    end
    local function initializePurchaseHandlers()
    local bundles = Bundles.getBundles()
    for bundleId, bundle in bundles do
    -- Gói không liên quan đến sản phẩm của nhà phát triển nếu nó không có loại giá thị trường
    if not bundle or bundle.pricing.priceType ~= "Marketplace" then
    continue
    end
    Bundles.setPurchaseHandler(bundleId, awardMarketplacePurchase)
    receiptHandlers[bundle.pricing.devProductId] = receiptHandler
    end
    -- Nếu bạn có bất kỳ tiền tệ trong kinh nghiệm nào mà bạn đang sử dụng cho các gói, hãy đặt người xử lý ở đây
    for currencyId, _ in Currencies do
    Bundles.setInExperiencePurchaseHandler(currencyId, awardInExperiencePurchase)
    end
    end
  2. Kết nối logic của bạn cho MarketplaceService.ProcessReceipt , nhưng điều này có thể được thực hiện ở nơi khác nếu trải nghiệm của bạn đã có sản phẩm nhà phát triển đang mua bán.Về cơ bản, khi một hóa đơn sản phẩm của nhà phát triển đang được xử lý, họ sẽ gọi Bundles.getBundleByDevProduct để kiểm tra xem sản phẩm có thuộc một góikhông.Nếu nó làm, thì kịch bản sau đó gọi Bundles.processReceipt .

    Ví dụ về gói hàng

    -- Xử lý hóa đơn từ thị trường để xác định xem người chơi có cần bị tính phí hay không
    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] -- Nhận xử lý viên cho sản phẩm
    local success, result = pcall(handler, receiptInfo, player) -- Gọi đại lý để kiểm tra xem logic mua có thành công không
    if not success or not result then
    warn("Failed to process receipt:", 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
    -- Mua hàng này thuộc về một gói, hãy để các gói xử lý nó
    local purchaseDecision = Bundles.processReceiptAsync(player, bundleId, receiptInfo)
    return purchaseDecision == Enum.ProductPurchaseDecision.PurchaseGranted
    end
    -- Mua hàng này không thuộc gói,
    -- ... Xử lý tất cả logic hiện có của bạn ở đây nếu bạn có bất kỳ
    return false
    end
  3. Kết nối Players.PlayerAdded:Connect(Bundles.OnPlayerAdded) để gói tính năng Bündel kích hoạt lại bất kỳ gói hoạt động nào chưa hết hạn cho một người chơi.

    ĐỌC TÀI LIỆU

    local function onPlayerAdded(player: Player)
    -- Thông báo các gói khi người chơi tham gia để có thể tải lại dữ liệu của họ
    Bundles.onPlayerAdded(player)
    -- Nếu bạn có một gói khởi động mà bạn muốn cung cấp cho tất cả người dùng mới, bạn có thể yêu cầu ở đây
    -- ... Các gói sẽ xử lý nếu người chơi đã mua nó hoặc nếu nó đã hết hạn vì nó không thể lặp lại
    -- Bundles.promptIfValidAsync(người chơi, "StarterBundle")
    -- Gọi điều này ở đây chỉ là ví dụ, bạn có thể gọi điều này bất cứ khi nào hoặc ở đâu bạn muốn
    onPromptBundleXYZEvent(player)
    end
  4. Các gói nhắc nhở. Trong khi điều này phụ thuộc vào trải nghiệm trò chơi, ví dụ nhắc nhở người chơi với một StarterBundle onPlayerAdded .

    • Logic gói tính năng Bündel đảm bảo mỗi người chơi không nhận được lời đề nghị lặp lại nếu họ đã mua gói, hoặc nếu họ để lời đề nghị hết hạn (dựa trên cấu hình gói).

    • Bất cứ khi nào bạn muốn yêu cầu một gói cho một người chơi, gọi Bundles.promptIfValidAsync(player, bundleId) .

    ĐỌC TÀI LIỆU

    local function onPromptBundleXYZEvent(player: Player)
    -- Kết nối bất kỳ sự kiện trải nghiệm nào bạn muốn sử dụng để xác định khi nào một người chơi được yêu cầu gói
    -- ... Điều này sẽ xảy ra bất cứ khi nào bạn đáp ứng các tiêu chí đủ điều kiện để yêu cầu một người chơi gói
    -- ... Ví dụ, nếu bạn muốn yêu cầu một gói khi một người chơi tham gia, hoặc khi một người chơi lên cấp
    task.spawn(Bundles.promptIfValidAsync, player, <Some_Bundle_Id>)
    -- ... Nếu tạo nhiều gói, sử dụng task.spawn() để bọc lại cuộc gọi chức năng trên sẽ giảm thiểu sự không hợp nhất giữa các lần đếm ngược
    end

Xem xét hướng dẫn tốt nhất về các bản ghi dự phòng của ReceiptIds sau đây:

  • Trong khi gói tính năng Gói hàng không ghi lại ReceiptIds để tránh xử lý cùng một hóa đơn hai lần, bạn cũng nên ghi lại ReceiptIds bên trong các bảng của mình để nếu dòng chuyển mua thất bại sau khi người xử lý mua hàng của họ đã hoàn thành, bạn sẽ biết trong lần thử lại tiếp theo không được trao các vật phẩm lại.

  • Gói tính năng Các gói sẽ không ghi lại ReceiptId nếu việc mua hàng thất bại ở bất kỳ bước nào, vì vậy bạn nên đảm bảo rằng bạn đang ghi lại ReceiptId trong các bảng của bạn trước khi xử lý hóa đơn như một phần của bộ xử lý mua hàng của bạn.

  • Sự trùng lặp này giúp đảm bảo rằng tất cả các logic mua đã được xử lý thích hợp và kho dữ liệu của bạn và gói tính năng Bündel đạt được sự thống nhất cuối cùng, với kho dữ liệu của bạn là nguồn sự thật.

Tùy chỉnh các biến số

Các tham số cho gói tính năng Trung tâm sống ở hai điểm:

  • Các biến chung sống trong ReplicatedStorage.FeaturePackagesCore.Configs.SharedConstants .

  • Các biến cố định cụ thể cho gói, trong trường hợp này là gói tính năng Bundles , sống trong ReplicatedStorage.Bundles.Configs.Constants .

Những điều chính bạn có thể muốn điều chỉnh để đáp ứng các yêu cầu thiết kế của trải nghiệm của bạn:

  • ID âm thanh
  • Thời lượng hiệu ứng mua và màu hạt của hạt
  • Hiển thị sự sụp đổ của đầu lên

Ngoài ra, bạn có thể tìm các chuỗi cho bản dịch được phân tách thành một vị trí: ReplicatedStorage.FeaturePackagesCore.Configs.TranslationStrings .

Tùy chỉnh các thành phần UI

Bằng cách sửa đổi các đối tượng gói, chẳng hạn như màu sắc, phông chữ và độ trong suốt, bạn có thể điều chỉnh trình bày hình ảnh của các lời nhắc của bộ của bạn.Tuy nhiên, hãy nhớ rằng nếu bạn di chuyển bất kỳ đối tượng nào xung quanh theo cấp bậc, mã sẽ không thể tìm thấy chúng, và bạn sẽ cần phải thực hiện các điều chỉnh cho mã của bạn.

Một lời nhắc được tạo bởi hai thành phần cấp cao:

  • PromptItem – Thành phần cá nhân lặp lại cho mỗi mục trong một gói (hình ảnh mục, phụ đề, tên, giá).
  • Prompt – Cửa sổ thông báo chính.

Bảng hiển thị cảnh báo cũng được tạo bởi hai thành phần:

  • HudItem – Một thành phần cụ thể đại diện cho mỗi lựa chọn menu trong bảng hiển thị chính.
  • Hud – Để được lấp đầy bằng cách lập trình với HudItems .

Nếu bạn muốn có kiểm soát lớn hơn về hiển thị đầu, thay vì chỉ sử dụng giao diện HUD hiện có trong ReplicatedStorage.Bundles.Objects.BundlesGui , bạn có thể di chuyển các thứ xung quanh để đáp ứng các yêu cầu thiết kế của riêng mình.Chỉ cần chắc chắn để cập nhật hành vi kịch bản khách hàng trong kịch bản ReplicatedStorage.Bundles.Client.UIController.

Tham chiếu API

Loại

Thời gian tuyệt đối

Một khi gói RelativeTime được cung cấp cho một người chơi, nó vẫn còn có sẵn cho đến khi thời gian chạy hết.Loại này hiển thị trên màn hình hiển thị trước của người chơi, và tự động yêu cầu trong các phiên tương lai cho đến khi gói hết hạn hoặc người chơi mua nó.

Một ví dụ phổ biến về loại gói này là một gói khởi động một lần dùng được hiển thị cho tất cả các người chơi mới trong 24 giờ.Về tốt nhất cho ngành công nghiệp về cách triển khai gói khởi động, xem Thiết kế gói khởi động.

TênLoạiMô tả
includeOfflineTimebool (Tùy chọn) Nếu không được cài đặt, chỉ thời gian được chi tiêu trong trải nghiệm sẽ được tính vào thời gian còn lại của ưu đãi.
singleUsebool (Tùy chọn) Nếu không được cài đặt, việc mua hàng có thể được kích hoạt lại sau khi nó được mua hoặc hết hạn.Nếu được cài đặt, một lần mua hoặc hết hạn đầu tiên, nó sẽ không bao giờ được yêu cầu lại, ngay cả khi bạn gọi Bundles.promptIfValidAsync với bundleId.

Thời gian cố định

Một khi gói FixedTime được cung cấp cho một người chơi, nó vẫn còn có sẵn cho đến khi kết thúc thời gian phối hợp toàn cầu (UTC).Loại này hiển thị trên màn hình hiển thị trước của người chơi, và tự động yêu cầu trong các phiên tương lai cho đến khi gói hết hạn hoặc người chơi mua nó.

Một ví dụ phổ biến về loại gói này là một ưu đãi ngày lễ chỉ có sẵn trong một tháng nhất định.

Một lần

Một gói OneTime không có sẵn cho đến khi nó được cung cấp cho một người chơi.Nó không hiển thị trên màn hình hiển thị của người chơi, và một khi một người chơi đóng lại lời nhắc, nó không thể được mở lại cho đến khi nó được yêu cầu bởi máy chủ một lần nữa.

Một ví dụ phổ biến về loại gói này là một lời đề nghị mua thêm tiền trong kinh nghiệm khi một người chơi hết.