Steam Input APIを利用してキーボード/マウスとゲームパッドのボタンアイコンをいい感じに切り替える

:::note info
この記事は **[Unreal Engine (UE) Advent Calendar 2023](https://qiita.com/advent-calendar/2023/ue)** シリーズ 1 12日目の記事です。
:::

# はじめに
この記事ではSteamが提供しているSteamworks APIのISteamInput Interface(以下Steam Input API)を利用して現在接続されているゲームパッドの種類の情報を取得する方法を解説します。

# 注意事項
1. この記事ではMarketplaceで販売されているプラグイン『**[Steam Core](https://www.unrealengine.com/marketplace/ja/product/steamcore)**』を利用しています。このプラグインを利用することでSteamworks APIをブループリントで利用することができるようになります。プラグインで提供されている関数名等はSteamworks APIと同名のため、プラグインを利用せず自前で実装するのも良いかもしれません。
1. PIEではSteamworks APIが動作しないため、必ずスタンドアロンでゲームを実行してチェックをする必要があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588505/72a75ad2-d1db-de23-fe21-d718c3785da5.png)
1. Steamworks APIを利用するにはSteamがインストールされおり、起動している必要があります。

# この記事を書いたときの環境
- Unreal Engine 5.2.1
    - Third Person テンプレート
- Steam Coreプラグイン
- Xbox One ワイヤレス コントローラー
- DualSense ワイヤレスコントローラー (PlayStation 5のコントローラー)

# 前提条件
Steam APIを利用するにはSteam側にゲームを認識させる必要があります。以下の情報を丸コピして`DefaultEngine.ini`にそのまま追加しちゃいましょう。
 ```DefaultEngine.ini
[/Script/Engine.GameEngine]
+NetDriverDefinitions=(DefName="GameNetDriver",DriverClassName="OnlineSubsystemSteam.SteamNetDriver",DriverClassNameFallback="OnlineSubsystemUtils.IpNetDriver")

[OnlineSubsystem]
DefaultPlatformService=Steam

[OnlineSubsystemSteam]
bEnabled=true
SteamDevAppId=480

[/Script/OnlineSubsystemSteam.SteamNetDriver]
NetConnectionClassName="OnlineSubsystemSteam.SteamNetConnection"
```
スタンドアロンでゲームを実行中に画面右下にSteamのオーバーレイが表示されていれば本記事の前提条件のひとつはクリアされます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588505/0423aeb6-5675-41d2-2852-271a1f949828.png)

ちなみにですが、`SteamDevAppId=480`はSteamのテスト用ゲーム『Spacewar』の番号にあたります。スタンドアロンでゲームを実行すると、SteamでSpacewarを遊んでいることにされます。気にしないでください。

:::note warn
SteamCore プラグインの有効化を忘れないように！
:::

# 接続しているゲームパッドの情報の取得
## 初期化イベントの作成
Actor Componentを作成し`BP_SteamInputComponent`と名前を付け、中身を実装していきましょう。
Steam Inputを利用するためには最初に初期化を行う必要があるので、初期化用のカスタムイベントを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588505/2c922aed-1007-a148-88e7-897231aaf5f4.png)

作成したカスタムイベントをプレイヤー側で呼び出してみましょう。
`BP_ThirdPersonCharacter`に`BP_SteamInputComponent`を追加し、先ほど作成したカスタムイベントを呼び出します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588505/7a4c547c-9509-d652-b1f2-20c546895962.png)
## 接続しているゲームパッドの情報を取得
`GetConnectedControllers`を呼び出すことで現在接続されているゲームパッドの情報を取得することができます。先ほど実装したカスタムイベントのすぐ後に以下の画像のような実装を行いましょう。今回はシングルプレイ想定なので試していませんが、`OutHandles`から出力された値をこの記事では1つだけ保存していますが、ローカルマルチプレイのゲームなどでは複数取得することで1PはXboxのボタンアイコン、2PはPSのボタンアイコン、3PはSwitchのボタンアイコン……といったような切り替えが恐らくできると思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588505/af958414-33dc-d76b-66e4-d71b3e062403.png)

## 接続しているゲームパッドの種類を判別
先ほど取得した情報を使用することでゲームパッドの種類を判別することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588505/35130d85-2a31-31ce-20e2-50026de60841.png)

ここで取得した`Input Gamepad Type`の中身を見てみましょう。Steamが対応しているゲームパッドがたくさん表示されていますね。この記事ではこのEnumを利用してゲームパッドのボタンアイコンをいい感じに切り替えていきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588505/36dbbc2e-2fc3-9f4d-720d-bdaf5b5b3c87.png)

# ゲームパッドのボタンアイコンUIをいい感じに切り替える
## 操作しているデバイスがキーボード/マウスなのかゲームパッドなのかを取得する
ゲームパッドのボタンアイコンUIを表示するために適当なWidgetを作成しましょう。表示用のWidgetと実装用のWidgetに分けると後が楽だと思います。ImageとTextくらいで中身は十分です。（`BP_ThirdPersonCharacter`あたりで`AddtoViewport`しておいてください。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588505/d10b5df6-c810-e9a0-4301-dbbadc314024.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588505/4ad628b2-1a08-2869-07b7-7f809700b393.png)

Widgetの実装の前に少しだけ`BP_SteamInputComponent`に処理を追加します。Steam Input APIは接続しているゲームパッドの種類の判別はできますが、現在の入力デバイスがキーボード/マウスなのかゲームパッドなのかを判別することができないからです。

情報を格納するための列挙体を作成して名前を`EInputType`にしましょう。
中身は以下の画像のような感じで作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588505/688698d2-6c8d-c062-4e50-4cf1530feb6b.png)

Any Keyで何かしらの入力があった際にその入力がキーボード/マウスなのかゲームパッドの入力なのかを判別する処理を追加します。先ほど作成した`EInputType`に値を格納しておきましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588505/6df75104-f975-213a-e777-961050137157.png)

試しに`PrintString`で`EInputType`の情報を出力してみました。操作しているデバイスによって表示が切り替わっていることがわかると思います。
![7e46197e05cea3d3e53db167b8ec4db3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588505/caa075bb-0832-16a2-6b4d-7ef918fd726d.gif)

## いい感じにボタンアイコンUIを切り替える
実装用のWidget内に処理を書いていきます。
`BP_SteamInputComponent`の情報を取得したいので、Construct内でプレイヤーの情報を取得します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588505/8753d519-8556-25e6-e51b-436c29be44f3.png)

Tickに以下の処理を追加します。先ほど実装した処理のおかげで現在入力しているデバイス毎に処理が分かれました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588505/5b464bdb-ae6b-ad99-59fd-0b55ed384e0e.png)

この後の処理を実装する前にボタンアイコンを用意しましょう。自作したボタンアイコンを使用するのもいいですし、Marketplaceで購入したボタンアイコンがあるならそれでも大丈夫です。今回はCC0で配布されているボタンアイコンを利用しようと思います。

感謝の正拳突きをしながらダウンロードしてください。
**[Xelu's FREE Controller Prompts](https://thoseawesomeguys.com/prompts/)**

ダウンロードが完了したら必要なアイコンをプロジェクトにインポートしてください。この記事ではKeyboard & MouseとPS5, Xbox Oneをインポートしました。

これらの画像を扱いやすくするために構造体を作成します。

:::note warn
多分本当はデータテーブルとかデータアセットとかその辺で管理した方がいいです。
:::
`SInputButtonIcon`という構造体を用意しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588505/c4fa57a4-1a9a-332f-8a7b-aa6ffd76cbdf.png)

実装用のWidgetに先ほど作成したSInputButtonIcon型の変数を作成します。この変数は公開変数にして表示用のWidgetで値を編集するのでInstance EditableをTrueにしておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588505/5ea4bda3-b650-6b7b-ecfd-4f2ac2aa3e1f.png)

先ほど作った変数の値を決めます。変数は公開されているので表示用のWidgetで中身を決めます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588505/8bdcda98-8093-b0f9-189e-4e648854bfaa.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588505/9bc5990a-1ef9-49a2-aa22-a8287805cff4.png)



以下のような形で処理を実装します。
これでキーボード/マウス操作時のボタンアイコン切り替えは準備完了です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588505/4a1c4e82-803c-9576-6141-d901aa3541be.png)

続いてゲームパッド操作時のボタンアイコン切り替えを実装します。インポートしたボタンアイコンの分だけピンの接続をしていますが、実際にSteamでゲームをリリースする場合は可能であれば全部埋めた方がいいと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588505/3d485039-beb7-ec9e-03af-2e97bce5cde5.png)

お疲れさまでした。ここまでできたら**いい感じに**ボタンアイコンUIが切り替わるようになっているはずです。スタンドアロンでゲームを実行してみましょう。

<iframe width="2499" height="1036" src="https://www.youtube.com/embed/-WB0gM7qmOI" title="Unreal Engine (UE) Advent Calendar 2023" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

# まとめ
めちゃくちゃ久々に記事を書いたので読みにくいかもしれないです。
Steamworkshop周りのAPIは結構色々な機能が用意されていてドキュメントを読んでるだけでも楽しいのでおすすめです。

明日は[おかず](https://twitter.com/pafuhana1213)さんの[【UE5】コリジョン管理・処理に関する講演メモ・補足 : Collision Data in UE5: Practical Tips for Managing Collision Settings & Queries | Unreal Fest 2023](https://pafuhana1213.hatenablog.com/entry/2023/12/13/034613)です。
お誕生日おめでとうございます🥳って先に書いておきます。