GS2-Experience
Game as a Service において、成長要素は不可欠です。 キャラクターの成長をおこない、成長したキャラクターでさらに高難度のコンテンツに挑むゲームサイクルを実装するための経験値・ランク機能を提供します。
ランク
GS2-Experience は経験値の値から自動的にランクの値を計算します。 そのためには、マスターデータを利用してランクアップの閾値となる経験値テーブルを定義する必要があります。 Experience Model でランクアップ閾値を管理し、その下に任意の値を持つプロパティIDをぶら下げることができます。 プロパティIDごとに経験値の値は管理され、経験値の値に基づいてランクが決定します。
ランクキャップ
ランクには上限を設定可能です。 ランクの上限に達した状態で経験値を入手した場合、経験値は破棄されます。
ランクの上限は Experience Model で初期値を設定しますが、プロパティIDごとに個別に引き上げることが可能です。 これにより限界突破の強化をおこなうと、経験値の上限を引き上げるような仕様を実現することが可能です。
報酬加算テーブル
Experience Model にはランクに応じて報酬量を調整する acquireActionRates を設定できます。
倍率には通常の rates に加えて、int64 を超える値を扱える bigRates も定義でき、インフレの激しいゲームでも精度を保ったまま報酬量を制御できます。
スクリプトトリガー
ネームスペースに rankCapScript・changeExperienceScript・changeExperienceDone・changeRankScript・changeRankDone・changeRankCapScript・changeRankCapDone・overflowExperienceScript を設定すると、ランクキャップ取得や経験値変化、ランク変化、キャップ変化、経験値あふれの前後でカスタムスクリプトを呼び出せます。スクリプトは同期・非同期の実行方式を選択でき、非同期では GS2-Script や Amazon EventBridge を介した外部処理にも対応します。
設定できる主なイベントトリガーとスクリプト設定名は以下の通りです。
rankCapScript(完了通知:rankCapDone): ランクキャップ取得の前後changeExperienceScript(完了通知:changeExperienceDone): 経験値変化の前後changeRankScript(完了通知:changeRankDone): ランク変化の前後changeRankCapScript(完了通知:changeRankCapDone): ランクキャップ変更の前後overflowExperienceScript(完了通知:overflowExperienceDone): 経験値あふれ時
マスターデータ運用
マスターデータを登録することでマイクロサービスで利用可能なデータや振る舞いを設定できます。
マスターデータの種類には以下があります。
ExperienceModel: ランク閾値と報酬加算テーブル
マスターデータの登録はマネージメントコンソールから登録する他、GitHubからデータを反映したり、GS2-Deployを使ってCIから登録するようなワークフローを組むことが可能です。
バフによる補正
GS2-Buff と連携すると、ステータスの rankCapValue や AddExperienceByUserId・SubExperienceByUserId などのアクションの
experienceValue をバフで補正できます。イベントやキャンペーン時に取得量やランク上限を一時的に増減させるといった調整が柔軟に行えます。
トランザクションアクション
GS2-Experience では以下のトランザクションアクションを提供しています。
- 検証アクション: ランクの検証、ランクキャップの検証
- 消費アクション: 経験値の減算、ランクキャップの減算
- 入手アクション: 経験値の加算、経験値の設定、ランクキャップの加算、ランクキャップの設定、ランクに応じた報酬の倍率適用
「経験値の加算」を入手アクションとして利用することで、ショップでの商品購入時やミッション達成時の報酬として、直接キャラクターの経験値を増やしてランクアップさせるといった処理が可能になります。また、「ランクキャップの加算」を報酬として設定することで、特定の条件を達成した際に自動的にレベル上限を解放するといった運用も可能です。
実装例
経験値の加算
経験値の加算はゲームエンジン用の SDK では処理できません。
GS2-Quest のクエストのクリア報酬や、GS2-Enhance の強化報酬として経験値を加算するようにしてください。
ランクキャップの引き上げ
ランクキャップの引き上げはゲームエンジン用の SDK では処理できません。
GS2-Exchange で強化素材や、同一キャラクターとの交換の報酬としてランクキャップを引き上げるようにしてください。
経験値の一覧を取得
var items = await gs2.Experience.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).StatusesAsync(
).ToListAsync(); const auto Domain = Gs2->Experience->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
);
const auto It = Domain->Statuses( // experienceName
);
TArray<Gs2::UE5::Experience::Model::FEzStatusPtr> Result;
for (auto Item : *It)
{
if (Item.IsError())
{
return false;
}
Result.Add(Item.Current());
}経験値の取得
var item = await gs2.Experience.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Status(
experienceName: "character_ssr",
propertyId: "property-0001"
).ModelAsync(); const auto Domain = Gs2->Experience->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Status(
"character_ssr", // experienceName
"property-0001" // propertyId
);
const auto item = Domain.Model();所持証明署名の取得
GS2 内の他のマイクロサービスと連携する際に、本当に GS2-Experience でランクや経験値が正しいか保証したデータを求められることがあります。
たとえば、GS2-Experience でプレイヤーのランクを管理しており、GS2-Stamina でそのランクに応じてスタミナの最大値を決定するとします。 GS2-Stamina は所持証明署名とあわせてランクを指定するよう要求します。
これによって、GS2-Stamina は裏で GS2-Experience と通信して、本当にそのランクに達しているかを判断する必要がなくなります。
var result = await gs2.Experience.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Status(
experienceName: "character_ssr",
propertyId: "property-0001"
).GetStatusWithSignatureAsync(
keyId: "grn:gs2:{region}:{yourOwnerId}:key:namespace-0001:key:key-0001"
);
var body = result.Body;
var signature = result.Signature; const auto Domain = Gs2->Experience->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Status(
"character_ssr", // experienceName
"property-0001" // propertyId
);
const auto Future = Domain->GetStatusWithSignature(
"key-0001"
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;
// obtain changed values / result values
const auto Future2 = Future->GetTask().Result()->Model();
Future2->StartSynchronousTask();
if (Future2->GetTask().IsError()) return false;
const auto Result = Future2->GetTask().Result();
const auto Body = Result->Body;
const auto Signature = Result->Signature;