Система транзакций на острове позволяет продавать различные предметы и предложения, включая предлагаемые наборы, на ваших островах при помощи Verse.
В этом руководстве мы разберём, как создавать предметы, предложения и предлагаемые наборы. Затем мы научимся использовать модуль магазина в API Verse для работы с системой внутриигровых продаж.
Предметы
Предметы определяются в коде Verse как права доступа, для которых предусмотрено две категории: расходуемые предметы, которые удаляются из инвентаря игрока при использовании, а также предметы длительного пользования, которые не расходуются и не удаляются из инвентаря при использовании.
Любое право доступа в Verse имеет следующие свойства:
Name: название права доступа (не более 50 символов)
Description: длинное описание, отображаемое рядом с правом доступа (не более 500 символов).
ShortDescription: краткая информация о праве доступа в небольшом диалоговом окне (не более 100 символов).
Icon: изображение права доступа.
Если ваше право доступа является случайным платным предметом, в его описании необходимо указать чёткую числовую вероятность выпадения того, что может получить игрок. Дополнительную информацию см. в статье Случайные платные предметы.
Право доступа в Verse также может иметь следующие дополнительные свойства:
MaxCount: максимальное количество этого права доступа, которое может быть у игрока в любой момент времени.
Consumable: если true, право доступа может быть использовано, а его количество будет уменьшаться по мере использования. Если false, право доступа является нерасходуемым и не будет уменьшаться по мере использования
PaidArea: если true, право доступа будет предоставлять доступ к платной области.
PaidRandomItem: если true, эти права доступа приобретаются или погашаются с контентом, позволяющим получить случайную награду.
ConsequentialToGameplay: если для этого поля установлено значение true, продаваемый предмет даёт игрокам существенное преимущество на вашем острове. Подробнее см. в разделе Влияние на игровой процесс.
Если у вас есть активные неиспользуемые позиции, но вы не подтвердите наличие покупок в приложении в вопроснике IARC, ваш остров не пройдёт модерацию.
Чтобы обойти это, вы можете закомментировать свои предметы в Verse до тех пор, пока не будете готовы использовать их в реальной игре. Если предметы закомментированы, вам не нужно заявлять наличие покупок в приложении в опроснике IARC.
Создание расходуемой позиции
Права доступа определяются в 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 успешно скомпилировался, при создании права доступа необходимо указать путь к допустимой текстуре значка. Как значок пакета с семенами кукурузы, так и другие значки из этого обучающего материала вы можете использовать совершенно бесплатно!
Для того, чтобы значки прав доступа были выполнены на должном уровне, необходимо использовать квадратные текстуры с размерами, соответствующими степени двойки. Более подробную информацию о том, как импортировать текстуры в редактор UEFN для использования в качестве значков, можно найти в разделе Импорт ресурсов. Более подробную информацию о том, как открывать доступ к таким ресурсам, как текстуры в Verse, можно найти в разделе Доступ к ресурсам.
Создание прав доступа длительного пользования в Verse
Долговечные права доступа в Verse выполняются в том же формате, что и расходуемые права доступа, но с одним ключевым отличием: в их случае для свойства Consumable указывается false вместо true. Долговечные права доступа можно приобрести только единожды: в инвентаре игрока может находиться только одно долговечное право доступа определённого типа.
В этом примере в качестве такого права доступа мы создадим лопату. Значок для текстуры лопаты уже есть во фрагменте ниже.
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
По умолчанию предметы не являются расходуемыми и для MaxCount у них установлено значение 1. Если предмет является платной областью или случайным платным предметом, либо даёт существенное преимущество в игре, соответствующие поля должны быть определены в коде.
Правила проверки прав доступа
Все права доступа в Verse должны соответствовать приведённым ниже требованиям. Покупки прав доступа, не соответствующих этим требованиям, будут отклонены.
В отношении права доступа действуют следующие требования:
для Name действует лимит в 50 символов;
для Description действует лимит в 500 символов;
для ShortDescription действует лимит в 100 символов;
для MaxCount должно быть установлено значение 1 при Consumable=false;
значение MaxCount не может превышать 10 000 000.
MaxCount < 1 формально не запрещён, однако приведёт к ошибке, поскольку невозможно выдать игроку менее одного предмета.
Каталог прав доступа
В «Каталоге продуктов» вы найдёте все права доступа, которые предлагаете игрокам.
Отчёт со списком прав доступа вы можете найти в редакторе UEFN по пути Инструменты > Каталог прав, или непосредственно в самом каталоге вашего острова на Творческом портале.
Предложения
Предложение подразумевает наличие у предмета или ресурса определённой цены (в В-баксах). У каждого предложения есть собственное название, описание и значок — отдельно от информации о характеристиках прав доступа. Предложение определяется в коде Verse.
У каждого предложения имеются следующие свойства:
Name — название предложения;
Description — длинное описание, отображаемое рядом с предложением;
ShortDescription — краткое описание предложения для небольших диалоговых окон;
Icon — изображение предложения;
EntitlementType: указание права доступа, включенного в предложение.
Price — цена в В-баксах. Должна быть в диапазоне от 50 до 5000 В-баксов включительно. Цена должна быть кратна 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
Цена в В-баксах должна быть кратна 50 и находиться в диапазоне от 50 до 5000 В-баксов.
Перед покупкой игрокам необходимо предоставить точную информацию о вероятности (в числовых значениях) выпадения каждого из возможных случайных платных предметов. Невыполнения этого требования будет являться нарушением «Правил для разработчиков Fortnite», которое может повлечь за собой ограничительные меры в отношении вас и вашего острова.
Более подробная информация приведена в разделе Ограничения на транзакции на острове.
Создание и изменение фиксированных и изменяемых предложений
Вы можете добавлять праздничные скидки на те или иные права доступа, бонусы для первых покупок, а также указывать различные цены в зависимости от области, в которой представлено предложение. Кроме того, вы можете создать два одинаковых предложения, но с разными значками, чтобы узнать, какое из предложений окажется более привлекательным для игроков. Используем этот значок в качестве примера.
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. Старайтесь по возможности ограничивать количество вложенных предложений.
Создание набора продуктов одного типа
В этом фрагменте кода мы создаём набор из семян кукурузы. Набор представляет собой массив кортежей, каждый из которых содержит заранее определённое предложение и целочисленное значение, которое определяет количество для такого предложения. В данном примере у нас будет два 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 это будет выглядеть так: при взаимодействии > вызов 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;
Общее количество идентификаторов прав доступа не может превышать 100 в рамках одного предложения. Иными словами, общее количество различных прав доступа, продаваемых за раз, не должно превышать 100.
цена предложения должна находиться в диапазоне от 50 до 5000 В-баксов и быть кратной 50;
длина текста названия предложения по умолчанию (
Name) не может превышать 50 символов;длина текста описания предложения по умолчанию (
Description) не может превышать 500 символов;длина текста краткого описания предложения по умолчанию (
ShortDescription) не может превышать 100 символов;Предложение должно содержать хотя бы 1 право доступа.
Предложение не содержит количество права доступа, превышающее значение
MaxCountправа доступа.Предложение не должно содержать право доступа длительного пользования со значением
MaxCount> 1.
Вы можете установить ограничения для мест появления ваших предложений и категорий просматривающих их игроков. Подробнее см. в разделе Ограничения на транзакции на острове.
Создание витрины
Мы успешно подготовили наши права доступа, а также предложения и наборы, поэтому самое время создать место, где мы будем их продавать!
Интерфейс по умолчанию
Интерфейс стандартной витрины сразу представляет список всех добавленных вами позиций и предложений. Первое право доступа в списке выделяется, а рядом со списком в отдельном окне отображается его описание.
Витрина может включать несколько страниц.
Списки, состоящие более чем из пяти элементов, можно прокручивать.
Процесс покупки игрок запускает вызовом метода BuyOffer или, при использовании стандартной витрины, с помощью метода ShowOffersDialog. Ниже представлены некоторые устройства, которые можно использовать для интеграции процесса покупки через витрину в вашей игре.
Устройства «Область»
Устройство «Таймер Scene Graph»
Персонажи
Устройство «Диалог»
При создании витрины рекомендуется предоставить игроку выбор для начала взаимодействия с системой покупок. Отсутствие такого выбора у игроков и их принуждение к взаимодействию с системой покупок ограничивает их свободу как игроков и может привести к снижению интереса.
Для всех предложений предусмотрены кнопки Купить и Посмотреть, по нажатию на которые открываются окна магазина для покупки или просмотра предложения. Только у наборов может быть кнопка Посмотреть набор.
Магазин закрывается нажатием на кнопку Закрыть.
Принцип работы витрины полностью определяется разработчиком.
Вы сами решаете, какие предметы или игровые свойства будут предлагаться игрокам.
Вы самостоятельно определяете цену для каждого из предложений, включая предложения наборов.
Вы можете создать собственную витрину или же воспользоваться уже готовым интерфейсом витрины Fortnite.
Интерфейс витрины магазина
В этом фрагменте кода мы определяем стандартный обратный вызов события для открытия заготовки интерфейса витрины Fortnite. Обратный вызов может быть вызван подпиской, нажатием кнопки, событием диалога и т. д.
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.")Чтобы упростить отладку неудачных покупок, добавьте в сообщение об ошибке информацию, позволяющую определить, какая именно покупка завершилась сбоем — например, укажите в нём название предложения.
Дополнительные функции
Случайные платные предметы
Предусмотрены два способа предложения случайных платных предметов на вашем острове:
Можно напрямую предлагать приобрести их за В-баксы
Вы можете предлагать их косвенно, позволяя игрокам приобретать за В-баксы права на получение предметов, которые затем можно обменять или которые влияют на вероятность получения случайной награды.
При создании предмета, который будет выдавать игрокам случайную награду, для 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