Paketler özelliği paketi, oyunculara indirimle koleksiyon halinde ürünler satmak için kutudan çıktığı gibi işlevsellik sunar. Oyuncuların paketleri özel deneyim içi para birimi veya Robux ile satın almasına izin verip vermeyeceğinizi, hangi paket türünü kullanmak istediğinizi, satmak istediğiniz ürün setini ve oyuncuları oyun sırasında nasıl bilgilendirmek istediğinizi seçebilirsiniz.
Paketin özelleştirme seçeneklerini kullanarak, paketlerinizi deneyimlerinizin tasarım ve para kazanma hedeflerine ulaşacak şekilde özelleştirebilirsiniz, örneğin:
- Yeni oyunculara değer sağlayan ve erken harcamaları teşvik eden indirimli başlangıç paketleri sunarak düşük dönüşüm oranı ölçütünü hedeflemek.
- Çeşitli fiyat noktalarında ürünleri birleştirerek daha geniş bir oyuncu yelpazesine hitap etmek için harcama derinliğini artırmak.
- Sınırlı süreli özel ürün paketleri sunarak canlı operasyonlar (LiveOps) etkinliklerini paraya dönüştürmek.

Paketi al
Yaratıcı Mağaza, projelerinizde kullanılmak üzere Roblox ve Roblox topluluğu tarafından oluşturulan tüm varlıkları bulabileceğiniz Araç Kutusunun bir sekmesidir. Model, resim, mesh, ses, eklenti, video ve font varlıklarını da içerir. Yaratıcı mağazayı kullanarak bir veya daha fazla varlığı açık bir deneyime doğrudan ekleyebilirsiniz, ayrıca özellik paketlerini de ekleyebilirsiniz!
Her özellik paketi, düzgün çalışması için Çekirdek özellik paketine ihtiyaç duyar. Çekirdek ve Paketler özellik paketi varlıklarınız envanterinizde bulunduktan sonra, onları platformdaki herhangi bir projede yeniden kullanabilirsiniz.
Envanterinizdeki paketleri deneyiminize almak için:
Aşağıdaki bileşenlerden oluşan setteki Envantere Ekle bağlantısına tıklayarak Çekirdek ve Paketler özellik paketlerini Studio içinde envanterinize ekleyin.
Studio'nun Pencere menüsü veya Ana Sayfa sekmesi araç çubuğundan Araç Kutusunu açın.
Araç Kutusu penceresinde, Envanter sekmesine tıklayın. Modellerim sıralaması görüntülenir.

Özellik Paketi Çekirdek kutucuğuna tıklayın, sonra Paket Özellik Paketi kutucuğuna tıklayın. Her iki paket klasörü Keşfedin penceresinde görüntülenir.
Paket klasörlerini ReplicatedStorage içine sürükleyin.
Paketlerle birlikte oyuncu satın alımlarını takip etmek için veri mağazası ç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ımla
Eğer deneyiminizin kendi para birimi sistemi varsa, bunları Çekirdek özellik paketi ile tanımlayarak kaydedebilirsiniz; ReplicatedStorage.FeaturePackagesCore.Configs.Currencies içinde tanımlayarak. Bu dosyada zaten yorum satırlarında bir Gemler para birimi örneği var; bunu kendi para biriminizle değiştirebilirsiniz.
Para BirimleriGems = {displayName = "Gemler",symbol = "💎",icon = nil,},
Para Birimleri betiği, Çekirdek özellik paketine para biriminiz hakkında bazı meta verileri belirtir:
- (zorunlu) displayName - Para biriminizin adı. Eğer bir sembol veya simge belirtmezseniz, bu ad satın alma düğmelerinde kullanılır (yani "100 Gemler").
- (isteğe bağlı) symbol - Para biriminiz için simge olarak kullanmak üzere bir metin karakteriniz varsa, bu, satın alma düğmelerindeki displayName yerine kullanılır (yani "💎100").
- (isteğe bağlı) icon - Para biriminiz için bir AssetId resim simgeniz varsa, bu displayName yerine kullanılacaktır (yani resim fiyatın soluna yerleştirilecektir "🖼️100").
Para biriminiz ayarlandığında, paketin fiyatını, para birimini ve bilgi ekranı görüntüsü için simgeyi manuel olarak belirtmeniz gerekir; bu bilgi, paketin ilişkilendirilmiş geliştirici ürününden alınmaz.
Paketler-- Eğer bir dev ürünü kullanmak istiyorsanız, yalnızca bir pakete ait olan eşsiz bir devProductId sağlamanız gerekir.-- Paket fiyatı ve simgesini geliştirici ürününden alacağızpricing = {priceType = CurrencyTypes.PriceType.Marketplace,devProductId = 1795621566,},-- Aksi halde, bir dev ürünü yerine deneyim içi para birimi kullanmak istiyorsanız, aşağıdakileri kullanabilirsiniz:-- Fiyat burada deneyim içindeki para biriminde, Robux değilpricing = {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.
BundlesExample
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 et
-- Oyuncu verilerini güncelle, ürünleri ver, vb.
-- Oyuncudan parayı düş
task.wait(2)
return true
end
local function initializePurchaseHandlers()
local bundles = Bundles.getBundles()
for bundleId, bundle in bundles do
-- Eğer pazar fiyat türü yoksa, paket geliştirici ürünü ile ilişkili değildir
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 deneyim içi para biriminiz varsa, burada handler'ı ayarlayın
for currencyId, _ in Currencies do
Bundles.setInExperiencePurchaseHandler(currencyId, awardInExperiencePurchase)
end
end
Özellikle, awardInExperiencePurchase fonksiyonunu doldurmanız gerekir; bu, Currencies üzerinden bir döngü ile çağrılır (yani her currencyId, Bundles.setInExperiencePurchaseHandler(currencyId, awardInExperiencePurchase) ile handler'a bağlıdır).
Paketleri tanımla
Deneyiminizde sunulabilir tüm paketler ReplicatedStorage.Bundles.Configs.Bundles içinde tanımlanabilir; tipler aynı klasördeki Types scriptinden dışa aktarılmıştır.
Eğer bir devProductId kullanıyorsanız, paketin ana devProductId'sini deneyiminizdeki ile eşleştirmenizi gerektirir. Bu, paketi satın almak için MarketplaceService üzerinden yönlendirilecektir. Satışların ayrı bir şekilde takip edilmesini kolaylaştırmak için pakete yeni bir geliştirici ürünü kullanmanız şiddetle önerilir.
Eğer birden fazla ürün bulunan bir paket istiyorsanız ve bunlar deneyiminizde geliştirici ürünleri ile zaten temsil ediliyorsa, ürün fiyatı/assetId/isimlerini açıkça ayarlamanıza gerek yoktur, bunlar ürün bilgileri üzerinden alınacaktır:
README{itemType = ItemTypes.ItemType.DevProduct,devProductId = <DEV_PRODUCT_ID>,metadata = {caption = {text = "x1",color = Color3.fromRGB(236, 201, 74),} -- Altyazı isteğe bağlıdır! Bu alanı da çıkarabilirsiniz}},
Aksi takdirde, bu ürün bilgilerini manuel olarak yapılandırabilirsiniz:
README{itemType = ItemTypes.ItemType.Robux,priceInRobux = 49,icon = <IMAGE_ASSET_ID>,metadata = {caption = {text = "x1",color = Color3.fromRGB(236, 201, 74),} -- Altyazı isteğe bağlıdır! Bu alanı da çıkarabilirsiniz}},
Örneğin, tüm paketiniz muhtemelen şöyle görünecektir:
READMElocal starterBundle: Types.RelativeTimeBundle = {bundleType = Types.BundleType.RelativeTime,-- Eğer bir dev ürünü kullanmak istiyorsanız, yalnızca bir pakete ait olan eşsiz bir devProductId sağlamanız gerekir.-- Paket fiyatı ve simgesini geliştirici ürününden alacağızpricing = {priceType = CurrencyTypes.PriceType.Marketplace,devProductId = <DEV_PRODUCT_ID>,},-- Aksi takdirde, bir dev ürünü yerine deneyim içi para birimi kullanmak istiyorsanız, aşağıdakileri kullanabilirsiniz:-- Fiyat burada deneyim içindeki para biriminde, Robux değil-- pricing = {-- priceType = CurrencyTypes.PriceType.InExperience,-- price = 79,-- currencyId = <CURRENCY_ID>,-- icon = <IMAGE_ASSET_ID>,-- },includedItems = {[1] = {-- Ürün geliştirici ürünü aracılığıyla satılmadığından, Robux cinsinden değerini belirtin ve bir simge verin-- priceInRobux, Paketlerin paket fiyatı ile içindekilerin toplam değeri arasında ilişki göstermesine yardımcı oluritemType = ItemTypes.ItemType.Robux,priceInRobux = 49,icon = <IMAGE_ASSET_ID>,-- Alternatif olarak, eğer bu bir dev ü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>-- Eğer gerekirse UI'ye özgü daha fazla isteğe bağlı meta veri alanı vardırmetadata = {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, -- Bir kez satın alındıktan veya süresi dolduktan sonra geçerli değildir; deneyiminiz tekrar prompte ulaşmaya çalışsa bile. Test aşamasında bunu false yapabilirsiniz.durationInSeconds = 900, -- 15 dakikaincludesOfflineTime = false, -- Sadece deneyimde geçen süre sayılırmetadata = {displayName = "BAŞLANGIÇ PAKETİ",description = "Yüzde 75 tasarruf edin ve başlanğıç yapın!",},}
Sunucu mantığını entegre et
ReplicatedStorage.Bundles.Server.Examples.BundlesExample'a bir göz atın, bu betik Paketler özellik paketi ile sunucunuzun nasıl etkileşime gireceğini ve yukarıdaki yöntemleri gösterir. Aşağıdaki parçalar bu betikten alınmıştır.
Paketler özellik paketini deneyiminize sürükledikten sonra dört şeyi bağlamanız gerekecek:
Bundles.setPurchaseHandler aracılığıyla satın alma işlemi sırasında ürünleri ödüllendirmek için hangi fonksiyonların çağrılacağını belirlemek için satın alma yönetici işlem bağlayıcılarını bağlayın.
BundlesExamplelocal function awardMarketplacePurchase(_player: Player, _bundleId: Types.BundleId, _receiptInfo: { [string]: any })-- Oyuncu verilerini güncelle, ürünleri ver, vb.-- ... VE receiptInfo.PurchaseId'yi kaydederek kullanıcı zaten bu pakete sahip mi kontrol edebilirsiniztask.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 et-- Oyuncu verilerini güncelle, ürünleri ver, vb.-- Oyuncudan parayı düştask.wait(2)return trueendlocal function initializePurchaseHandlers()local bundles = Bundles.getBundles()for bundleId, bundle in bundles do-- Paket, geliştirici ürünü ile ilişkili değilse pazar geçerli fiyat türü olamazif not bundle or bundle.pricing.priceType ~= "Marketplace" thencontinueendBundles.setPurchaseHandler(bundleId, awardMarketplacePurchase)receiptHandlers[bundle.pricing.devProductId] = receiptHandlerend-- Eğer paketler için kullandığınız her hangi bir deneyim içi para biriminiz varsa, burada handler'ı ayarlayınfor currencyId, _ in Currencies doBundles.setInExperiencePurchaseHandler(currencyId, awardInExperiencePurchase)endendMarketplaceService.ProcessReceipt için mantığınızı bağlayın, ancak bu, deneyimizin zaten satışta geliştirici ürünleri varsa başka bir yerde yapılandırılabilir. Temelde, bir geliştirici ürünü makbuzu işlenirken, şimdi Bundles.getBundleByDevProduct çağrılır; eğer ürün bir pakete aitse, script Bundles.processReceipt çağrısını yapar.
BundlesExample-- Pazar makbuzunu işleyin, böylece oyuncudan ücret alınıp alınmadığını belirleyinlocal 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 handler'ı allocal success, result = pcall(handler, receiptInfo, player) -- Handler'ı çağırarak satın alma mantığının başarılı olup olmadığını kontrol etif not success or not result thenwarn("Makbuzu işleyemedi:", 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, Bundles bununla ilgilensinlocal 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 yönetinreturn falseendPaketler özellik paketinin herhangi bir aktif paketi yeniden yönlendirebilmesi için Players.PlayerAdded:Connect(Bundles.OnPlayerAdded) bağlantısını kurun ve süresi dolmamış olan paketin yeniden yönlendirilmesini sağlayın.
READMElocal function onPlayerAdded(player: Player)-- Oyuncu katıldığında Pakete bildirir, böylece verilerini yeniden yükleyebilirBundles.onPlayerAdded(player)-- Eğer yeni kullanıcılara sunmak istediğiniz başlangıç paketi varsa, burada bunu yönlendirebilirsiniz-- ... Paketler, eğer oyuncu bunu zaten satın aldıysa veya süresi dolduysa bu durumu yönetecektir.-- Bundles.promptIfValidAsync(player, "StarterBundle")-- Bunu burada örnek olması açısından çağırıyorum, bunu istediğiniz zaman veya istediğiniz yerde çağırabilirsiniz.onPromptBundleXYZEvent(player)endPaketleri yönlendirin. Bu oyun oynama durumuna bağlıdır; örnekte, yeni katılan oyunculara bir Başlangıç Paketi ile yönlendirme gerçekleşmektedir.
Paketler özellik paketi mantığı, her oyuncunun zaten satın aldığı bir teklifi tekrar almadığından emin olur veya teklifin süresi dolduysa yönlendirmeyi sağlar (paket yapılandırmasına göre).
Bir oyuncuya bir paketi yönlendirmek istediğiniz her seferde, Bundles.promptIfValidAsync(player, bundleId) çağrısını yapabilirsiniz.
READMElocal function onPromptBundleXYZEvent(player: Player)-- Bir oyuncunun paketi ne zaman yönlendirileceğini belirlemek için istediğiniz deneyim olayını bağlayın-- ... Bu, oyuncunun paketi yönlendirmek için uygunluk kriterlerinizi karşıladığı zaman olacaktır-- ... Örneğin, bir oyuncu katıldığında veya bir oyuncu seviye atladığında bir paketi yönlendirmek isterseniz,task.spawn(Bundles.promptIfValidAsync, player, <Some_Bundle_Id>)-- ... Eğer birden fazla paket oluşturuyorsanız, yukarıdaki işlev çağrısını sarmak için task.spawn() kullanmak, geri sayımlar arasındaki tutarsızlıkları en aza indirecektir.end
Aşağıdaki en iyi uygulama önerilerini, Makbuz Kimliklerinin gereksiz kayıtları konusunda dikkate alın:
Paketler özellik paketi, aynı makbuzu iki kez işlememek için Makbuz Kimliklerini kaydetsede, satın alma akışı, satın alma işleyicisi başarıyla tamamlandıktan sonra başarısız olursa, sizin de tablolarınızda Makbuz Kimliklerini kaydetmiş olmanız gerekir.
Paketler özellik paketi, satın almanın herhangi bir aşamasında başarısız olursa Makbuz Kimliği kaydetmeyecektir, dolayısıyla işlem makbuzunu işlemeden önce tablolarınızda Makbuz Kimliğini kaydettiğinizden emin olmalısınız.
Bu gereknizlik, tüm satın alma mantığının uygun şekilde işlenmesini sağlamak ve veri mağazanız ile Paketler özellik paketinin veri mağazasının nihai tutarlılığa ulaşmasına yardımcı olur; veri mağazanız doğru kaynaktır.
Sabitleri yapılandır
Çekirdek özellik paketi için sabitler iki yerde bulunmaktadır:
Paylaşılan sabitler ReplicatedStorage.FeaturePackagesCore.Configs.SharedConstants içinde yer alır.
Paket özel sabitleri, bu durumda Paketler özellik paketi ReplicatedStorage.Bundles.Configs.Constants içinde yer alır.
Deneyiminizin tasarım gereksinimlerini karşılamak için ayarlamak isteyebileceğiniz ana unsurlar:
- Ses varlık kimlikleri
- Satın alma etkisi süresi ve parçacık renkleri
- Bilgi ekranı çökebilirliğini
Ayrıca, çeviri için dizilmiş dizeleri tek bir konumda bulabilirsiniz: ReplicatedStorage.FeaturePackagesCore.Configs.TranslationStrings.
UI bileşenlerini özelleştir
Paket nesnelerinin renklerini, fontunu ve saydamlığını değiştirerek, paket yönlendirmelerinizin görsel sunumunu ayarlayabilirsiniz. Ancak, herhangi bir nesneyi hiyerarşik olarak hareket ettirirseniz kodun onları bulması mümkün olmayacaktır; bu nedenle kodunuzda yabancı değişiklik yapmanız gerekir.
Bir yönlendirme, iki üst düzey bileşenden oluşur:
- PromptItem – Bir paketteki her bir ürün için tekrarlanan bireysel bileşen (ürün resmi, altyazı, isim, fiyat).
- Prompt – Yönlendirme penceresinin kendisi.
Bilgi ekranı da iki bileşenden oluşur:
- HudItem – Bilgi ekranında her bir menü seçeneğini temsil eden bireysel bileşen.
- Hud – Programatik olarak HudItems ile doldurulur.
Eğer bilgi ekranını daha fazla kontrol etmek istiyorsanız, ReplicatedStorage.Bundles.Objects.BundlesGui içinde bulunan mevcut HUD arayüzünü kullanmak yerine, istediğiniz tasarım gereksinimlerini karşılayacak şekilde nesneleri yer değiştirebilirsiniz. Sadece ReplicatedStorage.Bundles.Client.UIController betiğindeki istemci betiği davranışlarını güncellemeyi unutmayın.
API referansı
Türler
RelativeTime
RelativeTime paketi bir oyuncuya sunulduğunda, zaman süresi dolana kadar kullanılabilir kalır. Bu tür, oyuncunun bilgi ekranında görüntülenir ve paket süresi dolana kadar veya oyuncu onu satın alana kadar gelecekteki oturumlarda otomatik olarak yönlendirilir.
Bu paket türüne yaygın bir örnek, tüm yeni oyunculara 24 saat boyunca viewable olan tek kullanımlık bir başlangıç paketi sunmaktır.
| İsim | Tür | Açıklama |
|---|---|---|
| includeOfflineTime | bool | (İsteğe Bağlı) Belirtilmezse, yalnızca deneyimde geçirilen süre kalan teklif süresine sayılacaktır. |
| singleUse | bool | (İsteğe Bağlı) Belirtilmezse, satın alma işlemi satın alındıktan sonra veya süresi dolduktan sonra tekrar etkinleştirilebilir. Belirtilirse, ilk kez satın alındıktan veya süresi dolduktan sonra artık bir daha yönlendirilmez, hatta Bundles.promptIfValidAsync ile bundleId ile çağırırsanız bile yönlendirilmez. |
FixedTime
FixedTime paketi bir oyuncuya sunulduğunda, belirlenen evrensel koordinat zamanının (UTC) sonuna kadar kullanılabilir kalır. Bu tür, oyuncunun bilgi ekranında görüntülenir ve paket süresi dolana kadar veya oyuncu onu satın alana kadar gelecekteki oturumlarda otomatik olarak yönlendirilir.
Bu paket türüne yaygın bir örnek, yalnızca bir ay için geçerli olan bir tatil teklifidir.
OneTime
OneTime paketi, bir oyuncuya sunulduğu an itibarıyla yalnızca o anda geçerlidir. Oyuncunun bilgi ekranında görüntülenmez ve bir oyuncu yönlendirmeyi kapattıktan sonra tekrar açılmaz, yalnızca sunucu tarafından tekrar yönlendirilirse açılabilir.
Bu paket türüne yaygın bir örnek, bir oyuncunun deneyim para birimi bittiğinde, hemen daha fazla satın alması için sunulan bir tekliftir.