섬 내 거래 Verse 장치에는 섬 내 거래를 위한 템플릿이 있습니다. 여기에는 Verse API의 마켓플레이스 모듈 핵심 요소가 모두 구현되어 있습니다. 아이템, 오퍼, 번들 오퍼와 기본 상점 UI를 활용한 구매 처리가 포함되어 있습니다. 이 코드를 빠르게 상점을 구성하기 위한 가이드로 사용하세요.
장치에 액세스하는 방법은 다음과 같습니다.
Verse 익스플로러(Verse Explorer)로 이동합니다.
프로젝트 이름을 우클릭합니다.
프로젝트에 새로운 Verse 파일 추가(Add new Verse File to project)를 선택합니다.
섬 내 거래 장치(In-Island Transactions Device)를 선택합니다.
아이템 생성
아이템은 Verse에서 권한으로 정의되며, 사용 시 플레이어 인벤토리에서 제거되는 소모성 아이템과 인벤토리에서 제거되지 않고 플레이어가 계속 사용할 수 있는 내구성 아이템의 두 가지 카테고리로 분류됩니다.
EntitlementInfo 모듈
모듈은 다수 파일에서 사용하도록 재배포 가능한 최소 단위 코드입니다. 모듈은 시간이 지나면서 변경될 수 있지만, 모듈이 이미 사용되고 있는 파일에서 종속성은 깨지지 않습니다. 길거나 반복적인 코드 단위(예: 권한 정의)는 다른 파일로 분리하고, using 조건문으로 임포트할 수 있습니다.
EntitlementInfo 모듈은 템플릿 내 모든 권한의 이름, 설명, 짧은 설명을 수집합니다. 동일한 상품에 대해 권한을 정의할 때마다 이름이나 기타 변수를 반복해서 다시 정의할 필요 없이 이 모듈을 필요한 모든 파일에 포함할 수 있습니다. 이를 통해 권한 정보를 보다 쉽게 수정할 뿐 아니라 권한 디버깅 과정의 복잡성도 줄일 수 있습니다.
# Setup your entitlements and offers data, in this case that's your Names, Descriptions & Short Descriptions as well as any other data you want to be constant
EntitlementInfo<public> := module:
Potion<public> := module:
Name<public><localizes>:message = "Potion"
Description<public><localizes>:message = "Adds +10 health when used. Green so you know its healthy!"
권한 정의하기
모든 Verse 권한은 다음 프로퍼티를 갖고 있습니다.
Name: 50자까지 입력 가능한 권한 이름입니다.
Description: 500자까지 입력 가능하며 권한과 함께 표시되는 긴 설명입니다.
ShortDescription: 최대 100자로 권한을 요약한 간략한 설명입니다.
Icon: 권한의 이미지입니다.
권한이 유료 랜덤 아이템일 경우, 설명에 플레이어가 아이템을 획득할 정확한 확률 수치를 포함해야 합니다. 자세한 정보는
Verse 권한에는 다음과 같은 선택적 프로퍼티도 있습니다.
MaxCount: 플레이어가 아무 때나 보유 가능한 제품의 최대 권한 개수입니다.
Consumable: true로 설정하면
ConsumeEntitlement로 권한을 소모할 수 있으며, 사용자의 에픽 시스템 내 총 권한 소모 횟수가 줄어듭니다. False로 설정하면 권한이 영구 아이템이 되어 사용해도 소모되지 않습니다.PaidArea: true로 설정할 경우 권한이 지불 시 이용 가능한 영역에 대한 액세스를 제공합니다.
PaidRandomItem: true로 설정할 경우, 랜덤 보상을 얻으려면 권한을 구매하거나 콘텐츠로 교환해야 합니다.
ConsequentialToGameplay: true로 설정할 경우 해당 아이템은 섬에서 실질적인 이득을 제공합니다. 자세한 내용은 게임플레이 결과에 영향 있음을 참고하세요.
아래 스니펫에서는 앞서 정의한 EntitlementInfo 모듈을 사용하여 권한 정의를 구성합니다. 소모성 권한을 정의하려면 Consumable을 true로 설정해야 합니다.
권한을 정의하기 전 경험 내 권한에 사용될 기본 권한 클래스를 생성해야 합니다.
이 경우에는 feature_example_entitlement입니다.
각 권한 정의마다 아이콘 텍스처의 파일 경로를 반드시 정의해야 하며, 정의하지 않을 시 권한 정의가 유효성 검사를 통과하지 못합니다.
Entitlements<public> := module:
using {EntitlementInfo}
# The base entitlement you should define for ALL your entitlements in your experience
feature_example_entitlement<public> := class<abstract><castable>(entitlement){}
basic_sword<public> := class<concrete>(feature_example_entitlement):
var Name<override>:message = Sword.Name
기본적으로 아이템은 Consumable이 아니며 MaxCount가 1입니다. 아이템이 유료 영역이거나, 유료 랜덤 아이템이거나, 게임플레이 결과에 영향이 있는 실질적 이점을 제공하는 경우, 코드에서 관련 필드를 정의해야 합니다.
권한 오퍼
오퍼는 아이템 또는 에셋의 가격을 V-Bucks로 지정합니다. 오퍼에는 각각 자체적인 이름, 설명, 아이콘이 있으며, 이는 권한 사양과는 별개입니다. 오퍼는 Verse에서 정의합니다.
권한을 판매하려면 해당 권한에 대한 오퍼가 반드시 필요합니다.
모든 오퍼에는 다음 프로퍼티가 있습니다.
Name: 오퍼의 이름입니다.
Description: 오퍼와 함께 표시되는 긴 설명입니다.
ShortDescription: 작은 대화 상자로 오퍼를 요약하기 위한 간략한 설명입니다.
Icon: 오퍼의 이미지입니다.
EntitlementType: 오퍼에 포함된 권한 선언입니다.
가격: V-Bucks 기준 가격입니다. 50 V-Bucks보다 작거나 5000 V-Bucks보다 클 수 없으며, 가격은 50 단위로 설정되어야 합니다.
이 스니펫은 기본 권한 오퍼 정보를 위해 기존에 정의한 EntitlementInfo 모듈을 활용하여 권한 오퍼를 정의하는 방법을 보여줍니다. 또한 아이콘, 오퍼의 권한을 정의하는 EntitlementType와
그 가격을 V-Bucks로 설정해야 합니다. 고정 값이나 국가 코드에 따라 오퍼의 최소 구매 연령을 선택적으로 정의하거나, 플랫폼 제품군, 또는 이러한 요소의 조합으로 오퍼에 대한 액세스를 제한할 수도 있습니다. 아래 스니펫에서 몇 가지 예시를 확인할 수 있습니다.
(현재 지역 코드는 지원되지 않으며 이후 출시 버전에서 제공될 예정입니다.)
ExampleOffers<public> := module:
using {EntitlementInfo}
basic_sword<public> := class(entitlement_offer):
var Name<override>:message = EntitlementInfo.Sword.Name
var Description<override>:message = EntitlementInfo.Sword.Description
var ShortDescription<override>:message = EntitlementInfo.Sword.ShortDescription
V-Bucks 가격은 50~5000 V-Bucks 사이여야 하며, 50의 배수로 지정해야 합니다.
유료 랜덤 아이템의 경우, 플레이어가 오퍼 세부 정보에서 각 유료 랜덤 아이템의 정확한 획득 확률 수치를 볼 수 있어야 합니다. 그렇게 하지 못할 경우 포트나이트 개발자 규정 위반으로 간주되며, 개발자와 개발자의 섬에 해당하는 제재가 취해집니다.
자세한 정보는 섬 내 거래 제한을 참고하세요.
번들 오퍼
번들은 Verse에서 정의하며, 여러 오퍼를 조합하거나, 같은 오퍼를 스택으로 제공하거나, 두 가지 방식을 함께 활용할 수도 있습니다. 단순 오퍼와 마찬가지로 번들 오퍼에는 자체적인 가격, 이름, 설명이 지정되어야 하고, 권한 및 오퍼와 구별되는 아이콘이 있어야 합니다. 번들 오퍼 안에 번들을 포함시켜 오퍼를 중첩할 수도 있는데, 삽과 옥수수 씨앗 꾸러미 번들이 포함된 기간 한정 번들을 예로 들 수 있습니다. 이렇게 하면 더 작은 번들을 구성 요소로 사용해 합쳐서 더 큰 번들을 만들 수 있습니다.
일반적인 번들 유형은 다음과 같습니다.
스택 번들: 같은 권한의 여러 오퍼가 포함된 번들로, 대개 할인가로 판매됩니다.
복수 오퍼 번들: 복수의 권한 오퍼가 합쳐진 번들로, 중첩 오퍼와 일반 오퍼를 조합해 포함할 수 있습니다.
번들에는 오퍼의 튜플 배열이 포함됩니다. 여기에는 정의된 오퍼 및 오퍼 개수를 나타내는 int가 포함됩니다.
중첩 오퍼의 중첩 단계는 5단계를 넘을 수 없으며, 넘을 경우 거래 시도 시 실패합니다. 가능하면 오퍼 중첩을 최소화하세요.
아래 스니펫은 물약 팩 번들의 생성을 보여줍니다. 오퍼 정보는 EntitlementInfo 모듈 내에서 정의되며 PotionCount 변수를 포함합니다.
ExampleOffers<public> := module:
using {EntitlementInfo}
<# --- other offer definitions above --- #>
potion_pack<public> := class(bundle_offer) :
var Name<override>:message = EntitlementInfo.PotionPack.Name
구매 제한
권한 구매를 제한하여 한정판 홀리데이 아이템, 프로모션 또는 시즌 오퍼를 생성하고, 지역별 콘텐츠도 생성할 수 있습니다.
GetMinPurchaseAge
GetMinPurchaseAge를 사용하여 특정 entitlement_offer에 대한 맞춤형 구매 제한을 정의할 수 있습니다. 구매 유효성 검사의 일부로 자동으로 호출되므로 정의만 하면 됩니다. 특정 integer 값, 국가 코드 또는 플랫폼 제품군을 정의할 수 있습니다.
(현재 지역 코드는 지원되지 않으며 이후 출시 버전에서 제공될 예정입니다.)
아래는 GetMinPurchaseAge 함수의 몇 가지 예시입니다.
# Optional overrideable function you can use to inform Epic systems what the minimum age a player needs to be to purchase this offer
GetMinPurchaseAge<override>(CountryCode:string, SubdivisionCode:string, PlatformFamily:string)<decides><computes>:int =
# A Hypothetical example where you only want to sell swords to people who don't live in Antarctica
CountryCode <> CountryCodes.Antarctica
return 0
플레이어 권한 유효성 검사
플레이어 권한 유효성 검사는 플레이어가 구매한 권한이 세션 간에 유지되도록 보장하는 데 필요한 단계입니다. 권한의 유효성을 올바르게 검사하지 못하면 권한 중복 또는 권한 상실 등의 문제가 발생할 수 있습니다.
기존 구매 유효성 검사
아래 스니펫은 플레이어가 세션에 입장할 때 실행되는 간단한 권한 유효성 검사를 보여줍니다. 먼저, 들어오는 플레이어가 OnPurchasesChanged 이벤트에 구독되었는지 확인합니다. 다음으로, 아직 구독되지 않았다면 들어오는 플레이어를 구독시킵니다. 마지막으로, ValidatePreviousPurchases를 사용하여 해당 플레이어가 구매한 모든 권한의 기록을 가져옵니다.
이 단계에서 플레이어의 권한에 대한 유효성 검사도 실행하여 경험 내 저장된 데이터가 마켓플레이스 API상의 소유 정보 및 플레이어 인벤토리와 일치하는지 확인하는 것이 좋습니다.
OnPlayerJoin(InPlayer:player):void =
Subscription := GetEntitlementsChangedEvent(InPlayer, Entitlements.feature_example_entitlement).Subscribe(OnPurchasesChanged)
if (set EntitlementChangeSubscription[InPlayer] = option{Subscription}):
Print("Adding entitlement Change Subscription player subscription")
# On players joining you are likely going to want to run some validation checks to make sure that any data you save
구매 처리하기
권한 구매 처리에 사용되는 두 가지 주요 함수는 BuyOffer 및 OnPurchasesChanged입니다. 첫 번째 함수는 플레이어에게 오퍼를 표시하고 구매의 유효성을 검사하는 로직을 처리합니다. 두 번째 함수는 성공적인 거래나 환불 로직을 처리합니다. 아래 스니펫에는 두 함수가 나와 있습니다.
# Base Implementation of how to present players with an offer to purchase in your experience
TryBuyOffer(Player:player, Offer:entitlement_offer)<suspends>:void =
Result := BuyOffer(Player, Offer)
if (Result?):
# do nothing it should respond in the purchase subscription, see OnPurchasesChanged for details
Consumables 처리
소모성 권한을 소모하려면 마켓플레이스 Verse API의 ConsumeEntitlement 함수를 사용해야 합니다. 소모에 성공하면 소모 이후 생성되는 효과에 대한 로직을 처리해야 합니다. 플레이어가 소유한 권한의 Count는 함수에서 소모한 Count만큼 감소합니다.
아래의 스니펫은 특정 플레이어가 보유한 특정 권한의 소모 방식을 보여줍니다.
# Base Implementation of how to flag a consumable in your experience as being consumed
TryConsumeEntitlement(Player:player, Entitlement:concrete_subtype(entitlement), NumberConsuming:int)<suspends>:void =
Result := ConsumeEntitlement(Player, Entitlement, ?Count := NumberConsuming)
if (Result?):
Print("Successfully consumed entitlement!")
영구 권한은 소모할 수 없습니다. 소모를 시도하면 ConsumeEntitlement가 실패하게 됩니다.
권한 부여
V-Bucks가 필요한 권한 구매와 달리, GrantEntitlement 메서드를 사용하면 플레이어에게 권한을 부여할 수도 있습니다.
권한 부여는 프로모션 아이템, 소모품 무료 샘플 제공, 버그 또는 글리치로 인해 사라진 아이템 복구 등에 사용할 수 있습니다.
이 스니펫은 플레이어에게 권한을 부여하는 방법을 보여줍니다.
# Base Implementation of how to give players a entitlement in your experience without them purchasing it
TryGrantEntitlement(Player:player, Entitlement:concrete_subtype(entitlement), NumberToGrant:int)<suspends>:void =
Result := GrantEntitlement(Player, Entitlement, ?Count := NumberToGrant)
if (Result?):
Print("Successfully granted a player your entitlement!")
권한의 최대 개수를 초과해 부여하거나, 영구 권한을 1개보다 많이 부여하려 하면 권한 부여에 실패합니다.
플레이어에게 권한 오퍼 표시
Marketplace module은 ShowOffersDialog 함수를 통해 섬 내에서 사용할 수 있는 기본 상점 UI를 제공합니다. 아래의 스니펫은 특정 플레이어에게 오퍼를 표시하는 상점을 표시하는 방법을 보여줍니다.
# Base Implementation of how to show an array of offers to the player that are available for purchase
ShowArrayOfOffers(Player:player)<suspends>:void =
ShowOffersDialog(Player, array{ExampleOffers.basic_sword{}, ExampleOffers.potion{}, ExampleOffers.potion_pack{}, ExampleOffers.potion_thanksgiving{}, ExampleOffers.potion_gib{}})완성된 코드
using { /Fortnite.com/Devices }
using { /Fortnite.com/Marketplace }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /Verse.org/Assets }
using { /Verse.org/Simulation }