Paketler özelliği paketi, oyunculara indirimli olarak eşya koleksiyonları satmak için kutudan çıkar çıkmaz işlevsellik sunar. Oyuncuların paketleri özel bir oyun içi para birimi veya Robux kullanarak satın alıp almayacaklarını, hangi paket türünü kullanmak istediğinizi, hangi eşya setini satmak istediğinizi ve oyunculara oyun sırasında nasıl bir bildirimde bulunmak istediğinizi seçebilirsiniz.
Paketin özelleştirme seçeneklerini kullanarak, paketlerinizi oyunlarınızın tasarım ve para kazanma hedeflerine uyacak şekilde özelleştirebilirsiniz, örneğin:
- Yeni oyunculara değer sağlayan ve erken harcamayı teşvik eden indirimli başlangıç paketleri sunarak düşük dönüşüm oranı metriğini hedeflemek.
- Farklı fiyat noktalarında eşyaları birleştirerek çeşitli oyunculara hitap etmek için harcama derinliğini artırmak.
- Sınırlı süreli özel eşyaların paketlerini sunarak canlı operasyonlar (LiveOps) etkinliklerini paraya dönüştürmek.

Paketi alın
Yaratıcı Mağaza, projelerinizde kullanılmak üzere Roblox ve Roblox topluluğu tarafından oluşturulan tüm varlıkları bulmak için kullanabileceğiniz Araç Kutusu'nun bir sekmesidir; model, resim, ağ, ses, eklenti, video ve yazı tipi varlıklarını içerir. Yaratıcı Mağaza'yı, bir veya daha fazla varlığı açık bir oyuna doğrudan eklemek için kullanabilirsiniz; bu, özellik paketlerini de içerir!
Her özellik paketi, düzgün çalışması için Çekirdek özellik paketini gerektirir. Çekirdek ve Paketler özellik paketi varlıkları envanterinize alındıktan sonra, bunları platformdaki herhangi bir projede yeniden kullanabilirsiniz.
Envanterinizden paketleri oyununuzda almak için:
Aşağıdaki bileşenler setinde Envantere Ekle bağlantısına tıklayarak Çekirdek ve Paketler özellik paketini Studio içinde envanterinize ekleyin.
Studio'nun Pencere menüsünden veya Ana Sayfa sekmesi araç çubuğundan Araç Kutusu penceresini açın.
Araç Kutusu penceresinde, Envanter sekmesine tıklayın. Modellerim sıralaması görüntülenir.

Özellik Paketi Çekirdek kutusuna, ardından Paket Özellik Paketi kutusuna tıklayın. Her iki paket klasörü de Keşfedin penceresinde görüntülenir.
Paket klasörlerini ReplicatedStorage içine sürükleyin.
Paketlerle birlikte oyuncu satın alımlarını izlemek için veri deposu çağrılarına izin verin.
- Studio'nun Dosya ⟩ Deneyim Ayarları penceresini açın.
- Güvenlik sekmesine gidin, ardından API Hizmetlerine Studio Erişimini Etkinleştir seçeneğini etkinleştirin.
Para birimlerini tanımlayın
Eğer oyununuzun kendi para birimi sistemi varsa, bunları Çekirdek özellik paketi ile ReplicatedStorage.FeaturePackagesCore.Configs.Currencies içinde tanımlayarak kaydedebilirsiniz. Bu dosyada zaten yorum satırıyla kapatılmış bir Gems para birimi örneği bulunmaktadır; bunu kendi para biriminizle değiştirin.
Gems = {
displayName = "Gems",
symbol = "💎",
icon = nil,
},Currencies betiği, Çekirdek özellik paketine para biriminiz hakkında bazı meta veriler sağlar:
- (zorunlu) displayName - Para biriminizin adı. Eğer bir sembol veya simge belirtmezseniz, bu ad satın alma butonlarında kullanılır (yani "100 Gems").
- (isteğe bağlı) symbol - Para biriminiz için simge olarak kullanmak üzere bir metin karakteriniz varsa, bu, satın alma butonlarında displayName yerine kullanılır (yani "💎100").
- (isteğe bağlı) icon - Para biriminiz için bir AssetId resim simgeniz varsa, bu, satın alma butonlarında displayName yerine kullanılır (yani resim fiyatın soluna yerleştirilecektir "🖼️100").
Para biriminiz ayarlandıktan sonra, paketin fiyatını, para birimini ve başlık ekranı için simgesini manuel olarak belirtmeniz gerekir; bu bilgiler paketin ilişkili geliştirici ürününden alınmaz.
-- Eğer bir geliştirici ürünü kullanmak istiyorsanız, yalnızca bir pakette kullanılan benzersiz bir devProductId sağlamalısınız.
-- Paket fiyatını ve simgesini geliştirici ürününden alacağız
pricing = {
priceType = CurrencyTypes.PriceType.Marketplace,
devProductId = 1795621566,
},
-- Aksi takdirde, bir geliştirici ürünü yerine oyun içi para birimi kullanmak istiyorsanız, aşağıdakini kullanabilirsiniz:
-- Fiyat burada oyun içi para birimindedir, Robux değil
pricing = {
priceType = CurrencyTypes.PriceType.InExperience,
price = 79,
currencyId = "Gems",
icon = 18712203759,
},Ayrıca BundlesExample betiğini referans alarak setInExperiencePurchaseHandler çağrısını yapmanız gerekir.
local function awardInExperiencePurchase(
_player: Player,
_bundleId: Types.BundleId,
_currencyId: CurrencyTypes.CurrencyId,
_price: number
)
-- Oyuncunun paketi satın almak için yeterli parası olup olmadığını kontrol edin
-- Oyuncu verilerini güncelleyin, eşyaları verin, vb.
-- Oyuncudan parayı düşürün
task.wait(2)
return true
end
local function initializePurchaseHandlers()
local bundles = Bundles.getBundles()
for bundleId, bundle in bundles do
-- Paket, pazar yeri fiyat türüne sahip değilse geliştirici ürünü ile ilişkilendirilmemiştir
if not bundle or bundle.pricing.priceType ~= "Marketplace" then
continue
end
Bundles.setPurchaseHandler(bundleId, awardMarketplacePurchase)
receiptHandlers[bundle.pricing.devProductId] = receiptHandler
end
-- Eğer paketler için kullandığınız herhangi bir oyun içi para biriminiz varsa, burada işleyiciyi ayarlayın
for currencyId, _ in Currencies do
Bundles.setInExperiencePurchaseHandler(currencyId, awardInExperiencePurchase)
end
endÖzellikle, awardInExperiencePurchase fonksiyonunu doldurmanız gerekir; bu, örnek initializePurchaseHandlers içindeki Currencies üzerinden bir döngü ile çağrılır (yani her currencyId, Bundles.setInExperiencePurchaseHandler(currencyId, awardInExperiencePurchase) ile işleyiciye bağlanır).
Paketleri tanımlayın
Oyununuza sunulabilir tüm paketler ReplicatedStorage.Bundles.Configs.Bundles içinde tanımlanabilir; türler aynı klasördeki Types betiğinden dışa aktarılır.
Eğer bir devProductId kullanıyorsanız, paketin ana devProductId'sini oyununuzdaki ile eşleşecek şekilde güncellemeniz gerekir. Bu, paketi satın almak için MarketplaceService üzerinden istenecektir. Paketin ayrı satışlarını takip etmeyi kolaylaştırmak için yeni bir geliştirici ürünü kullanmanız şiddetle önerilir.
Birden fazla eşya içeren bir paket istiyorsanız ve bunlar zaten oyununuzda geliştirici ürünleri ile temsil ediliyorsa, eşya fiyatı/assetId/isimlerini açıkça ayarlamanıza gerek yoktur; bunlar ürün bilgileri aracılığıyla alınacaktır:
{
itemType = ItemTypes.ItemType.DevProduct,
devProductId = <DEV_PRODUCT_ID>,
metadata = {
caption = {
text = "x1",
color = Color3.fromRGB(236, 201, 74),
} -- Başlık isteğe bağlıdır! Bu alanı da atlayabilirsiniz
}
},Aksi takdirde, bu eşya detaylarını manuel olarak yapılandırabilirsiniz:
{
itemType = ItemTypes.ItemType.Robux,
priceInRobux = 49,
icon = <IMAGE_ASSET_ID>,
metadata = {
caption = {
text = "x1",
color = Color3.fromRGB(236, 201, 74),
} -- Başlık isteğe bağlıdır! Bu alanı da atlayabilirsiniz
}
},Örneğin, tüm paketiniz muhtemelen şöyle görünecektir:
local starterBundle: Types.RelativeTimeBundle = {
bundleType = Types.BundleType.RelativeTime,
-- Eğer bir geliştirici ürünü kullanmak istiyorsanız, yalnızca bir pakette kullanılan benzersiz bir devProductId sağlamalısınız.
-- Paket fiyatını ve simgesini geliştirici ürününden alacağız
pricing = {
priceType = CurrencyTypes.PriceType.Marketplace,
devProductId = <DEV_PRODUCT_ID>,
},
-- Aksi takdirde, bir geliştirici ürünü yerine oyun içi para birimi kullanmak istiyorsanız, aşağıdakini kullanabilirsiniz:
-- Fiyat burada oyun içi para birimindedir, Robux değil
-- pricing = {
-- priceType = CurrencyTypes.PriceType.InExperience,
-- price = 79,
-- currencyId = <CURRENCY_ID>,
-- icon = <IMAGE_ASSET_ID>,
-- },
includedItems = {
[1] = {
-- Eşya, bir geliştirici ürünü aracılığıyla satılmadığı için, Robux cinsinden ne kadar değerli olduğunu belirtin ve bir simge verin
-- priceInRobux, Paketlerin paket fiyatı ile içeriğinin toplamı arasındaki göreceli değeri göstermesine yardımcı olur
itemType = ItemTypes.ItemType.Robux,
priceInRobux = 49,
icon = <IMAGE_ASSET_ID>,
-- Alternatif olarak, eğer bu bir geliştirici ürünü ise yukarıdaki fiyat ve simgeyi bırakın ve sadece devProductId'yi ayarlayın
-- Fiyat ve simge geliştirici ürününden alınacaktır
-- devProductId = <ITEM_DEV_PRODUCT_ID>
-- Gerekirse daha fazla isteğe bağlı meta veri alanı vardır
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, -- Satın alındıktan veya süresi dolduktan sonra, geçerli değildir; oyun bunu istemeye çalışsa bile (onPlayerAdded). Studio'da test ederken bunu false yapabilirsiniz.
durationInSeconds = 900, -- 15 dakika
includesOfflineTime = false, -- Sadece oyunda geçen süreyi say
metadata = {
displayName = "BAŞLANGIÇ PAKETİ",
description = "Yüzde 75 tasarruf edin ve hızlı bir başlangıç yapın!",
},
}Sunucu mantığını entegre edin
ReplicatedStorage.Bundles.Server.Examples.BundlesExample dosyasına göz atın; bu dosya, sunucunuzun Paketler özellik paketi ile nasıl etkileşime gireceğini ve yukarıdaki yöntemleri ModuleScript üzerinde nasıl kullanacağını gösterir. Aşağıdaki kod parçaları o betikten alınmıştır.
Paketler özellik paketini oyununuzda sürükledikten sonra dört şeyi bağlamanız gerekir:
Satın alma işleyicilerini Bundles.setPurchaseHandler aracılığıyla bağlayarak, bir satın alma işlemi işlenirken eşyaları ödüllendirmek için çağrılacak fonksiyonları belirtin.
BundlesExamplelocal function awardMarketplacePurchase(_player: Player, _bundleId: Types.BundleId, _receiptInfo: { [string]: any })-- Oyuncu verilerini güncelleyin, eşyaları verin, vb.-- ... VE receiptInfo.PurchaseId'yi kaydedin, böylece kullanıcının bu paketi zaten alıp almadığını kontrol edebiliriztask.wait(2)return Enum.ProductPurchaseDecision.PurchaseGrantedendlocal function awardInExperiencePurchase(_player: Player,_bundleId: Types.BundleId,_currencyId: CurrencyTypes.CurrencyId,_price: number)-- Oyuncunun paketi satın almak için yeterli parası olup olmadığını kontrol edin-- Oyuncu verilerini güncelleyin, eşyaları verin, vb.-- Oyuncudan parayı düşürüntask.wait(2)return trueendlocal function initializePurchaseHandlers()local bundles = Bundles.getBundles()for bundleId, bundle in bundles do-- Paket, pazar yeri fiyat türüne sahip değilse geliştirici ürünü ile ilişkilendirilmemiştirif not bundle or bundle.pricing.priceType ~= "Marketplace" thencontinueendBundles.setPurchaseHandler(bundleId, awardMarketplacePurchase)receiptHandlers[bundle.pricing.devProductId] = receiptHandlerend-- Eğer paketler için kullandığınız herhangi bir oyun içi para biriminiz varsa, burada işleyici ayarlayınfor currencyId, _ in Currencies doBundles.setInExperiencePurchaseHandler(currencyId, awardInExperiencePurchase)endendMarketplaceService.ProcessReceipt için mantığınızı bağlayın; ancak bu, oyununuzda zaten satılık geliştirici ürünleri varsa başka bir yerde yapılabilir. Temelde, bir geliştirici ürünü makbuzu işlenirken, artık Bundles.getBundleByDevProduct çağrılacak ve ürünün bir pakete ait olup olmadığı kontrol edilecektir. Eğer öyleyse, betik ardından Bundles.processReceipt çağrısını yapar.
BundlesExample-- Oyuncunun ücretlendirilip ücretlendirilmeyeceğini belirlemek için pazardan makbuzu işleyinlocal 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] -- Ürün için işleyiciyi alınlocal success, result = pcall(handler, receiptInfo, player) -- Satın alma mantığının başarılı olup olmadığını kontrol etmek için işleyiciyi çağırınif not success or not result thenwarn("Makbuzu işleme başarısız oldu:", 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-- Bu satın alma bir pakete aittir, Paketlerin bunu işlemesine izin verinlocal purchaseDecision = Bundles.processReceiptAsync(player, bundleId, receiptInfo)return purchaseDecision == Enum.ProductPurchaseDecision.PurchaseGrantedend-- Bu satın alma bir pakete ait değildir,-- ... Eğer varsa mevcut tüm mantığınızı burada işleyinreturn falseendPaketler özellik paketinin, süresi dolmamış aktif paketleri yeniden istemesi için Players.PlayerAdded:Connect(Bundles.OnPlayerAdded) bağlayın.
READMElocal function onPlayerAdded(player: Player)-- Oyuncu katıldığında Paketlere bildirin, böylece verilerini yeniden yükleyebilirBundles.onPlayerAdded(player)-- Eğer tüm yeni kullanıcılara sunmak istediğiniz bir başlangıç paketi varsa, bunu burada isteyebilirsiniz-- ... Paketler, oyuncunun bunu zaten satın alıp almadığını veya süresinin dolup dolmadığını kontrol eder, çünkü bu tekrar edilemez-- Bundles.promptIfValidAsync(player, "StarterBundle")-- Bunu burada örnek olarak çağırıyorum, bunu istediğiniz zaman veya istediğiniz yerde çağırabilirsinizonPromptBundleXYZEvent(player)endPaketleri isteyin. Bu, oyun oynanışına bağlıdır; örnek, oyunculara bir StarterBundle sunar onPlayerAdded.
Paketler özellik paketi mantığı, her oyuncunun paketi zaten satın almışsa veya teklifin süresi dolmuşsa tekrar bir teklif almadığından emin olur.
Bir paketi bir oyuncuya istemek istediğinizde, Bundles.promptIfValidAsync(player, bundleId) çağrısını yapın.
READMElocal function onPromptBundleXYZEvent(player: Player)-- Bir oyuncunun paketi istemesi için kullanmak istediğiniz herhangi bir oyun olayını bağlayın-- ... Bu, bir oyuncuya paketi istemek için uygunluk kriterlerinizi karşıladığınızda olacaktır-- ... Örneğin, bir oyuncu katıldığında veya bir oyuncu seviye atladığında bir paketi istemek istiyorsanıztask.spawn(Bundles.promptIfValidAsync, player, <Some_Bundle_Id>)-- ... Birden fazla paket oluşturuyorsanız, yukarıdaki fonksiyon çağrısını sarmak için task.spawn() kullanmak, geri sayımlar arasındaki tutarsızlıkları en aza indirecektirend
Makbuz Kimliklerinin gereksiz kayıtları hakkında aşağıdaki en iyi uygulama rehberliğini dikkate alın:
Paketler özellik paketi, aynı makbuzu iki kez işlememek için Makbuz Kimliklerini kaydederken, siz de satın alma akışı, satın alma işleyiciniz tamamlandıktan sonra başarısız olursa, tekrar denemelerde eşyaları tekrar vermemek için tablolarınızda Makbuz Kimliklerini kaydetmelisiniz.
Paketler özellik paketi, satın alma herhangi bir adımda başarısız olursa Makbuz Kimliğini kaydetmeyecektir, bu nedenle makbuzu işlemeye başlamadan önce tablolarınızdaki Makbuz Kimliğini kaydettiğinizden emin olmalısınız.
Bu fazlalık, tüm satın alma mantığının uygun şekilde ele alındığından emin olmaya yardımcı olur ve veri deponuz ile Paketler özellik paketinin veri deposunun nihai tutarlılığa ulaşmasını sağlar; veri deponuz gerçeklerin kaynağıdır.
Sabitleri yapılandırın
Çekirdek özellik paketi için sabitler iki yerde bulunur:
Paylaşılan sabitler ReplicatedStorage.FeaturePackagesCore.Configs.SharedConstants içinde bulunur.
Paket özel sabitleri, bu durumda Paketler özellik paketi, ReplicatedStorage.Bundles.Configs.Constants içinde bulunur.
Oyun tasarım gereksinimlerinizi karşılamak için ayarlamak isteyebileceğiniz ana şeyler:
- Ses varlık kimlikleri
- Satın alma efektinin süresi ve parçacık renkleri
- Başlık ekranı çökme durumu
Ayrıca, çeviri için ayrılmış dizeleri tek bir konumda bulabilirsiniz: ReplicatedStorage.FeaturePackagesCore.Configs.TranslationStrings.
UI bileşenlerini özelleştirin
Paket nesnelerini, renkler, yazı tipi ve saydamlık gibi değiştirerek, paket istemlerinizin görsel sunumunu ayarlayabilirsiniz. Ancak, herhangi bir nesneyi hiyerarşik olarak hareket ettirirseniz, kod bunları bulamayacak ve kodunuzda ayarlamalar yapmanız gerekecektir.
Bir istem, iki yüksek düzeyde bileşenden oluşur:
- PromptItem – Bir paketteki her eşya için tekrarlanan bireysel bileşen (eşya resmi, başlık, isim, fiyat).
- Prompt – İstem penceresi.
Başlık ekranı da iki bileşenden oluşur:
- HudItem – Başlık ekranındaki her menü seçeneğini temsil eden bireysel bileşen.
- Hud – Programatik olarak HudItems ile doldurulacaktır.
Başlık ekranı üzerinde daha fazla kontrol sahibi olmak istiyorsanız, ReplicatedStorage.Bundles.Objects.BundlesGui içindeki mevcut HUD UI'sını kullanmak yerine, kendi tasarım gereksinimlerinizi karşılamak için nesneleri hareket ettirebilirsiniz. Sadece ReplicatedStorage.Bundles.Client.UIController betiğindeki istemci betiği davranışını güncellemeyi unutmayın.
API referansı
Türler
RelativeTime
RelativeTime paketi bir oyuncuya sunulduğunda, süre dolana kadar mevcut kalır. Bu tür, oyuncunun başlık ekranında görüntülenir ve paket süresi dolana kadar veya oyuncu satın alana kadar gelecekteki oturumlarda otomatik olarak istemde bulunur.
Bu paket türünün yaygın bir örneği, tüm yeni oyunculara 24 saat boyunca sunulan tek kullanımlık bir başlangıç paketi teklifidir.
| Ad | Tür | Açıklama |
|---|---|---|
| includeOfflineTime | bool | (İsteğe bağlı) Ayarlanmazsa, yalnızca oyunda geçirilen süre, kalan teklif süresine sayılacaktır. |
| singleUse | bool | (İsteğe bağlı) Ayarlanmazsa, satın alma yapıldıktan veya süresi dolduktan sonra yeniden etkinleştirilebilir. Ayarlanırsa, ilk kez satın alındıktan veya süresi dolduktan sonra, bir daha asla teklif edilemeyecektir; Bundles.promptIfValidAsync ile bundleId çağrısı yapsanız bile. |
FixedTime
FixedTime paketi bir oyuncuya sunulduğunda, belirlenen koordinatlı evrensel zaman (UTC) sona erene kadar mevcut kalır. Bu tür, oyuncunun başlık ekranında görüntülenir ve paket süresi dolana kadar veya oyuncu satın alana kadar gelecekteki oturumlarda otomatik olarak istemde bulunur.
Bu paket türünün yaygın bir örneği, yalnızca belirli bir ay boyunca mevcut olan bir tatil teklifidir.
OneTime
OneTime paketi, yalnızca bir oyuncuya sunulduğu anda mevcut olur. Oyuncunun başlık ekranında görüntülenmez ve bir oyuncu istemi kapattığında, sunucu tarafından tekrar istemedikleri sürece yeniden açılamaz.
Bu paket türünün yaygın bir örneği, bir oyuncu oyun içi para birimi tükendiğinde daha fazla satın alma teklifi yapmaktır.