スマホ写真の投稿でフィールドが作られてゆく2Dプラットフォームゲーム「Landscape Extenders」の紹介


> https://qiita.com/advent-calendar/2024/siv3d
> 
> この記事は、Siv3D Advent Calendar 2024の25日目の記事です。


本記事では、筆者が[OpenSiv3D](https://siv3d.github.io/ja-jp/)を用いて制作した **多人数参加型ゲーム「Landscape Extenders」** の概要とその実装を紹介します。

:::note info
この作品は、[情報処理学会 エンタテインメントコンピューティング シンポジウム（EC2024）](https://ec2024.entcomp.org)にてデモ発表されたものです。
:::

# 1. 何を作ったの？

「Landscape Extenders」は**ゲーム参加者らが投稿した写真をステージにして、駆け巡れる2Dアクションゲーム**です。

![figure1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274710/0aed3a36-3c06-c920-734a-0523c57556db.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274710/2955f006-6425-f4fb-e604-7cbb66f5ae26.png)

参加者は会場に示したWebサイトを通じて、スマートフォンから**所望の写真を投稿**します。その**写真は新たなゲームステージに変換**され、今まで投稿されたステージに繋がっていきます。その後、参加者はゲーム端末に移り、キャラクタを操作することで**投稿した写真がつながったステージを駆け巡れ**ます。

<iframe width="560" height="315" src="https://www.youtube.com/embed/2X_ZQukCaTs?si=bFLN32mgtIw_6QPf" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

:::note warn
上図のQRコードでWebアプリが提示されていますが、今は動作しません。
:::

# 2. 実装
<img width="500" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274710/8956881c-7189-547e-5834-ef6f105cf2e5.png">

システム「Landscape Extenders」の機能は次の3つの部分に大きく分けられます。

i. **デモ参加者らが投稿する写真を受理して保管する**
ii. **投稿された写真をゲームフィールドに追加する**
iii. **キャラクタがゲームフィールドを駆け巡れるようにする**

このうち、i.の機能はWebアプリケーションとして、ゲーム端末部分ii., iii.の実装はOpenSiv3Dアプリケーションとして実装しています。

以降では、各機能をどのように実装したか、そしてWebアプリケーションとOpenSiv3Dアプリケーションをどのように接続したかについての方針を説明します。


## i)  デモ参加者らが投稿する写真を受理して保管する

この機能は、写真を送信するための**フォーム**と写真データを保管する**サーバ**を配置することで実装しました。**フォーム**では、次のような画面で写真をフィールドのどの位置に置くかをドラッグ＆ドロップして指定できたり、写真をサーバに送信したりできます。

<img width="300" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274710/df9125c6-a130-da35-76d7-e0da19bfc1f3.png">

Webアプリケーションの実装には[Firebase](https://firebase.google.com/?hl=ja)を用いています。サイトの配信にはFirebase Hostingを使用しています。また、写真の配置情報はFirestoreに、写真そのもののデータはFirebase Cloud Storageに保存しました。

## ii) 投稿された写真をゲームフィールドに追加する
投稿された写真をフィールドに反映するには、Firebaseに格納された画像データ・配置情報をOpenSiv3D アプリに送信する必要があります。現状FirebaseはC++におけるPCアプリ向けAPIは用意していませんが、Firestore, Cloud Storageに対するREST APIが用意されています。[^1] たとえば、`https://(ホスト名)/v1/projects/(プロジェクト名)/databases/(default)/documents/...`といったURLに対してGETリクエストを送ることで、Firestoreからデータを読み取れます。

[^1]: [iOSアプリ向けのAPI](https://firebase.google.com/docs/cpp/setup?hl=ja&platform=ios#prerequisites)は実装されていますが、スタンドアロンのPCアプリ向けについてはまだ実装されていないようです。

従って、OpenSiv3DアプリからHTTPリクエストを送ることで、投稿されたデータを受信できます。この実装にあたり、OpenSiv3Dの`SimpleHTTP::Save`関数が使えます。また、FirebaseサーバからのレスポンスはJSON形式で記述されているため、その読み取りにはOpenSiv3Dの`JSON`名前空間中の関数を活用できます。

<details><summary>実装のさらなる詳細について</summary>

1. **REST APIからFirestore等を操作する場合、あらかじめFirebase ID トークンを取得しておく必要があります**
このIDトークンはFirebase Authentication上のアカウントに対して発行できます。
従って、上記の処理に加えて匿名アカウントをAuth APIを通じて作成し、そのアカウントのIDトークンを発行するという処理を挟む必要があります。配置データをFirestoreから読み取る時にも、IDトークンを`Authentication`ヘッダに添付しなくてはなりません。
また、トークンには有効期限（1時間程度）が定められています。Firebaseへのデータ取得が挟まるたびにトークンが有効かを調べ、有効でなかったらトークンのIDを再取得をしています。
c.f. [Firebase Auth REST APIについて]( https://firebase.google.com/docs/reference/rest/auth/?hl=ja)

2. **すでに一度取得した画像データを2回取得しないよう、画像取得に対してはキャッシュ機能を設けています**
そのため、ステージのデータを更新するたび、新たに追加されたステージ（ドキュメント）がないかを確認します。もし新たに追加された画像があればそれをダウンロードすることで対処しています。
</details>

**実装に関する参考情報**
- OpenSiv3Dの`SimpleHTTP`: https://zenn.dev/reputeless/books/siv3d-documentation/viewer/tutorial-http
- OpenSiv3Dの`JSON`: https://zenn.dev/reputeless/books/siv3d-documentation/viewer/tutorial-configfile
- FirestoreのREST API:https://firebase.google.com/docs/firestore/use-rest-api?hl=ja
- Firebase Cloud StorageのREST API: https://firebase.google.com/docs/storage/gcp-integration?hl=ja#rest_api

## iii) キャラクターがゲームフィールドを駆け巡れるようにする

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274710/bd11004c-43d0-1974-4bef-982078819ce9.png)

ここまでで、投稿された写真情報をゲーム端末に送信できました。ですが、写真の地形通りにキャラクタが駆け巡るには、フィールドに当たり判定を設定する必要があります。

地形は物体の境界（画像上では、輝度が大きく変化するライン）に対応することが多いです。従って、OpenCVにある線分検出アルゴリズムを用いることで、そのような地形の位置を推測できます。しかし、ゲームの地形として利用するには更に工夫が必要となります。（この詳細は **3章 Landscape Wander** にて後述します）

そのほか、ゲーム部のメインループ・描画の実装にもOpenSiv3Dの機能を用いています。工夫の一例として、**ゲーム画面中のプレイヤキャラクタの強調**があります。プレイヤのスプライトをそのまま描画すると、写真の背景の色合いに埋もれてしまい、どこにキャラクタがいるのかわかりにくいことがあります。そこで、キャラクタを画面中で少し光らせ、どのような色合いの写真であってもキャラクタが目立つようにしました。（**2Dライトブルーム**）2Dライトブルームは、ガウシアンブラー（`Shader::GaussianBlur`）と加算合成2Dレンダーステート（`ScopedRenderStates2D { BlendState::Additive }`）を組み合わせることで[実装できます](https://siv3d.github.io/ja-jp/tutorial3/render-texture/#4110-2d-ライトブルーム)。

<img width="350" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274710/efd00384-4fcd-1f39-1039-577d5d1144fd.png">

（余談）デモ会場では、Joy-Conを用いてプレイヤキャラクタを操作できたりしました。このJoy-Conの入出力もまた[OpenSiv3Dの機能](https://zenn.dev/reputeless/books/siv3d-documentation/viewer/tutorial-gamepad#32.2-joy-con-%E3%81%AE%E5%85%A5%E5%8A%9B%E3%82%92%E6%89%B1%E3%81%86)を用いています。

# 3. Landscape Wander：写真から地形情報を抽出する枠組みについて

写真の地形に合わせてプレイヤキャラクタが動き回るためには，その写真に映る地形の形に合わせて当たり判定を配置する必要がありました。このタスクを補助するモジュールが、筆者らが開発を進めている「Landscape Wander」です。


写真の地形（物体境界）の検出はOpenCVにある [確率的Hough変換](https://docs.opencv.org/4.10.0/d9/db0/tutorial_hough_lines.html) や [Fast Line Detector](https://docs.opencv.org/4.10.0/df/ded/group__ximgproc__fast__line__detector.html) 等の線分検出器を用いれば実装はできます。しかし、その検出結果をゲームの地形に直接適用するにはいくつかの課題があります。



- i. **大量の画像処理パラメータを入力画像ごとに適切に定める必要がある**
    - パラメータの数をできるだけ減らして、簡潔に設定したい

- ii. **出力される地形に途切れが発生する**
    - プレイヤの地形当たり判定をナイーブに実装すると、プレイヤがその途切れた部分で落ちてしまう

- iii. **足場として抽出したい対象は、開発したいゲームごとに変わり得る**
    - 例えば、写真に写った雲を足場として検出したくないケースが考えられる
    - 検出対象を制御する手段を提供したい

「Landscape Wander」では、これらの問題を解消するための処理を追加した関数を提供しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274710/27b30fa7-1bea-c067-3750-ff8f81cdc133.png)

はじめに、以下の二つの前処理・後処理を加えました。
- **前処理：バイラテラルフィルタによるエッジ保存ぼかし**
    - このぼかしフィルタを用いると、あまり色が変化してない領域だけを選択的にぼかせる
    - 建物の壁面の模様などの細やかなディティールをぼかして、足場として誤検出されることを防げる
    - ぼかす強さは可変なため、コントラストに基づいて抽出対象の制御ができる
- **後処理：線分結合処理**
    - 問題ii.の線分途切れの問題に対し、似た位置と傾きを持つ線分を結合する処理を加えている
    - こうすることで、プレイヤが隙間から落ちることなく安定して足場を走れるようになる

また、三つのパラメータ（**制御パラメータ**）を新たに定義し、それらの値によって画像処理に用いるパラメータを決定するようにしました。
制御パラメータ$\alpha, \beta, \gamma$は$0$以上$1$以下の値を取り、それぞれ次のような意味合いを持ちます。
- $\alpha$：**検出対象のコントラストの閾値**
    - 値を高めると周囲に対して強い輝度を持ったオブジェクトしか抽出されにくくなる
    - e.g. 写真に写った雲の上を足場として抽出するかどうかを制御できたりする
- $\beta$：**検出対象をどれだけ細かく取るか**
    - 値を高めると、より形に合うように細かな線分を組み合わせて物体を表現する
- $\gamma$：**線結合処理において、どれだけ近い線分を結合するか**
    - 値を高めると、より近い線分しか結合されなくなる

たとえば、$\alpha$の値を変えることで、画像のように抽出対象を変えられます。

![スクリーンショット 2024-12-25 0.09.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274710/a340a9f0-ac0b-045c-b5b7-fdeb28bcf2a6.png)

以上の工夫により、三つの制御パラメータでより簡易に適切な画像処理パラメータを探索したり、抽出対象を制御できたりするようになりました。

# おわりに
この記事では、筆者らが制作したデモンストレーション「Landscape Extenders」についてその実装の詳細とOpenSiv3Dの利活用法、その中核を担うライブラリ「Landscape Wander」を紹介しました。

ライブラリ「Landscape Wander」はまだ開発途中にあり、実用段階に持っていくにはいくつか課題が残されています。
1. <INS>**写真に映る物体を用いたインタラクション**</INS>
現地点では古典的な線分検出技術によって地形を検出しています。
写真中の物体の内容に応じたリアクションを開発者が設定できるようになれば、より面白いゲームが作れそうです。例としては、「木の枝の上に乗っかったらバネのようにジャンプできる」「物体を動かしたり」など。
近年はリアルタイムセマンティックセグメンテーションの研究が進んでいる [^2] ため、機械学習的アプローチをLandscape Wanderで活用することも有効かもしれません。あるいは、[GrabCut法](https://siv3d.github.io/ja-jp/samples/image/?h=grabcut#7-grabcut-%E3%81%AB%E3%82%88%E3%82%8B%E8%83%8C%E6%99%AF%E5%88%86%E9%9B%A2%E3%81%A8-inpaint-%E3%81%AB%E3%82%88%E3%82%8B%E4%BF%AE%E5%BE%A9)とボックス検出を組み合わせるという方法でも良いかも。

2. <INS>**計算量の削減**</INS>
Landscape Wanderで導入した「2線分を結合する後処理」は、検出された線分の数$n$に対し$O(n^2)$の時間計算量を持ちます。検出された線分の数が1000個、10000個程度になると、地形検出に時間がかかってしまいます。たとえば、四分木を用いれば組み合わせの数を減らせて、計算量を定数倍の範囲で軽減できそうです。
うまく最適化問題に落とし込めば計算量次数を改善できそうな予感もしています。この分野はLine Segment Mergingと呼ばれるようですが、有効そうなアルゴリズムは今のところ発見できていないです....(情報求む！)

今後は、Landscape Extendersの制作事例を拡充とともに、こういった現実世界の写真を活用したゲーム開発の技術基盤として機能拡充を行っていく予定です。世界中の景色をゲームの舞台にしたアクションゲームを遊べる日を夢見て、開発・研究を頑張りたいところ...！

[^2]: Jiacong Xu: PIDNet: A Real-time Semantic Segmentation Network Inspired by PID Controllers https://arxiv.org/abs/2206.02066v3

# 発表原稿 ＆ 関連リンク
- [Landscape Wander EC71 発表原稿](https://ipsj.ixsq.nii.ac.jp/ej/index.php?active_action=repository_view_main_item_detail&page_id=13&block_id=8&item_id=233635&item_no=1)
- [Landscape Extenders EC2024 発表原稿](https://ipsj.ixsq.nii.ac.jp/ej/?action=pages_view_main&active_action=repository_view_main_item_detail&item_id=238774&item_no=1&page_id=13&block_id=8)
- GitHubリポジトリ：https://github.com/Appbird/LandscapeWander/tree/main
    - （公開地点ではまだリポジトリが整っていませんが、後日整備します）