GS2-SkillTree
キャラクターなどの成長要素として一般的に使用されるスキルツリー機能を実現するためのマイクロサービスです。 スキルツリーとは、以下のような木構造を持ち、ノードを解放することでキャラクターのパラメーターを向上させることができる機能を指します。 ノードの解放にはコストが必要で、GS2-SkillTree では各マイクロサービスが提供する《消費アクション》を設定できます。
flowchart TD Base --> Node1[STR+5] Node1 --> Node2[DEF+5] Node2 --> Node3[SPD+5] Node3 --> Node4[STR+5] Node4 --> Node5[DEF+5] Node5 --> Node6[STR+5] Node6 --> Node7[DEF+5] Node3 --> Node10[SPD+5] Node10 --> Node11[DEF+5] Node11 --> Node12[SPD+5] Node12 --> Node13[DEF+5] Node11 --> Node30[SPD+5] Node30 --> Node31[STR+5] Node31 --> Node32[SPD+5] Node7 --> Node20[STR+5] Node13 --> Node20 Node20 --> Node21[STR+5] Node21 --> Node22[STR+5]
前提ノード
各ノードにはツリー構造を作るために《前提ノード》を設定できます。 前提ノードには最大10個のノードを設定でき、前提ノードに設定されたノードが解放状態でなければノードは解放することができません。
前提ノードにはそこに至るまでの全てのノードを設定する必要はなく、直前の1つだけ設定すれば木構造を作成できることにご注意ください。
解放したノードを未解放に戻す
各ノード単位、もしくは全てのノードを未解放状態に戻すことができます。
コストの返還
その際、ノードを解放するのに消費したコストを指定した割合に基づいて返還することができます。
このとき注意しなければならないのは、トランザクションの消費アクションには「反転可能」と「反転不可能」の2種類が存在することです。 「反転可能」な消費アクションについては返還が行われますが「反転不可能」な消費アクションについては返還が行われません。
消費アクションが「反転可能」かは、各マイクロサービスのリファレンスの記載を確認してください。
ツリーの途中にあるノードの操作
ノードに依存しているノードが解放済みの場合、ノードを未解放状態に戻すことはできません。
ノードの一括解放
ノードの解放は複数のノードを一括で解放することができます。 その際依存関係について気をつけながらノードを指定する必要はなく、GS2-SkillTree 側で依存関係の順番を考慮しながら解放処理を行い、解放可能判定を行います。
スクリプトトリガー
ネームスペースに releaseScript
・restrainScript
を設定すると、ノード解放や未解放への戻し処理の前後でカスタムスクリプトを呼び出せます。doneTriggerTargetType
を設定すれば Amazon EventBridge や GS2-Script 経由の非同期実行も選択できます。
設定できる主なイベントトリガーとスクリプト設定名は以下の通りです。
releaseScript
(完了通知:releaseDone
): ノード解放の前後restrainScript
(完了通知:restrainDone
): ノードを未解放に戻す前後
マスターデータ運用
マスターデータを登録することでマイクロサービスで利用可能なデータや振る舞いを設定できます。
マスターデータの種類には以下があります。
NodeModel
: スキルツリーのノード定義
マスターデータの登録はマネージメントコンソールから登録する他、GitHubからデータを反映したり、GS2-Deployを使ってCIから登録するようなワークフローを組むことが可能です。
バフによる補正
GS2-Buff を利用すると、ノードモデルの releaseVerifyActions
・releaseConsumeActions
・restrainReturnRate
をバフで補正し、解放条件やコスト、返還率をイベントに応じて調整できます。
実装例
ノードの解放状態を取得
var domain = gs2.SkillTree.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Status(
);
var item = await domain.ModelAsync();
const auto Domain = Gs2->SkillTree->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Status(
);
const auto Future = Domain.Model();
Future->StartSynchronousTask();
if (Future->GetTask().IsError())
{
return false;
}
ノードの解放
var domain = gs2.SkillTree.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Status(
);
var result = await domain.ReleaseAsync(
nodeModelNames: new string[] {
"node-0001",
}
);
const auto Domain = Gs2->SkillTree->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Status(
);
const auto Future = Domain->Release(
[]
{
const auto v = MakeShared<TArray<FString>>();
v->Add("node-0001");
return v;
}()
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError())
{
return false;
}
ノードの解放状態を元に戻す
var domain = gs2.SkillTree.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Status(
);
var result = await domain.RestrainAsync(
nodeModelNames: new string[] {
"node-0001",
}
);
const auto Domain = Gs2->SkillTree->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Status(
);
const auto Future = Domain->Restrain(
[]
{
const auto v = MakeShared<TArray<FString>>();
v->Add("node-0001");
return v;
}()
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError())
{
return false;
}
ノードの解放状態をリセットする
var domain = gs2.SkillTree.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Status(
);
var result = await domain.ResetAsync(
);
const auto Domain = Gs2->SkillTree->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Status(
);
const auto Future = Domain->Reset(
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError())
{
return false;
}