UnityでSteamworksのInventory Serviceのアイテム付与を実装する

# ■はじめに
SteamworksのInventoryService周りをUnityで実装する記事が少なかったので書いてみる。

この記事にたどり着いた人は、既にある程度Steamworksの設定を触っており、InventoryServiceの設定か、Unity上でユーザーにアイテムを付与する箇所で詰んでいるという想定で、Steamworksの基本的な設定についての説明は省きます。

アイテムの取得に関しては別記事で書いてみたので、そちらを確認してみて下さい。

https://qiita.com/wanwancat2467/items/eabc98960b412eb5c350

# ■環境
Unity2022.3.53f1


# ■今回の記事でやること
:::note info
- SteamworksのInventory Serviceの設定
- Unity x Steamworks.NETでアイテムを付与する
(GenerateItems , TriggerItemDrop)
- アイテムの付与に関する部分のアイテム定義について
:::

# ■この記事でやらないこと
:::note alert
- Steamworksの基本設定
- Unity x Steamworks.NETでアイテムを付与する以外のアクション
- 具体的なアイテム定義
- WorkShopの諸々
- Promoアイテムの取得(やるのは通常のアイテムのみ)
- 付与したアイテムの取得
:::

# ■公式ドキュメント
https://partner.steamgames.com/doc/features/inventory?l=japanese


## ■設定を開始する前に
:::note warn
以下の内容を進めていくにあたって、steamworks上でアプリの設定をいじるユーザー権限が必要です。
:::
もしSteamworksのInventory Serviceあたりを編集できない場合は、所属しているOrganizationの管理者に編集権限を付与してもらって下さい。

## ■Steamworks上でのInventory Serviceの設定

### 1. パブリッシャーWeb APIキーの取得
inventory serviceの設定を進めるために、まずゲーム毎のパブリッシャーWebAPIを取得する必要がある。

:::note warn
パブリッシャーWebAPIは、個人のSteamWebAPIとは違うものです。
:::



#### 1-1.まずユーザーと権限からグループの管理に移動
![webapi_group.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2951067/2737a77e-572d-8a6d-0c6b-342de63d5ab1.png)

#### 1-2.作成中のゲームのグループを選択し、WebAPIキーを作成を押す。
![webapi.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2951067/2975ed02-1462-e2dd-1a2f-b147790f40d4.png)

### 2. InventoryServiceの設定

#### 2-1.インベントリサービスの有効化
- 作成中のゲームのSteamworks設定を編集
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2951067/acd9bf74-70ff-e84b-4568-c62b24fd1090.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2951067/84225afe-ab0a-7d64-d785-e268265a43b9.png)
- インベントリサービスの有効化にチェック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2951067/32eaff10-0e81-71b4-bb80-0159e8f71fa5.png)

#### 2-2.エコノミー設定でアセットサーバーキーを設定する
そのまま上記画像のエコノミー設定の文字リンク or コミュニティー/エコノミーからエコノミー設定に進み、先ほど作成したWebAPIキーをアセットサーバーキーに入力して保存する。

※この画像だとアイテムの公開設定が公開になっているが、アイテムの公開設定を公開にするにはまずゲームを審査に出さないと行けないので、一旦非公開に設定されているという前提で進める。
![economy.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2951067/4f235200-4203-0a29-5493-2ae7cc003115.png)

### 3. アイテム定義の作成

#### 3-1.コミュニティー/インベントリサービスのページでプレイ時間に応じたアイテム付与の時間を設定する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2951067/0dadc385-5bb5-2414-1fa9-526330dfded0.png)

#### インベントリのドキュメント
https://partner.steamgames.com/doc/features/inventory

:::note warn
上記で設定した付与時間設定は、付与頻度をアイテム定義の方で設定していないアイテムに適用される。
:::
:::note warn
付与頻度をアイテム定義の方で設定している場合は、アイテムの設定が優先される。
:::
:::note warn
アイテムは自動的に付与されない

例えばここの設定で、1時間で1個のアイテムを付与する設定にしたとしても、アプリ側でアイテムを付与する関数を呼ばないと、アイテムは付与されない。

上記設定はあくまでアイテムを付与可能になる時間の設定という意味です。
:::




ここではテストで全て5にしていますが、皆さんは作成しているゲームと作り方に合わせて設定してください。


#### 3-2.アイテム定義を書いたjsonをアップロードする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2951067/f2b83d76-5277-ff7a-da0a-12de6ef7e10b.png)

アイテム定義のドキュメント<br>https://partner.steamgames.com/doc/features/inventory/schema

##### 3-2-1.アイテム定義ってだいたいこんなの
```{
    "appid": あなたのゲームのAppID,
    "items": [
        {
            "itemdefid": "重複無しのアイテムID",
            "type": "item",
            "name": "アイテムの名前",
            "description": "アイテムの説明文",
            "icon_url": "画像URL",
            "icon_url_large": "画像URL",
            "name_color":  "steam上で表示されるアイテムの名前の色16進数",
            "background_color":  "steam上で表示されるアイテムの背景の色16進数",
            "display_type": "任意のアイテムのタイプ",
            "hidden": false,
            "quantity": 0,
            "tradable": false,
            "marketable": false
        }
    ]
}
```

#### 3-3.アイテムのアップロードに成功したら、アイテム定義の欄に定義したアイテムが表示される。![itemdef.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2951067/f7dd189e-1edb-5d2b-7195-d45ae23a3a4c.png)

:::note info
既に登録されているitemdefidの定義されたjsonをアップロードすると、既存の定義がアップロードされた内容で上書きされる。
:::

:::note info
アイテムのユニークIDであるitemdefidはアップロード前に整理することをおすすめします。
typeがitemのものは1000~、generatorは10000~、playtimegeneratorは20000~等。
:::

:::note warn
jsonでの定義の書き方と、一度アップロードした後で「未加工の定義を編集」から変更する場合では書き方が違う場合があるので注意。
:::

:::note alert
アイテムの定義を一度アップロードしてしまうと、そのアイテムの定義を削除することはできません。

なので、確実に使うアイテムを先に決めてからアップロードすることをおすすめします。

ただ、削除できないと言っても、アイテムの上書きは常に可能なので、ゴミ化したアイテムを新規アイテムの定義で上書きすれば、使っていないアイテムの定義が残ることは一応防げます。
:::


###  4.アイテムの公開設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2951067/48a15d14-7472-c906-83c6-906cf16a8d3a.png)

:::note warn
エコノミー設定内の「アイテムの公開設定」を公開に設定する場合は、まずゲームを審査に提出する必要があります。
:::


全てのアイテム定義が揃ったら、リリース前、もしくはリリースと同時にアイテムを公開に設定しましょう。

:::note warn
正しい情報では無いかもしれないですが、僕の場合はアプリ審査中に上記を公開に設定したら審査に落ちたので、審査に通った後に公開に設定するのがいいのかもしれません。
:::


:::note alert
注意

ゲームをリリースし、ユーザーにアイテムが付与された後に、そのアイテムの定義を変更しても、既に付与されたものには新しい定義が反映されません。

リリース前に十分に確認しておきましょう。
:::



- - -

## ■休憩
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2951067/ce09f5c0-efe5-ac96-0681-7b95d6e20973.png)

:::note info 
これでSteamworksの方でのinventory serviceに関する設定は一旦終了です。
ここで一度猫を吸うかコーヒーを飲んで休憩しましょう。
:::


- - -

## ■UnityでSteamworks.NETを使用してアイテムに関するコードを書く
:::note warn
※ここまで来た段階でアイテムが公開されていなくても大丈夫です。
ただ、テスト用に最低1つはアイテムの定義をアップロードしておきましょう。
:::

##### インベントリ系の関数のドキュメント
https://partner.steamgames.com/doc/api/ISteamInventory#GenerateItems

### 1.SteamAPIの初期化
Steamの機能を使う前に初期化を呼びましょう。
SteamManager.Initialized、SteamAPI.Init()

### 2-1.アイテムを付与するコード(テスト用アイテムの付与) => GenerateItems

:::note warn
あくまで開発テスト用のアイテム付与関数。
アイテム定義のtypeがitemのものを直接付与したりできる。
:::

(この関数はテスト用なので、先ほど設定した付与時間は関係なく付与される)


```
private void GenerateTestItems(SteamItemDef_t[] itemDefinitionIDs,uint[] quantities)
{
    //アイテム生成
    bool success = SteamInventory.GenerateItems(out var handle,itemDefinitionIDs,quantities,(uint)itemDefinitionIDs.Length);
    
    if (success)
    {
        Debug.Log($"アイテムの生成要求が成功しました => {handle.m_SteamInventoryResult}");
    }
    else
    {
        Debug.LogError("テストアイテムの付与要求が失敗しました");
    }
}
```
この関数を実行すると、結果はSteamInventoryResultReady_tのコールバックで帰ってきます。
なので、この関数で付与したアイテムの結果を取得したい場合は、この関数を実行する前にコールバックを作成しておく必要があります。
```
Callback<SteamInventoryResultReady_t>.Create(OnSteamInventoryResultReady);
```

```
void OnSteamInventoryResultReady(SteamInventoryResultReady_t pCallback)
{			
    if (pCallback.m_result != EResult.k_EResultOK)
    {
        Debug.Log($"アイテムの取得結果の準備ができたコールバックの中で、結果がOK以外でした => {pCallback.m_result}");
        return;
    }
}
```

ここまで実装した状態で実行すると、アイテムが付与されると思います。
付与されたアイテムはGetAllItemsで取得するか、steamクライアントから確認できます。

SteamInventoryResultReady_tのコールバックは複数の原因で実行されるので、何がトリガーで実行されたかを知るためにはpCallback.m_handleがトリガーする時に発行されたm_handleと一致するかどうかで判別できます。
ただし必須ではないので今回は飛ばします。

### 2-2.実際にユーザーにアイテムを付与する関数 => TriggerItemDrop

```
public void TriggerItemDrop(SteamItemDef_t _dropItemID)
{
    var success = SteamInventory.TriggerItemDrop(out var handle, _dropItemID);
    if (success)
    {
        Debug.Log($"TriggerItemDropの要求に成功した => {handle.m_SteamInventoryResult}");
    }
    else
    {
        Debug.LogError("TriggerItemDropの要求に失敗した");
    }
}
```
この結果も先程のOnSteamInventoryResultReady関数で帰って来る。

:::note alert
TriggerItemDropで付与できるアイテム

アイテム定義のtypeがplaytimegeneratorのアイテムのみです。
typeがitemのものや、generatorに設定されているアイテムは、テスト用の関数GenerateItemsでは直接付与できますが、TriggerItemDropでは付与できません。
:::

## ■ 詰みメモ
僕はTriggerItemDropで大分詰んだので、試した内容のメモ。
もしかしたら他に条件があったり、間違っている条件があるかも。

:::note info
実際のアイテム付与を成功させる条件

- アイテム定義がplaytimegeneratorになっている必要がある。
- playtimegeneratorアイテム定義でbundle項目が定義されている。
bundleが設定されていないと、playtimegeneratorのアイテムの付与は成功するが、generateされるアイテムが無いので、結果何も付与されない。
- この記事の上記Inventory Serviceの設定の3-1で設定した、アイテム全般の付与間隔の設定の付与頻度を超えて取得しようとしていないか確認する
- アイテム定義に付与間隔が設定されている場合は、きちんと付与できるタイミングであることを確認する
- 付与できるアイテムの個数以上の個数を付与しようとしていないか確認する
:::

## ■アイテムの付与に失敗する場合にありそうな原因
:::note alert
アイテムの付与に失敗する場合にありそうな原因
- steam_appid.txtのappIdが正しくない
- インベントリ系関数を実行する前にSteamAPIの初期化が完了していない
- 付与しようとしているアイテム定義IDと実際に登録されているアイテム定義IDが違う。
- 付与しようとしている(現在ログインしているsteamクライアントの)steamアカウントが開発グループに所属していないアカウントになっている
:::

## ■アイテムの付与に関するアイテム定義

#### 特定のitemを付与したい
特定のアイテムを付与したい場合は、playtimegeneratorのアイテムの定義のbundleの定義を付与したいアイテムのitemdefidのみにしましょう。
```
例 "bundle": "5"
```
#### ランダムなitemを付与したい
- ランダムなアイテムを付与したい場合は、上記のbundleの部分に複数のアイテムのitemdefidとそのアイテムが付与される確率をセットで設定したらOKです。
```
例 "bundle": "5x90;6x10;"
```
- もしくは、bundleにtypeがgeneratorのアイテムに設定すれば、上記と同じ様にgeneratorで設定したランダムなアイテムが付与されます。
```
例 "bundle": "100"
```

```
{
    "itemdefid": "100"
    "type": "generator"
    "bundle": "5x70;6x25;7x5"
}
```


- generatorを入れ子に設定できるかは試していないのでわかりません。。できるという説明をどこかで見た気がします。。これがもし可能なのであれば、レア度別にgeneratorアイテムを定義しておいて、playtimegeneratorのbundleでレア度別に確率を設定しておけば、80%でレア度がコモンのgeneratorが付与され、そのgeneratorがランダムなコモンアイテムを付与する、という形でアイテムの整理ができそうです。


## ■最後に
アイテム付与できましたでしょうか。
もし上記で実装できない場合は、コメントを頂ければ可能な限り共闘します。

アデュー
![cat-cute.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2951067/c3f6dd88-dfa0-1ec0-5f65-f80f6b027b28.gif)