島内の取引では、Verse を使用して、島内でアイテムやオファー、バンドル オファーを販売できます。
このガイドでは、独自のアイテム、オファー、バンドル オファーを設定する方法を学びます。 ゲーム内アイテムの販売は、Verse のマーケットプレイス モジュール API を使用して処理されます。
アイテム
アイテムは Verse では利用資格として定義され、使用するとプレイヤーのインベントリから削除される消耗アイテムと、インベントリから削除されずにプレイヤーが使い続けることができる耐久アイテムの 2 つのカテゴリーに分類されます。
全ての Verse 利用資格には次のプロパティがあります。
Name:最大 50 文字の利用資格名
Description:利用資格に表示される最大 500 文字の長い説明。
ShortDescription:利用資格を要約した、小さなダイアログ ボックスで最大 100 文字の短い説明。
Icon:利用資格の画像。
利用資格が有料ランダム アイテムである場合、プレイヤーが受け取る可能性のあるアイテムの正確な数値の確率を説明に含める必要があります。 詳細については、「有料ランダム アイテム」をご参照ください。
Verse の利用資格には次のオプション プロパティもあります。
MaxCount:プレイヤーが一度に所有できるその利用資格の最大数。
Consumable:true に設定すると、利用資格を消費できるようになり、使用回数の合計が減ります。 false に設定すると、利用資格は永久的なアイテムとなり、使用しても消費されません。
PaidArea:true に設定すると、その利用資格は有料エリアへのアクセスを提供します。
PaidRandomItem:true に設定すると、これらの利用資格はコンテンツの購入または引き換えによって取得され、ランダムな報酬を得ることができます。
ConsequentialToGameplay:これを「true」に設定した場合、このアイテムはあなたの島で大きな利点を持つようになります。 詳細は、「ゲームプレイに影響する」をご参照ください。
未使用のアクティブな利用資格があり、IARC アンケートでアプリ内課金を承認しない場合、島の審査に通過しません。
これを回避するには、Verse で利用資格をライブ ゲームで使用する準備が整うまでコメント アウトしてください。 利用資格がコメント アウトされている場合、IARC アンケートでアプリ内課金を申請する必要はありません。
Verse での消耗品利用資格の作成
利用資格は Verse で定義され、利用資格基本クラスから派生します。 次のスニペットは、消耗アイテムの作成を示すものです。 この例では、消耗アイテムとしてトウモロコシの種を作成します。 以下に表示されている種のアイコンを使用できます。
# The base entitlement you should define for ALL of your entitlements in your experience.
my_island_entitlement := class<abstract><castable>(entitlement){}
CornSeedPacket<public> := module:
Name<public><localizes> : message = "Corn seed pack"
Description<public><localizes> : message = "A pack of corn seeds. Opening a pack yields 10 corn seeds for planting."
ShortDescription<public><localizes> : message = "Contains 10 corn seeds for planting."
cornseedpacket<public> := class<concrete>(my_island_entitlement):
var Name<override>:message = CornSeedPacket.Name
利用資格を作成するときは、Verse コードが正常にコンパイルされるように、有効なアイコン テクスチャへのパスを含める必要があります。 このガイドに含まれるトウモロコシの種の袋や、その他のアイコンを無料で受け取ってください。
ストアフロントでの画像として、利用資格アイコンには高品質の正方形の 2 の累乗テクスチャを使用してください。 アイコンとして使用するためにテクスチャを UEFN にインポートする方法については、「アセットのインポート」を参照してください。 Verse でテクスチャなどのアセットを公開する方法については、「アセットの公開」を参照してください。
Verse での永続品利用資格の作成
Verse における耐久品利用資格は消耗品利用資格と同じ形式に従いますが、重要な違いがあり、消耗品は true ではなく false に設定されます。 耐久利用資格はプレイヤーが1回だけ購入でき、プレイヤーは特定の耐久利用資格を1つだけ所有できます。
この例では、耐久性のある利用資格としてシャベルを作成します。シャベル テクスチャのアイコンは、以下のスニペットの後に掲載されています。
Shovel<public> := module:
Name<public><localizes>: message = "Shovel"
Description<public><localizes>: message = "An unbreakable shovel used to dig holes for planting."
ShortDescription<public><localizes>: message = "Digs holes."
shovel<public> := class<concrete>(my_island_entitlement):
var Name<override>:message = Shovel.Name
var Description<override>:message = Shovel.Description
var ShortDescription<override>:message = Shovel.ShortDescription
var Icon<override>:texture = # path to your texture here
デフォルトでは、アイテムは Consumable ではなく、MaxCount は 1 です。 アイテムが有料エリア、有料ランダム アイテム、またはゲームプレイに影響を与える重要な利点を提供する場合は、関連するフィールドをコードで定義する必要があります。
利用資格検証ルール
Verse の有効な利用資格は以下のガイドラインに従う必要があります。 条件を満たさない利用資格の購入は失敗します。
有効な利用資格を定義するルールは次のとおりです。
Name に入力できるのは 50 文字までです。
Description に入力できるのは 500 文字までです。
ShortDescription に入力できるのは 100 文字までです。
Consumable=false の場合、MaxCount は 1 である必要があります。
MaxCount の最大値は 10,000,000 です。
MaxCount < 1 の設定は強制ではありませんが、プレイヤーに 1 個未満のアイテムを付与することはできないため、この設定を行わない場合は失敗します。
利用資格カタログ
利用資格カタログを使用すると、プレイヤーに提供しているすべての利用資格を表示できます。
UEFN で利用資格をリストしたレポートを表示するには、[Tools (ツール)] > [Entitlement Catalog (利用資格カタログ)] をクリックするか、島のクリエイターポータルから直接カタログにアクセスします。
Offers (オファー)
オファーは、アイテムまたはアセットの価格を V-Bucks で指定します。 各オファーには、利用資格の仕様とは別に、独自の名前、説明、アイコンがあります。 オファーは Verse で定義されます。
全てのオファーには次のプロパティがあります。
Name:オファー名。
Description:オファーと共に表示される長い説明文。
ShortDescription:小さなダイアログ ボックスでオファーの概要に使用できる短い説明。
Icon:オファーの画像
EntitlementType:オファーに含まれる利用資格の宣言。
Price:V-Bucks の価格。 50 V-Bucks 以上 5000 V-Bucks 以下である必要があります。 価格は 50 の倍数で設定する必要があります。
単純なオファーの作成
このスニペットは、単純なオファーであるトウモロコシの種の袋の基本オファーを定義します。 利用資格例のトウモロコシの種のアイコンをこのオファーのアイコンとして再利用できます。
CornSeedPacket<public> := module:
Name<public><localizes> : message = "Corn seed pack"
Description<public><localizes> : message = "A pack of corn seeds. Opening a pack yields 10 corn seeds for planting."
ShortDescription<public><localizes> : message = "Contains 10 corn seeds for planting."
corn_seed_pack<public> := class(entitlement_offer):
var Name<override> : message = CornSeedPacket.Name
var Description<override> : message = CornSeedPacket.Description
var ShortDescription<override> : message = CornSeedPacket.ShortDescription
V-Bucks の価格は 50 の倍数で、50~5000 V-Bucks の範囲で設定する必要があります。
プレイヤーの購入前に、各有料ランダム アイテムを入手できる正確な数値の確率を確認できるようにする必要があります。 そうしない場合はフォートナイト デベロッパー規約の違反とみなされ、該当するユーザーとその島は適切な制裁の対象となります。
詳しくは「島内の取引の制限」を参照してください。
固定オファーと代替オファーの作成と変更
同じ利用資格に対して、ホリデー向けの特別価格や入門ボーナスを提供したり、地域ごとに価格を調整したりするための別のオファーを作成することができます。 同じようなオファーを作成する一方で利用資格テストに使用することもできますが、異なるアイコンを使用することでプレイヤーにより魅力的なものを確認できます。 このアイコンを例にしてみましょう。
CornSeedPacketAlternate<public> := module:
Name<public><localizes> : message = "Corn seed pack"
Description<public><localizes> : message = "Special price! Only today!"
ShortDescription<public><localizes> : message = "Special offer half price!"
corn_seed_pack_alternate<public> := class(entitlement_offer):
var Name<override> : message = CornSeedPacketAlternate.Name
var Description<override> : message = CornSeedPacketAlternate.Description
var ShortDescription<override> : message = CornSeedPacketAlternate.ShortDescription
var Icon<override> : texture = # Your texture here
バンドル オファー
バンドルは Verse で定義され、異なるオファーの組み合わせ、同じオファーのスタック、または両方の組み合わせを含めることができます。 単純なオファーと同様に、バンドル オファーは独自の価格、名前、説明を指定し、利用資格やオファーとは異なるアイコンが付いています。 バンドル オファー内にバンドルを含めることで、オファーをネストすることもできます。 一例としては、シャベルとトウモロコシの種の袋が含まれる期間限定バンドルなどがあります。 こうすることで、小さなバンドルをより大きな結合バンドルの構成要素として使用できるようになります。
標準のバンドル タイプは次のとおりです。
スタック バンドル:同じ利用資格の複数のオファーを含むバンドルで、通常は割引価格で提供されます。
複数オファー バンドル:複数の利用資格に対するオファーを組み合わせたバンドルで、スタックされたオファーと通常のオファーの組み合わせも含まれます。
ネストされたオファーの深さは 5 を超えることはできず、超えた場合、試行されたトランザクションは失敗します。 可能な場合は、ネスト オファーを制限するようにしてください。
スタック バンドルの作成
このスニペットは、トウモロコシの種のスタック バンドルを定義します。 バンドルにはオファーのタプル配列が含まれており、これには定義されたオファーとオファーの数を示す int が含まれます。 この場合、このバンドルには 2 つの corn_seed_pack オファーが含まれます。 この例ではアイコンが提供されています。
CornSeedPacketBundle<public> := module:
Name<public><localizes> : message = "Corn seed pack bundle"
Description<public><localizes> : message = "Two packs of corn seeds. Opening a pack yields 10 corn seeds for planting."
ShortDescription<public><localizes> : message = "Two packs of corn seeds containing 10 corn seeds for planting."
corn_seed_pack_bundle<public> := class(bundle_offer):
var Name<override> : message = CornSeedPacketBundle.Name
var Description<override> : message = CornSeedPacketBundle.Description
var ShortDescription<override> : message = CornSeedPacketBundle.ShortDescription
var Icon<override> : texture = # your texture here
複数オファー バンドルの作成
プレイヤーは複数の取引を避けたい場合があるため、異なる利用資格を含む複数のオファーをまとめたバンドルを作成できます。 このスニペットは、プレイヤーに最大数のトウモロコシの袋とシャベルを提供する複数オファー バンドルを作成します。
StarterBundle<public> := module:
Name<public><localizes> : message = "Starter bundle"
Description<public><localizes> : message = "Everything a new player needs. Get fully stocked to start quickly! A shovel that digs holes, and ten packs of corn seeds each containing 10 corn seeds for planting."
ShortDescription<public><localizes> : message = "A shovel that digs holes, and ten packs of corn seeds each containing 10 corn seeds for planting."
starter_bundle<public> := class(bundle_offer):
var Name<override> : message = StarterBundle.Name
var Description<override> : message = StarterBundle.Description
var ShortDescription<override> : message = StarterBundle.ShortDescription
var Icon<override> : texture = # your texture here
Verse では、バンドルに利用資格が直接含まれることはありません。 代わりに、それらには各自の利用資格定義を持つオファーが含まれています。
動的に作成されたオファー
動的に作成されるオファーは、Verse で実行時に生成されるオファーまたはバンドル オファーです。 動的オファーの一般的な使用例は次のとおりです。
クラフトゲームでプレイヤーが保持できる木材の最大量のオファー。
ダンジョン クローラーのダンジョンの入り口で、体力とマナのポーションを最大化するためのバンドル。
わかりやすくするため、ボタンやサインなどの単純なものと結び付けます。 Verse コードで On Interaction > call BuyOffer のフローに従います。
このスニペットでは、プレイヤーが持つことのできる最大量までトウモロコシの種の袋バンドルを販売する方法を紹介します。 購入に失敗した場合はエラー メッセージが表示されます。
TryBuyOffer(Player : player)<suspends>:void =
Purchases := GetPurchasedEntitlements(Player, Entitlements.corn_seed_pack)
var NumPlayerCornSeedPacks : int = 0
if (Purchase := Purchases[0]):
set NumPlayerCornSeedPacks = Purchase(1)
# Limit to at least 1 packet.
# If the player has the maximum amount, the offer displays with a disabled purchase button.
NumCornSeedPacks := Max(1, Entitlements.corn_seed_pack{}.MaxCount - NumPlayerCornSeedPacks)
オファー検証ルール
有効なオファーまたはバンドル オファーは、以下のガイドラインに従う必要があります。 基準を満たさない購入は審査に失敗します。 有効なオファーを定義するルールは次のとおりです。
ネストされたオファーの奥行きが 5 を超えることはできません。
利用資格 ID の合計数は、オファーごとに 100 を超えることはできません。 つまり、一度に販売される異なる利用資格の合計数は最大 100 個となります。
オファーの価格は 50~5000 V-Bucks の間で、50 の倍数でのみ設定できます。
オファー
名のデフォルトのテキストは 50 文字を超えることはできません。オファーの
説明のデフォルトのテキストは 500 文字を超えることはできません。ShortDescriptionのデフォルトのテキストは 100 文字を超えることはできません。オファーには少なくとも 1 つの利用資格が含まれている必要があります。
オファーには利用資格の
MaxCountを超える数量の利用資格を含みません。オファーには
MaxCount> 1 の耐久利用資格を含みません。
オファーの表示場所と閲覧可能なプレイヤーについて制限を設けることができます。 詳しくは、「島内の取引の制限」をご参照ください。
ストアフロントの作成
利用資格とそれに関連するオファーおよびバンドルの設定が完了したら、それらを販売する場所が必要です!
デフォルトの UI
デフォルトのストアフロント UI が開き、追加した全ての利用資格とオファーのリストが表示されます。 リストの最初の利用資格が強調表示され、リストの横のウィンドウに利用資格の概要が表示されます。
ストアフロントには複数のページがあることがあります。
5 項目を超えるリストはスクロール可能です。
プレイヤーは、BuyOffer メソッドを呼び出すか、ShowOffersDialog メソッドを使用してデフォルトのストアフロントを使用することで、購入フローをトリガーします。 以下は、ストアフロントの購入フローをゲーム デザインに結び付けるために使用できる仕掛けの例です。
ボリュームの仕掛け
シーングラフ タイマーの仕掛け
NPC
会話の仕掛け
ストアフロントを設計する際は、購入フローを開始するためにプレイヤーの選択を求めるのがベスト プラクティスです。 この選択をスキップしてプレイヤーに購入フローを強制的に開かせると、プレイヤーの自由な意思を奪い、不満を招くおそれがあります。
全てのオファーには [Purchase (購入)] ボタンと [Inspect (確認)]ボタンがあり、これをクリックするとマーケットプレイス ウィンドウが開き、アイテムを購入したり、オファーに含まれるものを確認したりできます。 [Inspect Bundle (バンドルを確認)] ボタンが表示されるのはバンドルのみです。
[Close (閉じる)] ボタンを選択するとマーケットプレイスが閉じます。
ストアフロントのエクスペリエンスはデベロッパーによって完全に制御されます。
デベロッパーはプレイヤーに提供するアイテムまたはゲームプレイのプロパティを決定します。
さらに、各オファーまたはバンドル オファーの価格も設定します。
自分でストアフロントを作成することも、あらかじめ用意されたフォートナイトのストアフロント UI を使用することもできます。
ストアフロント UI
このスニペットは、あらかじめ作成されたフォートナイト ストアフロント UI を開く汎用イベント コールバックを定義します。 コールバックは、サブスクリプション、ボタンのプッシュ、会話イベントなどから発生します。
OnEvent(Agent:agent):void=
if(Player:= player[Agent]):
spawn{ShowOffersDialog(Player, array{
ExampleOffers.shovel{},
ExampleOffers.cornseedpacket{}
})}
購入の処理
このスニペットには一般的なオファー購入が含まれています。 購入に失敗した場合はエラー メッセージが表示されます。
TryBuyOffer(Player:player, Offer : offer)<suspends>:void =
Result := BuyOffer(Player, Offer)
if (not Result?):
Print("Failed to buy the {offer.name} offer.")デバッグを容易にするため、購入エラーのメッセージに、どの購入が失敗したのかを特定できる情報を含めます。たとえば、上記のエラーメッセージにはオファー名を含めるとよいでしょう。
追加の機能
有料ランダムアイテム
島で有料ランダム アイテムを提供するには、次の 2 つの方法があります。
V-Bucks で直接オファーできます
V-Bucks を使用して利用資格を購入することで、間接的に提供することができます。この利用資格は、ランダムな報酬を引き換えたり、その受け取り確率に影響を与えたりすることができます。
ランダムな報酬を付与するアイテムを作成する場合は、PaidRandomItem を true に設定する必要があります。
ランダム報酬と引き換え可能なコンテンツを提供する場合は、アクセス権のないプレイヤーがランダム報酬を入手できないようにするため、RestrictPaidRandomItems 関数を使用する必要があります。
OnEvent(Agent:agent):void=
if (Player := player[Agent]):
if (RestrictPaidRandomItems[Player]):
Print("Player is not allowed to purchase PaidRandomItems.")
else:
Print("Player is allowed to purchase PaidRandomItems.")
直接的な購入プロンプト
島で直接購入を促すプロンプトを使用する場合は、プレイヤーがそのプロンプトを受け取る資格があるかどうかを判断するために、RestrictDirectPromptsToPurchase 関数を使用する必要があります。
OnEvent(Agent:agent):void=
if (Player:= player[Agent]):
if (RestrictDirectPromptsToPurchase[Player]):
Print("Player is not allowed to receive direct purchase prompts.")
else:
Print("Player is allowed to receive direct purchase prompts.")
ゲームプレイに影響する
販売しているアイテムが島でプレイヤーに有意な利点を提供する場合、ConsequentialToGameplay を true を設定する必要があります。
ゲームプレイに影響を与えるアイテムを購入すると、プレイヤーに時間などの重要な利点がもたらされます。 これは直接的なもの (プレイヤーのゲーム進行速度やパワー、能力を高めるアイテムなど) であったり、間接的なもの (プレイヤーがゲームを進める速度や勝利の可能性に大きく影響を与えるアイテムへのアクセスを可能にするアイテムなど) であったりします。
販売するアイテムに、同じメリットを提供するオファーが提示されると同時に、全てのプレイヤーが無料で利用できる代替手段がある場合、ConsequentialToGameplay を「true」に設定する必要はありません。 ゲームプレイ アイテムがゲームプレイに偶発的ではあるが重要でない影響を与える場合 (異なる環境では異なるコスチュームの配色がわずかに異なる可視性を持つ場合や、異なるエモートが異なるボディ モーションを作成する場合など)、それは重要な影響とは見なされません。
# The base entitlement you should define for ALL of your entitlements in your experience.
my_island_entitlement := class<abstract><castable>(entitlement){}
CornSeedPacket<public> := module:
Name<public><localizes> : message = "Corn seed pack"
Description<public><localizes> : message = "A pack of corn seeds. Opening a pack yields 10 corn seeds for planting."
ShortDescription<public><localizes> : message = "Contains 10 corn seeds for planting."
cornseedpacket<public> := class<concrete>(my_island_entitlement):
var Name<override>:message = CornSeedPacket.Name