こんにちは。この度、株式会社グリフォンでクライアントの仕事をさせていただきました、鈴木です。
インターン期間は約1か月で、主な業務内容はUnityでWebGLのベンチマークアプリを作るというものでした。
当時、ゲーム会社で働くというお話でしたので、ゲームのバグ取りのようなものを任されると考えていました。そのため、この話を聞いた時は驚きました。しかし、いざやってみると楽しくなってしまい、気づけば夢中で取り組んでいました。
最初はMayaでしいたけを作った
最初の業務は、しいたけを作ることでした。ベンチマークとはあまり関係がありませんが、3Dの知識として基本的なことを知るという意図があるようです。実際、良いUVマップとは何か、パフォーマンスのために何をするべきか、など重要な知識を具体的に得ることができました。MayaやPhotoshopを触ることは初めてでしたが、なんとかしいたけっぽいものはできたと思います(笑)。
モデリングにおけるパフォーマンス
ポリゴン数を減らすとパフォーマンスの改善につながります。しかし、ポリゴン数を減らすということは、表現の幅が狭まるということにもなります。そこを如何に工夫してポリゴン数を増やさずに完成度の高い作品に仕上げるか。そこにモデリングの楽しさがあるように感じました。
ポリゴン数の制限で起きる問題を考える上で重要になるのは、ポリゴン数をどこにどう分配するか、ということです。これは3Dモデルの観測者の立場から見る必要があります。観測者の側から見て一番よく見る部分はどこか、どのような環境にそれが存在するのか、どこの部分を誇張したいのかを総合的に考えてポリゴン数の分配をおおまかに決定します。
例えば、今回作成したしいたけを例にとると、観測者が一番注目するのは”かさ”の部分になります。リアルで有機的な”かさ”を表現するためにポリゴン数を多めに分配するように調整し、できるだけ幾何学的な表現は避けるようにしました。
UVマップにおけるパフォーマンス
UVマップについてパフォーマンスを改善する方法はまず、そのマップ面積を最大限利用することです。ポリゴン数と同様にその限られたマップ面積の中でどれだけ解像度の高い作品に見せるかということが重要になってきます。そのために必要なテクニックを学びました。五つほど紹介します。
マップ面積をどのように分配するか
一つ目は、モデリングのポリゴン数の分配と同様にマップ面積をどのように分配するかということです。一番目立つ、もしくは目立たせたい部分の面積を大きくとっていきます。大きい面積であればあるほど解像度を高く分配することになります。今回は”かさ”部分に大胆に配分しました。
UVマップのループ
二つ目は、UVマップのループです。UV画像をループさせて使用することで、解像度コストを大幅に改善することができます。ループのさせ方はごく単純な方法でできます。UVマップ作成時にループさせたい部分を重ねるだけです。今回はしいたけの”かさ”と”石づきの裏”部分以外にはすべてこの技術を使用しています。
ゆがみの修正
三つ目は、UV展開時のフェースの変形で生じるゆがみの修正についてです。UV展開後は好きにUVを配置することができます。そのうえで気を付けなければならないのがゆがみです。UVが伸縮、圧縮されるとゆがみになります。Mayaにはこのゆがみを特定するためにゆがみを表示するツール「歪曲シェーダ」があります。
使用すると、歪みが色によって表現されます。赤いフェースは伸張、青いフェースは圧縮を表し、白いフェースは最適な状態を表します。これにより、歪みの修正が行えます。
透過色
四つ目は、UV展開後の話になります。UV展開後はUV展開図を出力できます。これにPhotoshop等で画像を張り付けたものがマテリアルに適用するUV画像になります。ここで、画像作成時には透過であるべきかどうかを決定する必要があります。これはα値を使用するかどうかでファイルサイズが異ってくるためです。α値を使用しない上で気を付けなければならないことは、画像背景に透過色を設定しないようにすることです。パフォーマンスのために、張り付けた画像と似た色かつ単一色で塗りつぶすべきです。今回は茶色に塗りつぶしました。
陰
五つ目も、UV展開後の話になります。これはモデルを使用する環境によるのですが、画像上で陰を付けることができます。これにより場合によっては細かい陰の演算をする必要がなくなります。Photoshopにはグラデーション機能とマスク機能があり、これによって簡単に陰のグラデーションを行うことができます。今回は光が上から当たることを考えて、”かさ”の部分は中央から外側にかけて暗く、”かさの裏”部分は暗めに、軸もグラデーションをかけて表現しました。
作成したしいたけとUV画像
以上により作成したしいたけとUV画像は次のようになりました。ちょっとループがきつすぎました(笑)
いよいよベンチマーク作成に取り掛かる
Sub Module
Sub Moduleとは自分のgitリポジトリのディレクトリとして外部のgitリポジトリのコミットを参照するものです。チームの共有リポジトリとしてリソースやフレームワークを置いておけば、いつでもそこを参照して使用できます。Sub Moduleの登録はSource Tree上で簡単にできます。
ReSharperがすごい
ReSharperとはJetBrainsが提供しているVisualStudioに入れることができるアドオンです。学生はacドメインで無料で利用できます。
https://www.jetbrains.com/resharper/
ReSharperを使えば命名規則に則っていないものをハイライトして警告してくれます。さらにハイライトされた部分を右クリックで命名規則に従うように簡単にリネームすることができます。foreach文やコンストラクタなどで最適なコードに変換してくれたり、メソッドを抽出して冗長なコードを分けたり、「Ctrl」+「Alt」+「F」でコードクリーンアップもできます。日々ReSharper先生から最適なコードを学ばせてもらっています。
ベンチマークの設計「シーン管理」
ベンチマークは「機能追加」を基本理念に設計しました。最初に考えたことは、機能追加の管理方法です。開発当初は、機能の追加はStrategyパターンを使ってワンシーンでベンチマークをするつもりでした。しかし、Unity上で行うということで、リソースの開放の簡便さなどの理由もあり、機能はシーンで管理することに変更しました。こうすることで、機能単体テストもシーンごとに行うことができ、リソースの開放も簡便になりました。
機能追加を考えると、シーン管理の方法は少し特殊になります。まず、Unityでのシーン遷移は名前指定もしくはシーンindexでの指定になります。(今回は名前指定で行いました。)次に、追加機能(シーン)ごとに「シーンデータ」を作り、その中に「カテゴリ」というパラメータを用意しました。「カテゴリ」はアプリ実行中のTitleシーンで選択できる項目であり、その「カテゴリ」を選択すると、同じ「カテゴリ」に分類された機能(シーン)は、順次全て実行されます。選択した「カテゴリ」に各当する機能(シーン)の「シーンデータ」は、Titleシーンで共有データ上のQueueに突っ込まれ、それを順に実行すればよいように設計しました。個々のベンチマーク機能が実行終了すると、実行結果は共有データ上に保存されていきます。
以下にベンチマークのクラス図を載せます。
簡易版ベンチマーク図
[ベンチマーククラス図]
マルチシーン編集
開発当初はマルチシーン編集での実行を考えていました。共有データやマネージャークラスを別シーン上に分け、シーン遷移や実行結果を管理するというものです。しかし、Unityのマルチシーン編集にはその性質上仕方のない問題があります。
シーンのアンロードとリロード
一つ目は、シーンの遷移時にはアンロードとロードを指定しなければならないことです。単一でのシーン遷移ならばロード先を指定するだけで、現在のシーンは自動的に破棄され、次のシーンへ移ります。しかし、マルチシーン編集では破棄できる選択肢が複数存在するため、アンロード先を指定する必要があります。これによりコードが少し複雑になってしまうことになりました。
アクティブなシーンと非アクティブなシーン
二つ目は、マルチシーンでは「アクティブなシーン」と「非アクティブなシーン」が存在することです。マルチシーン編集ではゲームオブジェクトをインスタンスしたときに「アクティブなシーン」に生成されてしまいます。これにより、ベンチマーク機能のシーンをロードした後いちいちアクティブなシーンに切り替える必要がありました。
以上を解決するため、ゴッドクラスや共有データのみをDontDestroyOnLoadし、非アクティブな別シーンとして保持。シングルトンアクセスで対処することにしました。他にもマルチシーン編集で起こる問題はあります。それらの問題については以下のリンクを参考にさせていただきました。
http://tsubakit1.hateblo.jp/entry/2016/04/18/011411#Sceneは毎回取得する
ベンチマーク結果の出力
ベンチマーク結果の出力としての問題は一番時間がかかりました。いくつかのAPIを検証する必要があったためです。最終的には結果をダウンロードして保存する形式におちつきました。
Google Drive
開発当初はベンチマーク結果をGoogle Driveに保存する予定でした。しかし、githubの[Google Drtive for Unity3D](https://github.com/midworld/unity-googledrive)はエディタ上では正常に動き、WebGLビルド後にはOAuth認証が通りませんでした。また、Google Driveは2018.3.12にサービス完全終了することもあり、Google Driveへの保存する計画は頓挫しました。
GoogleAnalytics
Google Analytics とはアクセス解析ツールです。使用者のアプリ上の行動情報をアプリからGoogle Analyticsに送ることができます。Google AnalyticsはUnity向けのプラグイン(https://developers.google.com/analytics/devguides/collection/unity/v4/?hl=ja)を提供しており、Unityゲームでも関数呼べばすぐに送信できてめちゃくちゃ楽しいです。
実際にベンチマーク結果を出力するとGoogle Analytics上でも出力されていることが確認できました。しかし、Google Analyticsページ上で個々のクライアントのベンチマーク出力結果を参照するには、ユーザーエクスプローラーでクライアントIDを参照しなくてはなりません。これは非常に見にくく、実用には至りませんでした。Google Analyticsはマクロ的な視点での観察は向いているかもしれませんが、個々の出力結果を参照することには向いていないのかもしれません。
WebGLでブラウザ上のスクリプトと通信する
Unity Web Playerとブラウザーとで通信することができます。WebページからUnity Web Playerの関数を呼び出したり、またその逆もできます。よってプロジェクト内に自作JavaScriptを置いてスクリプトから直でJavaScriptを呼び出せます。“Assets/Plugins/WebGL” フォルダーに .jslib 拡張子に変えて(つまりプラグインとして) JavaScript のファイルを配置することで使用できます。注意すべきことは次の3点です。
- プラグインJavaScriptコードの最後でmergeIntoを使ってマージする必要がある
- Unity側のスクリプトではstatic externと[DllImport(“__Internal”)]アトリビュートを使って使用する関数を宣言する
- ビルド時にのみ利用できるので、利用している部分には#if !UNITY_EDITOR…#endifといった動作しない保証を付ける
しかし、この機構は重大な問題があります。Unityエディタ上ではエラーが確認できないことと、WebGLビルドは結構時間がかかることが悪質な連携を引き起こし、ビルドするまで待ってからjslib内でわけのわからないエラーが出るといった問題です。エラーを起こさなければ問題ないのですが、開発効率は非常に悪いです。
今回はこの方法でブラウザからの結果ファイルのダウンロードを行いました。
導入方法は以下を参考にしました。
https://docs.unity3d.com/jp/530/Manual/webgl-interactingwithbrowserscripting.html
ブラウザからの結果ファイルのダウンロード
こちらはJavaScript上での話になります。
今回はUnity側のスクリプトからjsonで出力しjavasciriptで受け取ってブラウザ上のスクリプトを呼び出してダウンロードを行いました。
ブラウザごとに対応する必要がある
ダウンロード処理はブラウザごとに対応する必要があります。こちらのリンクが参考になりました。
https://trueman-developer.blogspot.jp/2016/05/javascript_8.html
結果出力で日本語が文字化け
結果をjsonで受け取り、csv形式でダウンロードさせると、日本語が文字化けしました。これを解決するには、頭に”\ufffe”のBOMをつけます。こちらのリンクが参考になりました。
http://kuroeveryday.blogspot.jp/2016/04/byte-order-mark.html
インターンで学んだ一番大事なこと
最後に今回のインターンで学んだ一番大事なことをお伝えしたいと思います。
今まで、私のゲーム開発活動は部活動が主であり、非常に短い開発期間でチームゲーム開発を行ってきました。よって、開発スピードはものすごく速いのですが、とても汚いコード、即興で作り上げた設計でゲーム開発を行ってきました。しかし、インターンでは自分の設計を何度も練り直し、圧倒的に過去最高レベルの設計ができたと感じています。さらに、コーディング規約も明確にさだめられており、ReSharperでさらに強固な命名規則が作り上げられていました。
コードは誰にでもわかるように書くべきだ、ということをしばらく忘れていました。設計は再設計を嫌わず何度も練り直すものだ、ということを初めて知りました。インターンに来てそれが再確認できました。本当に良かったと感じました。これからはお金を受け取っても恥ずかしくないきちんとしたコード、設計をしたいと思います。短い間でしたが本当にお世話になりました!!!