[Unity]GitHub Pagesで手軽にスマホゲームを公開する

**[グレンジ Advent Calendar 2022](https://qiita.com/advent-calendar/2022/grenge)** 2日目担当の、flankidsです。
普段はインプット管理、カメラワーク作り、アニメーション制御などなどで遊び心地を作ることを主にやってるエンジニアをしています。

最近はゲームのモックアップを作ることが多いのですが、モバイル向けのアプリのビルドや公開って手順が多くて大変ですよね。

ささっと作ったオモチャみたいなゲームとか、インタラクティブなコンテンツのデモとかを公開したくなった時に小回りの効く手段を持っておくと便利だな〜と思い、今回はその選択肢のひとつとして`Github Pages`での公開方法を調べたのでまとめてみました。

**もしお役に立てたら、LGTM(いいね)やストックをしてもらえるとスゴく嬉しいです！**

# こんなかんじで遊べます
https://twitter.com/flankids/status/1598107644840923136

WebGLビルドを公開して、PCでもブラウザでも気軽にアクセスしてすぐ触れるものを公開できます。

`GitHub Pages`は、GitHubがユーザーに提供している静的ウェブページのためのウェブホスティングサービスです。
平たく言うと、**htmlなどをGitリポジトリにプッシュしてWebページを公開することができる仕組み**です。

### 補足: unityroomにアップしたらよいのでは？

今回Github Pagesで公開する方法をまとめていますが、WebGLでUnityゲームを公開する手段としては、unityroomがメジャーかなと思います。

unityroomは

* 純粋なビルドファイルのみをアップロードするだけでゲームを公開できる
* 共同制作者の登録で公開範囲が絞ることできる
* Unityゲーム共有プラットフォームとして目に付きやすい

という強みがあります。とてもすごい。
一方、その手軽さ・シンプルさの代償として

* ゲームを表示するページのhtmlの体裁をイジれない
* 広告の表示がある
* バージョン管理ができない

という特徴もあります。
Github Pagesなら少し知らないといけないことが増える分 自由度が上がり、後半３つのデメリットもないので試してみようと思った次第でした。

### サンプルプロジェクト

冒頭に載せたゲームとWebGLテンプレートが入ったプロジェクトのリポジトリを公開しています。
 
https://github.com/fluncle/AirHopper

# 概要

1. WebGLでプロジェクトをビルドする
1. GithubのリポジトリでPagesの設定をする
1. WebGLビルドをプッシュする
1. Github Pagesが生成するURLにアクセス
1. 遊ぶ

# 1. WebGLでプロジェクトをビルドする

まずは普通にWebGLビルドをしてください。

ただし今回はモバイルでも遊べることを目標にしているので、ゲーム画面がブラウザ上で**アスペクト比率を守りながらブラウザの縦サイズに自動でフィットする**WebGLテンプレート **「DynamicResize」** を作成しました。
冒頭に記述したサンプルプロジェクトから[パッケージファイルをダウンロード](https://github.com/fluncle/AirHopper/raw/main/DynamicResize.unitypackage)できます。
ぜひ使ってみてください。
DynamicResizeテンプレートの中身は後半に軽く解説します。

WebGLビルド方法は下記にざっくりまとめました。WebGLビルドをやったことがない方はご参考ください。
<details><summary><b>----------------------------------------<br>&nbsp;&nbsp;&nbsp;WebGLビルド手順を表示（折りたたみ）<br>&nbsp;------------------------------------------</b></summary><div>

`Build Settings`でWebGLを選択して、`Switch Platform`でプラットフォーム切り替え（ちょっと長めの切り替え時間が入ります）
![スクリーンショット 2022-12-01 7.47.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184294/7d04de3f-cc1a-7efe-cf23-6d85579ea524.png)

**ビルド前の準備①**
WebGLはスタンドアロンやモバイル実機向けのビルドに比べてパフォーマンス的な制約が多いです。
特に理由がなければ、`Project Settings`の`Quality`で、WebGLのクオリティ設定を**Medium**か**Low**あたりにしておきましょう。
![スクリーンショット 2022-12-01 7.49.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184294/bf1e2f0f-a1b7-c894-4465-92693969ac2a.png)

**ビルド前の準備②**
こちらも特に理由がなければ、`Project Settings`の`Player`で、**Publishing Settings**の**Compression Format**を**Disabled**にしておきましょう。
圧縮フォーマットの設定を「非圧縮」にしています。これだと読み込み前の通信量が増えますが、圧縮すると解凍時のエラー周りで引っかかることが多いので、WebGLビルドをあまりやったことがなければまずは非圧縮にしておくことをオススメします。
![スクリーンショット 2022-12-01 7.51.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184294/cd7e8d06-53aa-245b-9610-4f9f1142e549.png)

**ビルド前の準備③**
`Project Settings`の`Player`で、**Resolution and Presentation**内の**WebGL Template**の設定を選択しましょう。
初期状態だと「Default」と「Minimal」のテンプレートがありますが、今回**「Dynamic Resize」**というテンプレートを用意してありますので、前述の通りそちらをインポートして、**WebGL Template**の設定として選択してください。
![スクリーンショット 2022-12-01 7.50.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184294/c262ecc7-4f72-84e7-0804-5f7717dd54b5.png)

設定が済んだのでビルドします。
`Build Settings`でプラットフォームの選択がWebGLになっていることを確認して（「WebGL」の右にUnityマークが付いていればOK）、「Build」ボタンを押してください。
![スクリーンショット 2022-12-01 7.51.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184294/8868aa8a-6996-443f-989f-287aacda0251.png)

保存先を聞かれます。プロジェクトを管理してるGitリポジトリのどこかに保存してください。
特にこだわリがなければ。Unityプロジェクトを管理してるGitリポジトリのフォルダ直下に`docs`という名前で保存しましょう。
![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3138343239342f38646431393737382d666666632d643636342d626133382d6439313738643930663435372e706e67.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184294/e0f5a278-d629-6dfb-0acb-ce73fafc2e81.png)
</div></details>

# 2. GithubのリポジトリでPagesの設定をする

GitリポジトリでPagesが有効になるよう設定をします。

リポジトリの**Settings**へ移動
![スクリーンショット 2022-12-01 7.54.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184294/1a38edb2-edd6-75e2-77c6-e394c71a76c5.png)

**Pages**タブへ移動
![スクリーンショット 2022-12-01 7.54.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184294/716e214b-3674-9e04-f666-f06bca13f280.png)

WebGLビルドをプッシュする`ブランチ`と`フォルダ`を選択して、「Save」ボタンを押下して設定を保存します。
![スクリーンショット 2022-12-01 8.01.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184294/f9955280-1784-d21e-7a30-1460e94aa085.png)

これでGitリポジトリでPagesが有効になりました。

# 3. WebGLビルドをプッシュする

あとは、設定したブランチ＆フォルダにビルドしたWebGLビルドをプッシュするだけでOKです。
リポジトリにプッシュが入ると、**Actions**ページでWebページの更新ワークフローが実行されている様子が確認できます。
![スクリーンショット 2022-12-01 8.03.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184294/c158c593-a90a-8bf2-fcc9-6ee688e018f7.png)

ワークフローが成功で終了していたらOKです。
![スクリーンショット 2022-12-01 8.04.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184294/ca185871-ffe7-cc0f-2eb1-eb0bf1d6c277.png)

# 4. Github Pagesが生成するURLにアクセス

先ほどの**Settings**の**Pages**の画面の上部の表記が変化しています。
このURLがGithub Pagesによって公開されているページです。
![スクリーンショット 2022-12-01 8.05.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184294/5ec0d5dc-84f5-badb-e0dc-3fd4f3ec95ae.png)

そのままアクセスしたらビルドしたゲームがプレイできます！

# 5. 遊ぶ

あとはアクセスして、動作に問題がないか確認してみてください。
問題なければ、URLをSNSで公開してみんなに触ってもらったりチームに共有して動きを確認してもらったり、自由に公開してみましょう。

動画やサムネイル付きで公開するとアクセスされやすいかも。

https://twitter.com/flankids/status/1598107644840923136


# 所感

そこまで複雑なことなくサクッと設定してアップすることができました。
冒頭で記載したような気軽に何か製作したものを公開するのに向いていそうです。特にスマホゲームではその恩恵が大きいです。

また、クローズドな開発でも非エンジニアのメンバーがUnityプロジェクトを起動せずとも気軽にプレイできるように公開したり、マスタの調整などPCとモバイルの目線を行き来しないといけないような作業をPCだけで完結できるようにしたり、いろいろメリットがありそうです。

あくまでモックアップや小規模なプロダクトでのみ通用するやり方だと思いますが、使い勝手がよいため今後もうまく利用していきたいと感じました。

お読み頂きありがとうございました！
**もしお役に立てたら、LGTMしていただけるととても励みになります！**

# 補足：リポジトリの管理について

WebGLビルドを公開するにはリポジトリが`public`になっている必要があります。
しかしソースコードやアセットは公開したく無いことがほどんどだと思うので、ゲーム開発用のprivateリポジトリと、WebGLビルド公開用のpublicリポジトリを分けて管理するのが良さそうです。

またprivateリポジトリでもアクセス権があるユーザーなら、URLにアクセスした際にログイン画面が表示された後、通常通りプレイできます。

# 補足：DynamicResizeテンプレートについて

デフォルトのWebGLのテンプレートは`Default`と`Minimal`の２種類が用意されています。
今回は`Default`を複製して、ゲーム画面がブラウザ上でアスペクト比率を守りながらブラウザの縦サイズに自動でフィットするWebGLテンプレート **「DynamicResize」** を作成しました。

複製元の`Default`テンプレートはUnityをインストールしてる場所から以下の階層に格納されており、こちらを利用しました。

```
Unity/Hub/Editor/(Unityバージョン)/PlaybackEngines/WebGLSupport/BuildTools/WebGLTemplates/Default
```
※DynamicResizeテンプレートは`Unity2020.3`系のDefaultテンプレートがベースです

Defaultの一部ファイルに下記の差分を入れています。テンプレート自体はそのまま使えますが、内容が気になる方はご参考ください。
<details><summary><b>----------------------------------------<br>&nbsp;&nbsp;&nbsp;Default→DynamicResizeへの差分を表示（折りたたみ）<br>&nbsp;------------------------------------------</b></summary><div>

ブラウザの縦サイズに自動でフィットする処理は`dynamicResize.js`というJavaScriptファイルに記述しています。
そのJavaScriptファイルを使用するための記述をheadに追加しています。
```diff_html:index.html
    <link rel="stylesheet" href="TemplateData/style.css">
+   <script type="text/javascript" src="dynamicResize.js"></script>
  </head>
```

モバイル端末でアクセスした時に表示する警告を削除しています。
```diff_html:index.html
  </div>
- <div id="unity-mobile-warning">
-   WebGL builds are not supported on mobile devices.
- </div>
  <div id="unity-footer">
```

上に同じ。
```diff_html:index.html
  var fullscreenButton = document.querySelector("#unity-fullscreen-button");
- var mobileWarning = document.querySelector("#unity-mobile-warning");

  // By default Unity keeps WebGL canvas render target size matched with
```

DefaultテンプレートではPCとモバイルでアクセスした時の体裁が異なりますが、その差異を削除しています、
```diff_html:index.html
       // config.matchWebGLToCanvasSize = false;
 
-      if (/iPhone|iPad|iPod|Android/i.test(navigator.userAgent)) {
-        container.className = "unity-mobile";
-        // Avoid draining fillrate performance on mobile devices,
-        // and default/override low DPI mode on mobile browsers.
-        config.devicePixelRatio = 1;
-        mobileWarning.style.display = "block";
-        setTimeout(() => {
-          mobileWarning.style.display = "none";
-        }, 5000);
-      } else {
-        canvas.style.width = "{{{ WIDTH }}}px";
-        canvas.style.height = "{{{ HEIGHT }}}px";
-      }
+      config.devicePixelRatio = 1;
 #if BACKGROUND_FILENAME
```

cssファイルの調整です。
こちらもPCとモバイルの差異が出ないように差分が入っています。
```diff_css:style.css
 #unity-container { position: absolute }
-#unity-container.unity-desktop { left: 50%; top: 50%; transform: translate(-50%, -50%) }
-#unity-container.unity-mobile { width: 100%; height: 100% }
+#unity-container { width: 100%; height: 100% }
+#unity-container { justify-content: center; align-items: center; display: flex }
+#unity-container { background: {{{ BACKGROUND_COLOR }}} }
 #unity-canvas { background: {{{ BACKGROUND_COLOR }}} }
-.unity-mobile #unity-canvas { width: 100%; height: 100% }
+#unity-canvas { width: 100%; height: 100% }
 #unity-loading-bar { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); display: none }
```

上に同じ。
```diff_css:style.css
 #unity-footer { position: relative }
-.unity-mobile #unity-footer { display: none }
+#unity-footer { display: none }
 #unity-webgl-logo { float:left; width: 204px; height: 38px; background: url('webgl-logo.png') no-repeat center }
```


ブラウザの縦サイズに自動でフィットする処理のために追加したJavaScriptファイルです。
UnityのProjectSettingsで指定された解像度を参照してアスペクト比を決め、アスペクト固定でブラウザにフィットするように拡大しています。

また、PCなどでブラウザサイズを変えた場合にも対応しています。（`window.addEventListener`のあたり）
```javascript:dynamicResize.js
var canvas;

function initialize() {
    canvas = document.querySelector("#unity-canvas");
}

document.addEventListener('DOMContentLoaded', function(event){
    initialize();
    resize();
});

function resize() {
    var width = {{{ WIDTH }}};
    var height = {{{ HEIGHT }}};
    canvasAspectRatio = width / height;

    var windowWidth = window.innerWidth;
    var windowHeight = window.innerHeight;

    var aspectWindowHeight = windowWidth / canvasAspectRatio;
    if(aspectWindowHeight > windowHeight) {
        windowWidth = windowHeight * canvasAspectRatio;
    }
    else {
        windowHeight = aspectWindowHeight;
    }
    canvas.style.width = windowWidth + 'px'
    canvas.style.height = windowHeight + 'px'
}

let resizeTimer;
window.addEventListener('resize', function() {
    // 動的なリサイズは操作後0.2秒経ってから処理を実行する
    clearTimeout(resizeTimer)
    resizeTimer = setTimeout(function () {
        resize();
    }, 200)
})
```
</div></details>