> For the complete documentation index, see [llms.txt](/ja/llms.txt)


# 3. 冒険を開始し、報酬を受け取る

冒険をクリアしたら、コインがもらえるようにします。開始から報酬の受け取りまで作ってみましょう。



一覧に出てきた「森の探索」を、実際に遊べるようにしましょう。「開始」で冒険に出て、「クリア」で戻ってくると、10 コインがもらえるようにします。今回は何回クリアしても同じ報酬にし、初回ボーナスは付けません。

## クリア報酬は GS2 側に設定する

報酬の枚数は、GS2 のクエスト設定で決めます。通常クリア報酬を「無償コイン 10 枚」にしておけば、Unity からクリアを報告したときに、GS2 がコインを加えてくれます。Unity 側では、残高の変更に合わせて画面の表示を更新します。

コインは GS2-Money2 の「ウォレット」に保存します。ウォレットを区別する番号を「スロット」と呼び、このゲームでは `0` を使います。この章で Money2 の設定を追加し、以降の章でも同じウォレットを読み書きします。

冒険の完了とコインの入金は、まとめて処理したい操作です。このような一連の処理をトランザクションと呼びます。クエストの通常クリア報酬に入金を設定すれば、Unity はクリアを報告するだけで済みます。

コインを増やすといった処理を、設定の中では「獲得アクション」と呼びます。次の手順では、通常クリア報酬にコインの獲得アクションを追加します。

## 手順



**Studio**

### 通貨と報酬を設定する

1. **機能を追加** で「通貨」を追加します。

2. **クエスト（Quest）** で `forest` の **通常クリア報酬** のセルをダブルクリックします。右側の **アイテムを追加** を押し、追加された **未設定** を開きます。
3. **アクション** で `DepositByUserId` を検索し、`money2` の **ウォレットの残高に加算** を選びます。**ターゲットリソース** は **なし** のまま、次の値を入力します。

| 項目 | 値 |
| --- | --- |
| namespaceName | `Currency` |
| userId | `#{userId}` |
| slot | `0` |

`#{userId}` は、クリアしたプレイヤーを表す指定です。実際のプレイヤー ID に書き換えず、そのまま入力します。

4. **depositTransactions** の **追加** を押し、作られた **DepositTransaction** を開きます。`price` は `0`、`currency` は **値なし** のまま、`count` に `10` を入力します。これで無償コインを10枚入金する設定になります。

<a href="/images/adventure/later-chapters/03-coin-reward.jpg" target="_blank" rel="noopener" aria-label="通常クリア報酬の入金設定。Currency のスロット0へ、price 0・count 10 で無償コインを加える（別タブで拡大）"><img src="/images/adventure/later-chapters/03-coin-reward.jpg" alt="通常クリア報酬の入金設定。Currency のスロット0へ、price 0・count 10 で無償コインを加える" loading="lazy"></a>

初回クリア報酬と失敗時アクションは、0件のままにします。**反映**からサーバーへのデプロイと Unity プロジェクトの更新を行います。

### 開始・クリア・コイン表示をつなぐ

Unity の `Gs2` オブジェクトに、`ProgressHandler` と `WalletHandler` を追加します。Wallet Handler の **Slot** は `0` です。`AdventureStudioView.cs` をこの章のコードに置き換え、**Progress** と **Wallet** にそれぞれ割り当てます。**Quests** は設定済みの `QuestListHandler` を使います。

冒険の操作は、次の `DrawQuestButtons()` にまとめています。開始ボタンから `quest.Start()`、クリアボタンから `progress.Binder.Complete()` を呼びます。`Run()` は処理中の連打防止とエラー表示を担当します。

残高や読み込み状態の表示は別のメソッドに分けています。全体のコードは、下のリンクからダウンロードして置き換えてください。

```csharp
private void DrawQuestButtons()
{
    var canOperate = !busy && !unresolved && progress.HasValue;

    GUI.enabled = canOperate && !progress.Model.InProgress;
    foreach (var quest in quests.Binders)
    {
        GUILayout.Label(quest.DisplayName);
        if (GUILayout.Button("開始: " + quest.DisplayName))
        {
            Run(() => quest.Start());
        }
    }

    GUI.enabled = canOperate && progress.Model.InProgress;
    if (GUILayout.Button("クリア"))
    {
        Run(() => progress.Binder.Complete());
    }
    GUI.enabled = true;
}
```

<a href="/samples/adventure/03/AdventureStudioView.cs" download>この章の AdventureStudioView.cs をダウンロード</a>

自分のゲームでは、敵を倒したときやゴールに着いたときに、このクリア処理を呼びます。上のコードで「クリア」ボタンに割り当てている `Run(() => progress.Binder.Complete())` がその処理です。報酬は GS2 が加えるので、コインを加算するコードを別に書く必要はありません。




**SDK（Unity）**

### 設定を更新する

<a href="/samples/adventure/03/adventure-game.yaml" download>第3章のテンプレートをダウンロード</a>し、**Deploy → adventure-game → スタックを更新**を開き、**テンプレートデータ**でダウンロードしたファイルを選びます。これは前の章のリソースも含む完全なテンプレートです。更新内容を確認して適用し、実行状態が**更新完了**になるまで待ちます。

通常クリア報酬の `depositTransactions` に、無償コインを10枚入金する設定を書きます。

```yaml
action: Gs2Money2:DepositByUserId
request: |-
  {
    "namespaceName": "adventure-coin",
    "userId": "#{userId}",
    "slot": 0,
    "depositTransactions": [
      { "price": 0, "currency": "", "count": 10 }
    ]
  }
```

`request` は JSON を文字列として渡します。YAML の `|-` の下に JSON を書く形です。

`StartAsync()` で冒険を開始し、`EndAsync()` でクリアを報告します。それぞれが返す `transaction` の `WaitAsync(all: true)` でサーバー処理の完了を待ち、進行状況や残高の表示は、キャッシュの変更通知で更新します。

### Unity のコードを更新する

次の二つのファイルを `Assets` に入れます。同名ファイルがある場合は置き換えてください。

- <a href="/samples/adventure/03/AdventurePlay.cs" download>AdventurePlay.cs</a>：冒険の操作と画面表示
- <a href="/samples/adventure/03/AdventurePlayer.cs" download>AdventurePlayer.cs</a>：表示用データの購読と解除

既存の `AdventurePlay` と Inspector の **Login** 設定はそのまま使います。`AdventurePlayer` はコードから作成します。

### データの変更に合わせて表示を更新する

`AdventurePlayer` は `SubscribeWithInitialCallAsync()` で初回の値と、その後の変更を受け取ります。たとえばコイン残高は、ウォレットが更新されるたびに表示用のモデルを差し替えます。

```csharp
var balance = Gs2.Money2.Namespace("adventure-coin").Me(Session).Wallet(0);
var subscription = await balance.SubscribeWithInitialCallAsync(value => Wallet = value);
```

描画では `player.Wallet.Summary.Free` を参照します。進行状況も同じ方法で更新するので、開始やクリアの後に読み込み直す処理は要りません。

戻り値は購読を解除するときに使います。配布コードでは、再生を終了してオブジェクトが破棄されるときに `Unsubscribe()` を呼びます。

```csharp
balance.Unsubscribe(subscription);
```

### 冒険を開始・クリアする

ボタンからは `RunWrite()` を通して開始・クリアの処理を呼びます。`RunWrite()` は連打防止とエラー表示を担当します。`awaitingResult` は要求の応答を受け取ったことを示し、その後のエラーを「要求自体が拒否された場合」と区別します。

この章で使う処理は次の部分です。抜粋を別のクラスとして追加する必要はありません。

```csharp
private async UniTask BeginQuest(string questName)
{
    var transaction = await player.Gs2.Quest.Namespace("adventure-quest").Me(player.Session)
        .StartAsync("main", questName, force: false, speculativeExecute: false);
    awaitingResult = true;
    if (transaction != null) await transaction.WaitAsync(all: true);
}

private async UniTask CompleteQuest()
{
    var transaction = await player.Gs2.Quest.Namespace("adventure-quest").Me(player.Session)
        .Progress().EndAsync(isComplete: true, speculativeExecute: false);
    awaitingResult = true;
    if (transaction != null) await transaction.WaitAsync(all: true);
}
```

自分のゲームでは、敵を倒したときやゴールに着いたときに、この `CompleteQuest()` を呼びます。配布コードの「クリア」ボタンでは、`RunWrite(CompleteQuest).Forget()` として呼んでいます。勝利判定から呼ぶ場合も `RunWrite()` を通し、連打防止とエラー表示を使います。報酬は GS2 が加えるので、コインを加算するコードを別に書く必要はありません。





処理中は「開始」「クリア」ボタンを押せないようにします。返事が来る前に同じ処理を重ねて呼び出さず、成功したときに画面を切り替えます。エラーが返ったときは理由を表示してください。

## 冒険に出かけてみましょう

開始前のコイン残高を控えてから、冒険を始めてみてください。報酬の設定が反映されるまで最大5分かかります。変更前の枚数だった場合は、少し待ってから新しく冒険を始めて確かめます。

| 操作                               | 期待する結果                               |
| ---------------------------------- | ------------------------------------------ |
| 冒険を開始                         | 進行中の冒険が表示される                   |
| 冒険をクリア                       | 冒険が終了し、コインが開始前より 10 増える |
| 同じ冒険を新しく開始してクリア     | さらに 10 増える                           |
| 冒険を開始せずにクリアしようとする | コインは増えない                           |

通信が途切れて結果を受け取れなかった場合は、成功とも失敗とも決めつけず「結果を確認できません」と表示します。すぐにクリア要求を送り直さないでください。

## 練習：設定を変えて試す

クリア報酬を 15 コインに変えて、デプロイしてみましょう。そのあとで新しく冒険を始め、クリアすると何枚増えるかを確かめてください。

<details>
進行中の冒険には、開始時点の情報が保存されています。変更後の報酬を確かめるときは、冒険を新しく開始してください。


## 次の章へ進む前に

練習で変えた通常クリア報酬を **10 コインに戻してデプロイ** します。同じアカウントで森の探索を新しく始めてクリアし、残高が 10 増えることを確認してください。

[次へ：再起動して続きを遊ぶ]()



