GS2-Quest

進捗管理機能

ゲームの進捗管理や、クエストの進捗管理を行います。

クエスト

クエスト はインゲームの基本単位で、インゲームを開始する時に選択するエンティティです。 クエストには挑戦に必要なコストと、挑戦によって得られる報酬を設定でき、GS2-Quest はその開始・終了をAPIとして受け付けます。 つまり、GS2-Quest はインゲームの内容には関与しません。

クエストの挑戦コスト

クエストを開始状態にするために必要なコストを設定します。 一般的には GS2-Stamina で管理するスタミナを消費したり、GS2-Inventory で管理するアイテムを消費するようなコストを設定します。

クエストのクリア報酬

クエストに挑戦し、クリアした場合に得られる報酬を設定できます。 報酬には複数の種類を用意することができます。この機能を利用すれば、一定の確率でレアモンスターが出現するバージョンのクエストが開始され、報酬が通常より豪華になるような設定が可能です。

初回クリア報酬

クエストを初めてクリアしたときにのみ、追加の報酬を得られるよう設定が可能です。

クリア報酬の減額

クエスト内で出現したモンスターを倒さなかった場合や、宝箱を見落とした場合にクエスト報酬を報酬から減額できます。

クエストの開始APIのレスポンスにはクエスト内で得られる報酬の最大値が応答され、クエストの完了APIにはそのうち実際に入手した数量をレポートします。 そのレポートの際に報酬を減らして報告することで減額が行われます。 報告の際に最大値を超える報酬をレポートしようとするとエラーが発生します。

クエストの失敗報酬

クエストに挑戦し、クリアできなかった場合に得られる報酬を設定できます。 クエストの失敗した場合は、挑戦時に支払ったスタミナを払い戻すような処理を実現できます。

クエストの前提条件

クエストに挑戦するために、他のクエストをクリアしていることを条件として設定することができます。 これによって、クエストをチェーンのように繋ぐことができます。

クエストの挑戦可能期間

クエストには挑戦可能期間として GS2-Schedule のイベントを関連づけることができます。 挑戦可能な期間は開始APIの実行時に判定され、終了処理時には判定されません。

クエストグループ

複数のクエストをまとめるエンティティです。

クエストグループの挑戦可能期間

クエストグループにも挑戦可能期間として GS2-Schedule のイベントを関連づけることができます。 クエストグループに挑戦可能期間を設定すると、配下のクエスト全てに条件が適用されます。

クエストグループとクエスト両方に挑戦可能な期間を設定した場合は、その両方のイベントが開催期間の時にのみクエストに挑戦可能となります。

実装例

クエストモデルの一覧を取得

    var items = await gs2.Quest.Namespace(
        namespaceName: "namespace-0001"
    ).QuestGroupModel(
        questGroupName: "quest-group-0001"
    ).QuestModelsAsync(
    ).ToListAsync();
    const auto Domain = Gs2->Quest->Namespace(
        "namespace-0001" // namespaceName
    )->QuestGroupModel(
        "quest-group-0001" // questGroupName
    );
    const auto It = Domain->QuestModels(
    );
    TArray<Gs2::UE5::Quest::Model::FEzQuestModelPtr> Result;
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }

クエストのクリア状況を取得

    var item = await gs2.Quest.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).CompletedQuestList(
        questGroupName: "main"
    ).ModelAsync();
    const auto Domain = Gs2->Quest->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->CompletedQuestList(
        "main" // questGroupName
    );
    const auto item = Domain.Model();

クエストを開始

    var result = await gs2.Quest.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).StartAsync(
        questGroupName: "group-0001",
        questName: "quest-0001",
    );
    const auto Domain = Gs2->Quest->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->CompletedQuestList(
        "main" // questGroupName
    );
    const auto item = Domain.Model();

進行中のクエストを取得

    var item = await gs2.Quest.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Progress(
    ).ModelAsync();
    const auto Domain = Gs2->Quest->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->Progress(
    );
    const auto item = Domain.Model();

クエストの終了を報告

    var result = await gs2.Quest.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Progress(
    ).EndAsync(
        isComplete: true,
        rewards: new [] {
            new Gs2.Unity.Gs2Quest.Model.EzReward{},
        },
        config: null
    );
    const auto Domain = Gs2->Quest->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->Progress(
    );
    const auto Future = Domain->End(
        true,
        []
        {
            const auto v = MakeShared<TArray<TSharedPtr<Gs2::Quest::Model::FReward>>>();
            v->Add({});
            return v;
        }(), // rewards
        nullptr // config
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;

その他の機能

クエストの分岐

通常の仕様では、クエストを分岐させることはできません。 クエスト内に2つの出口を用意し、どちらの出口を利用したかで次に挑戦できるクエストが変動するようなクエストを実装したい場合は以下のようなデータ構造を検討してください。

クエスト名前提クエスト
Quest1
Quest1aPhantom
Quest1bPhantom
Quest2aQuest1a
Quest2bQuest1b

ややこしいですが、クエストの前提条件となるクエストにはマスターデータ内に存在しないクエスト名を設定できます。

今回は Quest1a / Quest1b は Phantom という名前のクエストを前提条件としていますが、Phantom というクエストはマスターデータ内に存在しません。 そのため、Quest1a / Quest1b は絶対に挑戦可能にならないクエストということになります。

Quest2a / Quest2b は Quest1a / Quest1b を前提クエストとしています。 この状態で、Quest1 のクリア報酬に 「Quest1a をクリア状態にする」「Quest1b をクリア状態にする」という報酬を設定しておき 利用した出口に応じて、どちらかのクリア状態を操作する報酬をプレイヤーに与えるかを決定します。

Quest1a / Quest1b が存在する理由は、マスターデータ内に存在しないクエストはクリア状態にできないためです。

graph TD
  Quest1 -- if use exit A --> Quest1a
  Quest1 -- if use exit B --> Quest1b
  phantom --- Quest1a
  phantom --- Quest1b
  Quest1a --> Quest2a
  Quest1b --> Quest2b
  linkStyle 2 stroke:#ccc,stroke-dasharray:4
  linkStyle 3 stroke:#ccc,stroke-dasharray:4
  style phantom fill:#fff,stroke:#ccc,stroke-dasharray:4
  style Quest1a fill:#fff,stroke:#ccc,stroke-dasharray:4
  style Quest1b fill:#fff,stroke:#ccc,stroke-dasharray:4

詳細なリファレンス