Azure上でUnityWebGLとPlayCanvasを動かしてみた。

※深いお話は全く以て出てきません。予めご了承ください。

## はじめに
普段はUnityクライアントサイドを担当しています。Webの知識ほぼゼロのペーペーです。
仕事の関係でWebGLを調べる事になったので、鶏の如く忘れてしまう自分の為にも、備忘録も兼ねてまとめておきたいと思います。

## 目的
とりあえず[Azure App Service](https://azure.microsoft.com/ja-jp/services/app-service/#product-overview)上で、ユーザの入力によりカメラが自由に動かせる、移動できる程度のゲームがWebGLで動いているのを確認できればヨシ。

Azureを選択した理由は特に無いのですが、以前先輩社員の方から少し教わったことがあるので、今回はAzureを利用します。
VisualStudioからポチポチするだけでも良いので便利便利！

## ところでWebGLとは？
まずは、さらっと WebGL(Web Graphics Library) についておさらいしてみます。
前提知識としてWebGLには、土台となる **OpenGL** と呼ばれるライブラリがありますので、まずはそちらから。

#### OpenGLとは
> OpenGL（オープンジーエル、Open Graphics Library）は、クロノス・グループ (英: Khronos Group) が策定している、グラフィックスハードウェア向けの2次元/3次元コンピュータグラフィックスライブラリである。SGI社内で自社のCGワークステーション向けにクローズドに策定されたAPI仕様が改良されて公開され、後に大きなシェアを持つに至った。現在は多様な描画デバイスを包括するグラフィックスAPIのオープン標準規格として策定が行なわれている。[^1]

グラフィックスハードウェアとは、GPUだと思って頂いてほぼ問題ないと思います。[^2] 
簡単に表すのであれば、「GPUを使う事で簡単に2D/3Dが描画できるライブラリ」と言った所でしょうか。

よくDirectXと比較されるライブラリなので、ご存知の方も多い事でしょう。
このOpenGLのサブセットとして、 **OpenGL ES** というモノもあります。

#### OpenGL ES とは
> OpenGL ES（OpenGL for Embedded Systems）は、主に携帯電話などの組み込みシステムで使用されている3次元コンピュータグラフィックス用 API である。OpenGL ESは、従来から存在する（主にデスクトップPCやワークステーション向けの）クロスプラットフォームなリアルタイム3DグラフィックスAPIである OpenGL のサブセットである。[^3]

`Embed`とは「埋め込む、はめ込む」等の意味を持つ単語です。
OpenGL の for Embedded Systems　(組み込みシステム用) という名前の通りのモノですね。

そして、OpenGL のサブセットである OpenGL ES から派生したのが **WebGL** です。

#### WebGLとは
> WebGL（ウェブジーエル、Web Graphics Libraryの略称）は、互換性のある任意のウェブブラウザ上で、プラグインを使用せずにインタラクティブな2次元および3次元のコンピュータグラフィックをレンダリングするためのJavaScript APIである。 [^4]

さて、こちらも凄いザックリ要約すると、「ブラウザ上で2D/3Dを描画したい時に便利なライブラリ」という事ですね。ええ。そうでしょう。多分。

**ブラウザ上で** という事なので、ブラウザが対応してくれなければ、どうしようもありません。
そんな WebGL の最新版は2021年6月現在、Ver2.0となっており、主要なブラウザは基本的には対応済みのようです。

WebGL2.0の各ブラウザ対応状況を[caniuse](https://caniuse.com/)で試しに調べた所はこんな感じです。(緑がsupportedとなります。)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1519104/0a48301f-4839-df49-e91f-bd5be4b78e53.png)

Safari on iOSの欄が非常に不安な赤色をしていますが、気持ちの問題です。
嘘です。リンゴでも齧りながら、対応される事を祈りましょう。

OpenGL・WebGLの詳細等はネット上で詳しく書いて下さってる方も沢山いらっしゃいますし、[wiki](https://ja.wikipedia.org/wiki/WebGL)にも結構な情報が載っているので気になる方は是非調べてみて下さい。 

## どうやって実現させるか
WebGLを使うと一言で言っても手段が沢山あります。
軽く調べただけで下記の選択肢が見つかりました。

#### 1. そのままゴリゴリ書く。
シェーダー自体は[GLSL(OpenGL Shading Language)](https://ja.wikipedia.org/wiki/GLSL)と呼ばれる言語を用います。
そこに JavaScript や HTML/CSS を合わせる事により、ブラウザ上で 2D/3D の描画を実現させます。
ワイはゴリゴリゴリラになるんや！という方は[こちらのサイト](https://gfxfundamentals.org/ "gfxfundamentals")のWebGL用ページが参考になると思います。
ゴリラにならない方でも非常に参考になります。

また、目的によってはこの方法がベストな時もあると思います。

#### 2. Three.jsを使って書く 
WebGLをラップしたライブラリと言った所でしょうか。
WebGLをそのまま書くより断然手間が減っています。
Three.jsは日本語の情報が豊富という点も、非常に手を出しやすそうです。
すごい！偉大なる先人達の余沢。有難く使わせて頂くべきでしょう。

#### 3. Unity等のゲームエンジンを用いてWebGL用にビルドする。
実は普段から使っている[Unity](https://unity.com/ja)でも、WebGLのビルドができるんです！
また、同様に[UnrealEngine](https://www.unrealengine.com/ja/)でもWebGLのビルドが一応[^5] 可能っぽい。

おぉ、コレは使い慣れてるUnityが早そうだ・・。

#### 4. WebGL用のゲームエンジンを使う。
今回の目的は 簡単なゲームを動かす事なので、WebGL用のゲームエンジンを使うのが一番良いでしょう。
なんと、3Dに強くGUIがしっかり整備されている [PLAYCANVAS](https://playcanvas.jp/ "PLAYCANVAS日本語公式サイト") というゲームエンジンがあるらしいではありませんか。

使う前から手っ取り早く目的を実現できる予感がビンビンします。

今回はWebGL初心者の私でも出来そうな、
**3. Unity等のゲームエンジンを用いてWebGL用にビルドする。(今回はUnityを使用)**
**4. WebGL用のゲームエンジンを使う。(PlayCanvasを使用)**
の二つのアプローチを試してみようと思います。

## 実践 UnityWebGL
(今回はUnity 2021.1.7f1を使用します。)

まずはUnityHubからプロジェクトを作成しますが、注意点としてはHDRPはWebGLに対応していません。
UnityはBRPに変わる次の標準RPとしてURPを推しています。[^6]
各Render Pipelineの詳細については割愛しますので、よくわからないという方はとりあえずURPを選択しましょう。

![select_project_template.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1519104/e8802216-6efd-bf58-a01c-e40ef6d57fec.png)

暫く待つとUnityEditorが開きます。
このサンプルシーンは既にキーボードとマウスの入力でカメラが動くように作られています。
試しにプレイボタンを押して確認してみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1519104/9faa3bca-13c2-7366-e293-abebbf0abeaf.png)

ウン。もういいわコレデ。
という事でビルドの準備をしましょう。

- 左上のメニューから`File > BuildSettings`を開きます。

- Platformの欄から`WebGL`を選択します。
WebGLのモジュールが入ってない方は`Install with UnityHub`というボタンを押し、UnityHubからWebGLのモジュールを入れましょう。

- `Switch Platform`を押しましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1519104/b07214c5-6d5b-6651-04db-603963a53125.png)

暫くするとSwitch Platformが終わります。
いよいよビルドです。

- **DevelopmentBuild** この項目のチェックが外れていることを確認します。この項目にチェックがついている場合は、圧縮されません。つまりサイズがでかいです。開発を終えたらチェックを外してビルドしましょう。

- **CodeOptimization** ビルド後のサイズ重視か速度重視かの選択だと思います。とりあえずSizeにでもしておきましょう。

- また、左下の `PlayerSettings...` というボタンから、細かい設定が可能です。各設定項目については[こちら](https://docs.unity3d.com/ja/2021.1/Manual/class-PlayerSettingsWebGL.html)を参照してください。(サイトの左上を見てUnityのVerが自分の環境と一致しているか確認を忘れずに！)

- Azureを利用する場合、レスポンスヘッダー見た限りだと多分デフォルトで良い感じにgzipの対応をしてくれてます。gzipエンコードが多重に起きる場合、`PublishingSettings`内の`CompressionFormat`をDisabledにすると良いかもしれません。

- 設定が終わった方はBuildSettingsから`BuildAndRun`ボタンを押しましょう。ビルド完了後にwebGLの確認が出来ます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1519104/674fc83a-29e1-4c3d-ec34-d7c0991e2651.png)

#### 注意ポイント
Unityを使用する場合はWebGLとして公開するにあたっていくつかの注意するべき事があります。

- 当然ですがビルドサイズに応じて、コンテンツ開始までのロード時間が伸びます。

- Unityの[公式マニュアル](https://docs.unity3d.com/ja/2021.1/Manual/webgl-building.html)にビルドサイズ削減のヒントと、WebGL用のコツが載っていますので是非確認してみて下さい。また、WebGLのパフォーマンスについては[こちらのページ](https://docs.unity3d.com/ja/2021.1/Manual/webgl-performance.html)を参照してください。

- 通信が必要な場合はクロスドメイン制約[^7] に気を付ける必要があります。

- 適切なメモリ管理を行う為にはAssetBundleを使う必要があります。AssetBundleを使用しない場合、コンテンツ実行中は全てのリソースがブラウザの使用するメモリ空間に非圧縮の状態で展開されます。

**これにてUnityでの作業は終了です。**
次にPlayCanvasの作業をします。

## 実践 PlayCanvas
次にPlayCanvasで試してみましょう。
まずは[公式サイト](https://playcanvas.jp/)でアカウントを作成し、ログインします。
恐らくアカウントを作ったばかりの人は、`My First Project`というプロジェクトが強制で作成され、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1519104/2870d39e-edf6-28e8-538f-bd94481a86aa.png)
自動的にEditorページに遷移します。
`MainScene`を選択しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1519104/aa4c0edf-82ea-88a5-6deb-2502fc01bde8.png)

この初期プロジェクトは、「キーボードの入力によりボールが板の上を転がる」だけなのですが、PlayCanvasの基礎を知るには丁度良いプロジェクトになっているので、初めての方は色々確認してみましょう。
基本的なGUIに関してはUnityと同じです。

とりあえずキーボード入力(Arrowキー)で動くので今回はこのままPublishしてみます。
Editor画面左上のPlayCanvasマークから、Publishingを選択します。(または、PlayCanvasマークの下にある矢印マークでも大丈夫です。)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1519104/e4ccfb27-0c7d-761d-ae9b-ae1ae33f70f9.png)

今回はPublish To PlayCanvasを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1519104/b3c57816-1818-bd56-8785-cbaae6ae886e.png)

するとゲームの設定をするウィンドウが開くので、
タイトルやVersion、ゲームの説明等諸々を設定したらPublishNowを押します
![publishNewBuildWindow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1519104/4cf1b7f8-3f41-ea5b-6f3c-fc311ae89339.png)

Publishが終わったものは、`Builds`の欄に登録されていると思います。
今回Publishした ビルドを押してみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1519104/0f94cb93-27aa-6094-aa11-2abb5b5e2969.png)

ブラウザ上で先ほど公開したゲームが動いていることが確認できます。

https://playcanv.as/b/Lud6XnwO/

#### 注意ポイント
PlayCanvasを使用するにあたり気を付けるポイント 

- PlayCanvasEngineが使用するメモリ量はUnity等に比べて少ない[^8]ですが、ビルドサイズの削減やメモリは意識しましょう。[公式運営サイト](https://support.playcanvas.jp/hc/ja)でPlayCanvasBlogの記事を翻訳してくれています。様々なTips [^9] が載っているので確認してみると良いかもしれません。

- 日本語の情報はまだそれほど多くないです。実践的なテクニックや詳しい内容は[フォーラム](https://forum.playcanvas.com/)で調べると良いかもしれません。

- 無料ライセンスの場合、合計ProjectSizeに制限があります。現在どれほど使用しているかは、ユーザページから確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1519104/9f8327ce-f15b-6dca-8468-12c617a272c5.png)

**これにてPlayCanvasでの作業は終了です。**
最後にUnityWebGL製ゲームと、
PlayCanvas製ゲームをWeb上に載せてみます。

## Azureの設定
※他のクラウドサービスを使うよ！って方は飛ばしてください。

アカウントをお持ちでない方は[MicrosoftAzure公式サイト](https://azure.microsoft.com/ja-jp/)から
無料アカウントを作りましょう。

次に[VisualStudio](https://visualstudio.microsoft.com/ja/downloads/)を使い、Azure用のプロジェクトを作ります。
Visual Studio Installerを起動し、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1519104/4bb08c0a-6957-236a-b943-49c76cdaf2e4.png)
**ASP.NETとWeb開発**、**Azureの開発** のモジュールを選択し、VisualStudio2019 をインストールします。
インストール済みの方は`変更`ボタンから同様のモジュールをインストールします。

インストールが終わり次第、VisualStudio2019を起動します。
起動後、`新しいプロジェクトの作成`を選択し、**ASP.NET Core Web アプリケーション** を選びます。
見つからない場合は、プロジェクトの種類をクラウドにするとすぐに見つかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1519104/a39a01f0-c3f1-ca20-7267-101740169769.png)

プロジェクト名などを決めたら、テンプレートの選択です。
今回は **ASP.NET Core Web アプリ(Model View Controller)** を選択します。
また、特にこだわりが無ければASP.NET Core 5.0にして`作成`を押します。 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1519104/7bb92734-9466-4ec4-9c3d-cc82461a5640.png)

VisualStudioが開いたら、とりあえずWebサイトがどんなもんか確認してみましょう。
1. `公開`ボタンを押す
2. `開始`ボタンを押す
3. ターゲット`Azure`を選択し、次へ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1519104/0bbdd9aa-ce1f-6ee7-a87b-1fbd63419a54.png)
  
特定のターゲットの選択で　**Azure App Service (Windows)** を選択し次へ。

この画面が出たら右上から先ほど作ったAzure用の無料アカウントでサインインします。

(※Azure Portal上で既にAppServiceインスタンスを作っている方は飛ばしてください。)
サブスクリプションが**無料試用版**である事を確認したら、AppServiceインスタンスの右側にある **+**ボタンを押しましょう
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1519104/e6d8fea6-9d3a-4fa1-a8e9-3dabd34f6ef4.png)

インスタンス名をお好きな名前に変更し、

1. リソースグループの欄で`新規`を押します。するとリソースグループ名を入力するダイアログが出るので、好きなように命名して下さい。
2. ホスティングプランの欄でも`新規`を押します。先ほどと同じ様にホスティングプラン名を命名し、
3. 場所を選択します。(今回は適当にJapanEastかJapanWestにしておけばイイトオモイマス。)
4. サイズは**Free**を選択します。Freeってなんやねんと不安になると思いますので、[こちら](https://azure.microsoft.com/ja-jp/pricing/details/app-service/windows/)で確認できます。少なからず私は不安になりました。

全て選択が終わったら、OKを押して閉じていきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1519104/0f328200-1c17-5983-76a4-71616d761e75.png)

先ほど作ったAppServiceインスタンス が選択出来ていることを確認したら、`完了`ボタンをおします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1519104/ca48026f-b52e-1db2-9a2b-36d5ea467191.png)

これでVisualStudioとAppServiceの連携は完了しました。
`発行`ボタンを押して、デプロイしてみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1519104/63fdab8e-53b6-2210-e8a7-ebb589ca6a74.png)

正常にデプロイが完了した場合このようなサイトが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1519104/44a2064c-133d-46cc-79ea-60d363c71069.png)

最後にUnityWebGLとPlayCanvasを埋め込みます。

## Azure上でUnityWebGLを動かす

実践UnityWebGLの欄で作成したビルド一式(Buildフォルダ、TempleteDataフォルダ、Index.html)をコピーし、VisualStudio上の任意のディレクトリ配下に置きます。
(今回は分かりやすさ重視で`wwwroot`直下に`WebGL`フォルダを作り、その中に置きます。)
また、`WebGL`フォルダ内のindex.hetmlを分かりやすくする為に、unitywebgl.htmlという名前にリネームします。

また、Azureを利用する方でUnityWebGLビルド時に`CompressionFormat`をDisabledにした方は、`WebGL\Build\bin.data`だけは手動でgzip化しておきましょう。(AzureAppServiceでもweb.configとかで自動でできるとは思うのですが...何方か詳しい方情報お待ちしております。)

手動でbin.dataのみをgzip化した場合はunitywebgl.htmlの61行目にあるbuildUrlのリテラルを変更する必要があります。
`"/bin.data"`　→ `"/bin.data.gz"`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1519104/ae4d1f8f-871c-3778-3d42-4f1a02b11572.png)

ソリューションエクスプローラーから`wwwroot`配下のcssフォルダを右クリックし、`追加`から`新しい項目`を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1519104/e289856a-9de0-6d97-64b4-2751cb806b22.png)
別ウィンドウが開くのでスタイルシートを選択し新しく作成したcssファイルを`webgl.css`にリネーム。
適当にiframeが中央に来るように書いておきます。

```css:webgl.css
.Unity-Webgl {
    height: 800px;
    display: flex;
    justify-content: center;
}
#unity-frame {
    height: 80%;
    width: 90%;
    border: 0;
}
```
次に`Views\Home`の下にある`index.cshtml`を編集します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1519104/c81c9077-1980-4521-01ed-6d2a3e697235.png)

```haml:index.html
@{
    ViewData["Title"] = "Home Page";
}

<head>
    <link rel="stylesheet" href="~/css/webgl.css">
</head>
<body>
    <div class="Unity-Webgl">
        <iframe id="unity-frame" src="~/WebGL/unitywebgl.html"></iframe>
    </div>
</body>
```

index.htmlの編集が終わり次第、再度デプロイします。
ソリューションエクスプローラーからプロジェクト名を右クリックし、`発行`を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1519104/e6fa9f0a-1d77-424a-643f-dcfb6e07df55.png)
公開設定のタブが開くと思うので、再度`発行`を押せば、サイトに反映されます。
サイトURLへ飛んでみて確認しましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1519104/af52cbb0-e89a-03f8-872f-6d128b532c3c.png)

ウン。動いてそう。

## Azure上でPlayCanvasを動かす
`Views\Home`内のindex.cshtmlを編集します。
PlayCanvas上でPublishしたゲームのURLをiframeの中に組み込むだけです。

```html:index.cshtml
@{
    ViewData["Title"] = "Home Page";
}

<head>
    <link rel="stylesheet" href="~/css/webgl.css">
</head>
<body>
    <div class="PlayCanvas-Webgl">
        <iframe id="playcanvas-frame" src="{PlayCanvasでpublishしたゲームの}"></iframe>
    </div>
</body>
```

編集が終わったらUnityWebGLの時と同じ手順でデプロイし、サイトで確認してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1519104/f0d7a47c-8faf-2185-37ab-6e54774f5ac5.png)

動いてる。むっちゃ簡単。しゅごい。
モバイルでも確認してみると良いかもしれまセン。

## おわりに
ということで無事、UnityWebGLとPlayCanvasを用いる事でAzure上で簡単なゲームがWebGLで動くのを確認できました。

初めてWebGLを触ってみましたが、何かしらのフレームワークを利用することで、特別新しい事を覚えなくても簡単にゲームをブラウザ上で動かす事が可能だと分かりました。
実務でやるとなれば、当然ゲームを作る過程や、メモリ管理やらビルドサイズ削減やらで非常に時間はかかってしまうと思います。

が、1からWebGLのみで3Dのゲームを作るよりは早いハズ。
ということで、使えるものはガンガン使っていきましょう。

長文を最後まで読んでいただき、ありがとうございました。
間違いのご指摘や、「ここはこうした方が良い」等ありましたら、コメント欄にてご教授・ご教示頂けると大変助かります。


## 余談
- Azure無料アカウントを作成した方は 一応[無料アカウントのFAQ](https://azure.microsoft.com/ja-jp/free/free-account-faq/)を読んでおくと良いかもしれません。

- WebGLの技術は様々な事に使えそうですね。ブラウザ上で3dの地図を表示する[Cesium](https://cesium.com/)等、面白そうなものも沢山あります。

[^1]: [OpenGL Wikipedia](https://ja.wikipedia.org/wiki/OpenGL)より引用
[^2]: 厳密にはGPUはグラフィックスハードウェアそのものを指す言葉ではありません。詳しくは [GPU Wikipedia](https://ja.wikipedia.org/wiki/Graphics_Processing_Unit) と [電子情報通信学会『知識の森』](http://www.ieice-hbkb.org/)の `2群-3編-4章`を参照してください。
[^3]: [OpenGL ES Wikipedia](https://ja.wikipedia.org/wiki/OpenGL_ES)より引用
[^4]: [WebGL Wikipedia](https://ja.wikipedia.org/wiki/WebGL)より引用
[^5]: [公式ドキュメント](https://docs.unrealengine.com/4.26/ja/SharingAndReleasing/HTML5/)を見ての通り、UEはWebGL専用の資料があまり用意されていない事から、対応が進んでいないように思えます。
[^6]: [Unityの次の標準レンダリングパイプライン "Universal Render Pipeline" は何がどう変わるのか] (https://learning.unity3d.jp/4966/) より
[^7]: セキュリティの観点からドメインが二つ以上にまたがっている状態では制約が課されます。解決策はUnityの[公式マニュアル](https://docs.unity3d.com/ja/2021.1/Manual/webgl-networking.html)を参照。
[^8]: [公式サイト](https://playcanvas.jp/features/)曰く、170kb
[^9]: [テクスチャ圧縮話](https://support.playcanvas.jp/hc/ja/articles/1500001523822)や、[歴史話](https://support.playcanvas.jp/hc/ja/articles/360048150674)等もあります。