UnityのWebGLビルドをレンサバに設置する際やっとく設定のメモ
WebGL の Build and Run
Unity の WebGL プラットフォームの Build の際、 Build and Run
をボタンを押せば Unity Editor はローカルサーバーを建てて動作確認することができます。
簡単便利。
サーバーにアップロード
とわいえ「ローカルで動いたから」といって何も考えずにそのままレンタルサーバーにアップロードしても、そのままでは動作してくれない。 (いや、レンタルサーバーによっては動くかもしれんが)
Brotli のエラー
Unable to parse Build/temp.framework.js.br! If using custom web server, verify that web server is sending .br files with HTTP Response Header "Content-Encoding: br". Brotli compression may not be supported over HTTP connections. Migrate your server to use HTTPS.
Gzip のエラー
Unable to parse Build/temp.framework.js.gz! This can happen if build compression was enabled but web server hosting the content was misconfigured to not serve the file with HTTP Response Header "Content-Encoding: gzip" present. Check browser Console and Devtools Network tab to debug.
なるほど、webサーバーの設定がうまくできてないのだな、と .htaccess
で gzip や brotli をなんやかんやしよう・・て試行錯誤したがなんやらうまくいかない。と踠いていたらもっとシンプルに対処できる方法を見つけた・・というより公式に書いてた・・。(わかりづらいけど)
WebGL: Compressed builds and server configuration - Unity マニュアル
サーバーで動作させるために設定すること
1. Decompression Fallback
にチェックを入れて Build
(ここではBrotliの例を記載する。チェックを入れると br
ファイルではなく unityweb
という独自の拡張子で書き出し、 loader.js
でよしなにしてくれるらしい)
2. 下記記述の .htaccess
を Build
ディレクトリ直下に配置
<IfModule mod_mime.c> AddEncoding br .unityweb </IfModule>
3. サーバーにアクセスしたら見れる!
やったー。 これで安易にゲームを公開できるようになったぞ。
UnityでShaderを書くときはなるべく #pragma vertex vert_img を使わないようにする
Unityでもビジュアルスクリプティングが叫ばれる昨今ですが、 レビューしたり、後からの修正のしやすさを考えると、スクリプトでゴリゴリ書きたいわたしです。
Render Texture を操作する Shader を書きたくなった時、
下記のように記述して、vertex shader を UnityCG.cginc
に宣言済みの vert_img
関数を指定すると、
fragment shader の記述に集中できるので楽チンですね。
#include "UnityCG.cginc" #pragma vertex vert_img
思わぬ落とし穴
私は PostProcess をモリモリ自作していて、そこで Render Texture を操作する Shader をよく記述します。 のだけども、iOS 用にビルドしてiPhoneの実機で表示確認してみると、Mac の Unity Editor の表示と全く違う表示がされた・・
なんで・・
原因は先ほど利用した vertex shader の v2f_img
です。
UnityCG.cginc
で定義されている記述はこちら。
struct appdata_img { float4 vertex : POSITION; half2 texcoord : TEXCOORD0; UNITY_VERTEX_INPUT_INSTANCE_ID }; struct v2f_img { float4 pos : SV_POSITION; half2 uv : TEXCOORD0; UNITY_VERTEX_INPUT_INSTANCE_ID UNITY_VERTEX_OUTPUT_STEREO }; v2f_img vert_img( appdata_img v ) { v2f_img o; UNITY_INITIALIZE_OUTPUT(v2f_img, o); UNITY_SETUP_INSTANCE_ID(v); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); o.pos = UnityObjectToClipPos (v.vertex); o.uv = v.texcoord; return o; }
そう、 half
が悪さをしていました。
PC(Mac)とMobile(iOS)で表示が異なる場合は、だいたい精度の問題ですね・・。
struct appdata_img
の half2 texcoord : TEXCOORD0;
、
struct v2f_img
の half2 uv : TEXCOORD0;
となっているため、Render Texture の座標精度が悪くて崩れてしまうのだ。
Unity公式だってテクスチャ座標を扱うときは float使え って言ってる。 (また iPhone12 の画面の解像度は 2,532 x 1,170 。 仮数が10bitしかない half では、2532は表現しきれない。)
対処法
残念だが v2f_img
の利用はやめよう。
便利だったけれど。お世話になったけれど。
無理せず float2
で宣言し、 appdata_base
を受け取るようにしよう。
( appdata_base
の texcoord
は float4
で宣言されている )
struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; UNITY_VERTEX_INPUT_INSTANCE_ID UNITY_VERTEX_OUTPUT_STEREO }; v2f vert(appdata_base v) { v2f o; UNITY_INITIALIZE_OUTPUT(v2f, o); UNITY_SETUP_INSTANCE_ID(v); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); o.pos = UnityObjectToClipPos (v.vertex); o.uv = v.texcoord; return o; }
まとめ
アイコンなど、少ない低解像度の画像を扱うときは v2f_img
を使っても良いが、
Post Process などで高解像度の画像を扱うときは v2f_img
を使ってはけない。
Unity の Reorderable のエディタ拡張を作る
Unity 2020.2 の Inspector では、配列は並び替えが可能な Reorderable
なものに変わりました。
自作クラスのデフォルト表示
下記のように [Serializable]
を指定した自作クラスについても、手軽に Reorderable
なGUIを Inspector で確認できます。便利ですね。
using System; using UnityEngine; [Serializable] public class TestObject { public GameObject Prefab; public Vector3 Position; public Vector3 Rotate; } public class TestComponent : MonoBehaviour { public TestObject[] TestObjects = {new TestObject(),}; }
けれど、ちょっと見づらいですね。イマイチポイントを挙げると以下のとおり。
- 一覧性に欠けている
- Foldoutを閉じた状態 :
Element 1
といった連番のラベル情報しか表示されない- → 配列の中身を識別できる固有の情報を表示したい
- Foldoutを開いた状態 :
- → インデントを用いて、データのまとまりを表現したい
- Foldoutを閉じた状態 :
自作クラスのエディタ拡張
ということで、エディタ拡張を書いて解決をしてみます。
EditorGUILayout
は使えないので、 EditorGUI
で Rect をしっかり指定します。
using System; using UnityEngine; using UnityEditor; [Serializable] public class TestObject { public GameObject Prefab; public Vector3 Position; public Vector3 Rotate; } public class TestComponent : MonoBehaviour { public TestObject[] TestObjects = {new TestObject(),}; } [CustomPropertyDrawer(typeof(TestObject))] public class TestObjectDrawer : PropertyDrawer { private static float GetPropertyHeight(SerializedProperty property = null) { var height = property == null ? EditorGUIUtility.singleLineHeight : EditorGUI.GetPropertyHeight(property, true); return height + EditorGUIUtility.standardVerticalSpacing; } private static bool FoldoutField(ref Rect rect, SerializedProperty property, string label, string propertyName) { var prop = property.FindPropertyRelative(propertyName); prop.isExpanded = EditorGUI.Foldout(rect, prop.isExpanded, GUIContent.none); EditorGUI.PropertyField(rect, prop, new GUIContent(label)); rect.y += GetPropertyHeight(prop); return prop.isExpanded; } private static void Field(ref Rect rect, SerializedProperty property, string label, string propertyName) { var prop = property.FindPropertyRelative(propertyName); EditorGUI.PropertyField(rect, prop, new GUIContent(label), true); rect.y += GetPropertyHeight(prop); } public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { position.height = EditorGUIUtility.singleLineHeight; if (FoldoutField(ref position, property, "Prefab", "Prefab")) { EditorGUI.indentLevel++; Field(ref position, property, "Position", "Position"); Field(ref position, property, "Rotate", "Rotate"); EditorGUI.indentLevel--; } } public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { var enableSkinName = property.FindPropertyRelative("Prefab"); var height = GetPropertyHeight(enableSkinName); if (enableSkinName.isExpanded) { height += GetPropertyHeight(property.FindPropertyRelative("Position")); height += GetPropertyHeight(property.FindPropertyRelative("Rotate")); } return height; } }
これでようやく使い物になりそうな見た目になりましたね。 ちょっとした気遣いで、UnityEditor上の作業はすごく楽になるので、「ゲームの内容には関係ないし・・」とか思わず、ちょいちょいっと書いていきましょい
M1 搭載 Mac mini を購入したので Unity のビルド時間とか測ってみた
総評
ワットパフォーマンス・コストパフォーマンス・静音性(ほぼ無音)がとんでもなく良い。 (比較用2016, 2019 のMacBook Pro はそれぞれ30万クラス。自作PCも20万弱程度で組んだハズ。一方、今回購入したMac mini は11万円)
このため、メイン開発環境はもちろん、常時起動のビルドサーバ用にとりあえず買う、のも大いにアリだと思う。 (Webエンジニアとしては、Docker対応されるまでintelさんにはお世話になるけども)
計測
手元にあるモノで色々とベンチマークとってみた。
端末 | 自作PC | MacBook Pro 2016 | MacBook Pro 2019 | Mac mini 2020 |
---|---|---|---|---|
CPU | Intel Core i5 4670 4core |
Intel Core i7 6700HQ 4core |
Intel Core i9 9880H 8core |
M1 4core + 4core |
GPU | GeForce GTX 1060 | Radeon Pro 460 | Radeon Pro 560X | M1 |
RAM | 32GB | 16GB | 16GB | 16GB |
消費電力 (idle) |
83W | 8W | 17W | 2.5W |
消費電力 (Cinebench Multi) |
117W | 61W | 91W | 22W |
Cinebench R23 ( Single Core ) |
881 | 821 | 1113 | 1519 |
Cinebench R23 ( Multi Core ) |
2796 | 3962 | 7468 | 7815 |
Unity 2020.2.0b12 iOS (IL2CPP) Build Time ( 3D Game Kit ) |
- | 00:09:16 | 00:05:17 | 00:05:16 |
Unity 2020.2.0b12 Win (Mono) Build Time ( 3D Game Kit ) |
- | 00:08:59 | 00:05:12 | 00:04:46 |
Unity 2020.2.0b12 Win (IL2CPP) Build Time ( 3D Game Kit ) |
00:12:14 | - | - | - |
Unity 2020.2.0b12 Linux (IL2CPP) Build Time ( 3D Game Kit ) |
00:29:36 | 00:24:42 | 00:13:37 | 00:13:09 |
Unityのビルド時間は、大方 Cinebench スコア比の通りの差になっている。 Unity 2020.2.0b12 ではまだ Apple Silicon にネイティブ対応したアプリケーションではないため、対応版だともうちょっと短縮されるのかもしれない。
余談
CPU と高速ストレージが上手いこと噛み合っているからか、アプリケーションのインストールや、アセットのインポートなどがガッツリ早くなっている。 Jetbrains Rider や Intellij IDEA の挙動も良い。Karabiner, 1Password, Alfred, Magnet といったユーティリティの動作も問題ないので、ゲーム開発する端末はもう移行しちゃおうと思う
Unity の Package Manager で GitHub の Private Repository を使う
2020/11/12 追記
SSH の鍵が適切に設定されていれば git+ssh://git@github.com/[orgs]/[repo].git?path=[path/to/dir] で Private Repository にアクセスが出来るので、そちらの方がより安全かもしれません 😉
— もんりぃ先生 (@monry) 2020年11月11日
(私の記事にリンクしていただきありがとうございます!)
manifest.json
にtokenを直書きしたものをcommit・運用するのもよくないので、もんりぃ先生のいう通り git+ssh プロトコルで通信しよう!
コピペ用 リポジジトリ直下にasmdefファイルがある場合
git+ssh://git@github.com/[orgs]/[repo].git
asmdefの格納されているディレクトリを指定したい場合
git+ssh://git@github.com/[orgs]/[repo].git?path=[path/to/dir]
結論
Personal access tokens を使った認証をしたらつかえる!
こばなし
Unity2020 にバージョンアップ Unity エディタ上から AssetStore は見れなくなり「Package Manager を使ってね」という圧がかけられるようになりました。なるほど。 購入アセットが100を超えてるので、一覧性がキビしい事を除けば、色々とできることが増えてて良さそうですね。
Package Manager 用の自作ライブラリの作り方
どうやら AssetStore で購入したライブラリだけでなく、自作ライブラリもお作法に則って作成・公開すれば、Package Manager で取り込みできるようになるみたい。大変便利だ。
PackageManagerで自作ライブラリを作成する方法 - Qiita
Package Manager で使えるライブラリのホストについて
GitHub の Public Repository が使えるのは上のQiitaの記事でも確認し、実践済み。 が、Private だとかで認証使いたい場合、他の人は npm サーバーを自分で建てたりしている。
UnityのPackageManagerプライベートリポジトリの調査 - 渋谷ほととぎす通信
Verdaccioでローカルのnpmサーバーを立ててUnityPackageManagerにプライベートリポジトリを使う - 渋谷ほととぎす通信
Unity Package Manager が認証に対応しました! - もんりぃ is undefined.
しかし、個人プロダクトのセキュリティを自分自身で面倒みるのは、正直コスパが悪すぎる。 このため、GitHub Packages を利用するのが適切・・なのだが、Private な Packages 0$運用するにはストレージ容量とデータ転送量が限られる。 パーソナルアクセストークンを使用するアクセスは無料なので、開発メンバーが1人だけで500MB以下の運用なら積極的に使っていこう。 スケールしてもお金で殴れば解決するし。
GitHub Packagesについて - GitHub Docs
GitHubパッケージの支払いについて - GitHub Docs
Package Manager で GitHub の Private Repository を使う設定
「Packages にするほどでもないけど、金かけたくないし、なんか雑に試したいし、認証付きで公開したいんじゃい!」という事もあると思う。
この場合 Personal access tokens
を使った認証をしたら、すんなり利用できた。
自身のUnity プロジェクトの manifest.json
に以下のフォーマットで読み込みたいリポジトリを指定するだけ。
{ "dependencies": { [中略] "{package_name}": "https://{token}:x-oauth-basic@github.com/{owner}/{repo}.git" } }
{package_name}
{token}
{owner}
{repo}
はよしなに置き換えてください。
ちなみにアクセストークンは以下リンクから発行できる。
https://github.com/settings/tokens
開発環境をうまく使いこなして、快適にすごそう
Unity の Command Buffer 用の Shader を雑に試せるスクリプト書いた
会社でお昼ご飯食べながら Amazon.co.jp: ディジタル画像処理 [改訂第二版] eBook: ディジタル画像処理編集委員会: Kindleストア を読む会をすることになった。
内容をパラっとめくってみるとどうやら、理論・数式・画像処理前後のサンプル画像は載っているが、実コードには落とし込まれていないようだ。 (汎用性を持たせるために、この構成は正しいと思う)
「より理解を深めるために実際にコードを書きたい!できればShaderで!」 「処理対象の画像は手軽に作り出したい!」
と思ったので、Unity のカメラへ手軽にShader(Material)を重ね掛けできるコンポーネントを作った。
GitHub - kumak1/CommandBufferTester
利用方法は、Readmeの通りに Unity Package Manager で Package を追加し、Camera オブジェクトにコンポーネントを追加するだけ。 Materials パラメータに自作の Shader(Material)をポイポイ登録すればOK。 (Shader の作成は https://github.com/kumak1/CommandBufferTester/blob/master/shader/Grab.shader を見るとお約束がわかると思う)
捗る準備はできたので、勉強すすめよ〜
Unity の Assembly Definition の依存関係を PlantUML で出力する雑スクリプト書いた
Unity - Manual: Assembly definitions 使ってますか?
上記の記事のような完全に理解したツワモノのおかげで、わたしも色々恩恵をいただいております。 肥大化した自作コードを分割できたり、エディタービルド時間を削減できたりして便利ですね。
コードで上で縛るのはもちろん、このように単体の依存関係を目で確認しやすいのも良い所ですね。
ここまでできると「プロジェクト全体など任意のスコープで俯瞰した図をみたい」「Docusaurusなどのドキュメントでも俯瞰図みたい」と欲が深くなったので、指定ディレクトリ配下を検索して PlantUML を出力できるようなスクリプトを書いてみました。
jq
や yq
を利用してるので注意、と、よしなに改変してご自由に利用ください。
#!/usr/bin/env bash JQ=/usr/local/bin/jq YQ=/usr/local/bin/yq TARGET_FILE=\*.asmdef WORK_DIR=$1 UP_ARROW="-up->" # オプションがなければカレントディレクトリが処理対象。 # 空白のあるディレクトリ名・ファイル名を考慮してないので注意 if [ -z "${WORK_DIR}" ]; then WORK_DIR="." fi ITEMS=$(find "${WORK_DIR}" -name "${TARGET_FILE}") ROOT="\"Assembly-CSharp\"" TEXTS=() echo "@startuml" echo "component ${ROOT}" for ITEM in ${ITEMS}; do NAME=$(cat $ITEM | $JQ '.name') # コンポーネントを宣言 echo "component ${NAME}" # Assembly-CSharp への依存関係を明示 if [ $(cat $ITEM | $JQ -r '.autoReferenced') = "true" ]; then echo "[${NAME}] ${UP_ARROW} [${ROOT}]" fi # その他ライブラリへの依存関係をいったん控える for GUID in $(cat $ITEM | $JQ -r 'select(.references != null) | .references[]' | sed s/GUID://); do TEXTS+=("[${NAME}] ${UP_ARROW} [${GUID}]") done done # bash 3.2 利用想定なので非常に非効率(ハッシュが使えない・・) # 利用しているシェルに合わせて、GUID -> Assembly Definition File Name への置換処理をチューニングしましょう for ((i = 0; i < ${#TEXTS[@]}; i++)) { SHOW=false for ITEM in ${ITEMS}; do NAME=$(cat $ITEM | $JQ '.name') GUID=$($YQ read $ITEM.meta 'guid') if [ "`echo ${TEXTS[$i]} | grep ${GUID}`" ]; then echo ${TEXTS[$i]} | sed s/${GUID}/${NAME}/ SHOW=true break fi done if ! "${SHOW}"; then echo ${TEXTS[$i]} fi } echo "@enduml"
これを実行すると PlantUML が標準出力されるので、クリップボードにコピーしてよしなに利用しましょう。 JetBrains IDE 利用者は PlantUML integration - Plugins | JetBrains を使うと scratch で作成できるし、プレビューも画像保存も手軽で良い。
で、実際に私が出力してみたものがこんな感じ。
どう依存してるかは俯瞰してみれるようになったが、なんだか大変煩雑な図ができあがってしまった。。コードをもっと整理しないとな・・ (図内でGUIDが直接出力されているのは、対象ディレクトリ外に Assembly Definition File があるため。Rewired や Behavior Designer などの asset だ)
なにはともあれ、こういった問題点も可視化できて便利だな?