Unity分かりにくすぎて伝わらないエラー選手権

#はじめに

Jenkins等でのUnityのコマンドラインビルドはなかなか辛いものがあって、エラー発生時にどこで何がエラーだったのかが非常に分かりづらいです。

``Scripts have compiler errors.``

とログの末尾に表示されるのはいいものの、それがどこのエラーなのか、ときに数十MBにもなるログファイルから見つけるのは至難の業です。

結局はテキストエディターなんかでそれっぽい単語を検索するしか手がなく、私は

- error
- failed
- could not
- not found
- unable

などで探しています。単語の前か後ろにスペースを入れて検索すると、ファイル名との区別がつくので探しやすくなるというバッドノウハウはありますが…。(ex."ErrorMessage.cs"とかが検索されなくなる)

ということで、以下はよくあるエラーとその対処方法です。

#よくあるエラーとその対処方法

> UnityException: Bundle Identifier has not been set up correctly
Please set the Bundle Identifier in the Player Settings. The value must follow the convention 'com.YourCompanyName.YourProductName' and can contain alphanumeric characters and underscore.
Each segment must not start with a numeric character or underscore.

BundleIdentifierが未設定か、正しく設定されていません。この場合はAndroidなのでハイフンが使えません。このエラー、単純ですが新規プロジェクトを作るたびにやりがちで、しかも、ひととおりアセットのインポートが終わってから出るのがたちが悪い…。(Unity2017だと昔よりエラーメッセージが詳しくなった？？)

---
> Exception: iPhoneBundleIdentifier has not been set up.
Please set up the iPhoneBundleIdentifier in the Player Settings. The value should follow the convention 'com.YourCompanyName.YourProductName' and can contain alphanumeric characters (a-z, A-Z, 0-9) and hyphen (-). You also have to create a Provisioning Profile with the same identifier and install it in the Xcode Organizer Provisioning Profiles.

上記のiOSビルド版。こちらはアンダースコアが使えません。

---
> Updating Assets/Textures/Hoge.png - GUID: ****************...
Could not create texture from Assets/Textures/Hoge.png: File could not be read

画像ファイルが壊れていたり、Unityが読み込むことができないフォーマットである場合に発生します。使用していない画像ファイルであってもUnityはProject内のすべてのAssetをインポートするので、たまたま何かと一緒に入れた未使用のファイルだったり、FBXファイル内の画像ファイルが展開されるときに悪さをすることがあります。

---
> Assets/Scripts/Hoge.cs(1,1): error CS0103: The name `UnityEditor' does not exist in the current context

Editorフォルダ以外にあるスクリプトで``using UnityEditor;``を宣言した場合に発生します。``#if UNITY_EDITOR``でくくるという手もありますが、事故を避けるためにEditorフォルダに入れたほうが良いでしょう。(#ifで囲んだせいで中カッコの対応がずれてエラーになるとかありがち)

---
> Assets/Scripts/Hoge.cs(1,1): error CS0246: The type or namespace name `UnityEditor' could not be found. Are you missing an assembly reference?

Editorフォルダ以外にあるスクリプトでUnityEditorのAPIを使ったを宣言した場合に発生します。対応策は上記と同じです。ビルドするまで分からない、というのが辛いところです。

---
> UnauthorizedAccessException: Access to the path "/Users/Shared/Jenkins/Home/workspace/HogeJob/Assets/Resources/Hoge.json" is denied.

ビルド時にスクリプトで必要なファイルを動的生成するようにしている場合などで、そのファイルを誰かが間違ってPerforceなどのVCSに登録してしまっていたときに発生します(PerforceではCheckOutしてないファイルはロックがかかるため)。除外しましょう。

---
> Assets/Scripts/Hoge.cs(1,1): error CS0234: The type or namespace name \`Xcode' does not exist in the namespace `UnityEditor.iOS'. Are you missing an assembly reference?

iOS版以外では理由は単純で、他プラットフォームでは不要なはずのXcode関連APIをusing宣言しているために発生します。#if UNITY_IOSなどで囲んで除外ください。

iOS版では非常に面倒なエラーです。このスクリプト自体に問題はなく、Unity側からXcode関連dll(PBXProject等)の参照が追加されないことがあるようです。

原因の究明に数日かかった(※実はここの解説も何度か書き換えました)んですが、InitializeOnLoadアトリビュートを付けたクラスのコンストラクタでPlayerSettings.SetScriptingDefineSymbolsForGroupを呼ぶと発生します(たぶん)。このAPIを呼んだ直後にスクリプトの再コンパイルが走るんですが、InitializeOnLoadの段階では何らかのiOS依存の処理が行われる前なのか、Xcode関連dllが設定されない状態でコンパイルしてしまうようです。InitializeOnLoad以外で呼びましょう。(2017/11/13追記。いずれ詳しく調査したい…)

---
> The same field name is serialized multiple times in the class or its parent class. This is not supported: Base(MonoBehaviour) Hoge

一見、エラーっぽいメッセージではありませんが、ビルドに失敗しています。基底クラスにあるpublicなフィールドと同名のフィールドを派生クラスで宣言した場合に発生します。ただ、コンパイルの段階では警告(CS0108)で済むんですが、これらのクラスが何らかのオブジェクトにAddComponentされてシリアライズ対象になった場合に名前解決ができずにUnityがエラー扱いにするようです。

これはもう、お作法的にもよろしくないので、設計を考え直しましょう。VisualStudioでは"new"を付けることを薦めてきますが、それでもエラーになります(同名フィールドが残ることには変わりがないので)。しかし、どのクラスが問題だったのか、フィールド名から推測するしかないのが厳しい…。
(2018/2/6追記)

---
> Receiving unhandled NULL exception

原因不明。次のビルドでは成功するかもしれません…。

---
> java.lang.NullPointerException

原因不明。次のビルドでは成功するかもしれません…。たぶんJenkinsがらみ。

---
> Building the player failed. APIUpdater encountered some issues and was not able to finish.
Unable to save updated script: Access to the path "/Users/Shared/Jenkins/Home/workspace/HogeJob/Assets/Scripts/Hoge.cs" is denied.

コマンドラインビルド時は"API Updater"が強制的に走ってしまうため、DeprecatedなAPIが残っていて、かつ、Perforceなどでファイルがロックされている場合に発生します(IntegrateされていてもなぜかCheckOutしてくれない)。事前にAPIを更新しましょう。

＃なお、Unity2017.2からはコマンドラインオプションでAPI Updaterを無効にできるようです。

---
> Error 131 running AssemblyUpdater. Its output is: `System.UnauthorizedAccessException: Access to the path "/Users/Shared/Jenkins/Home/workspace/HogeJob/Assets/FacebookSDK/Plugins/Editor/Facebook.Unity.Editor.dll" is denied.

同じくAPI Updaterがらみですが、これはエラーではあるもののビルドは成功するようです。(DeprecatedでもDLLには存在しているっぽい？)

---
> API updating skipped: timeout waiting for version control to get ready. You may run the updater manually via the 'Assets/Run API Updater' menu command.

IntegrateされているVCSの設定が間違っている場合にタイムアウトして、API Updaterが実行されないとこうなるんですが、これもビルドは成功します。

---
> Unable to convert classes into dex format.
> trouble writing output: Too many field references: 00000; max is 65536.
> You may try using --multi-dex option.

Android5.0未満が採用するDalvikというJava仮想マシン(正確にはJavaじゃないけど)には「メソッド64K参照制限」があって、1つの実行ファイル内のメソッド数が65536個を超えることができません。Google Play Service Pluginを導入すると発生しやすいです(メソッドが非常に多いため)。

対応策はいろいろあるんですが、MultiDexにするとか、不要なjarを消すとか、ProGuardで最適化するとか、ちょっと調べればいろいろ出てきます。

---
> Error: Error while saving blame file, build will continue
> Error: 要素タイプ"uses-sdk"に関連付けられている属性"tools:overrideLibrary"の接頭辞"tools"がバインドされていません。

Androidビルドで発生するエラーです。マニフェストファイル(AndroidManifest.xml)のマージツールが26.0.2から変わり、今まで省略可だった記述が必須になったようです。この場合、

``<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.unity3d.player">``

のように、 ``xmlns:tools="http://schemas.android.com/tools"`` を Assets/Plugins/Android/AndroidManifest.xml に追記しましょう。

他の問題でも同様のエラーが出ることがあるようですが、どちらにせよ各プロジェクトでのマニフェストファイルの問題のようで、UnityのIssueTrackerには"Won't Fix"="直さないよ"と書いてありました。

---
> '' is an incorrect path for a scene file. BuildPlayer expects paths relative to the project folder.

ビルド対象になっているシーンを消しても、BuildSettingsのシーンリスト(Scenes In Build)は自動的には更新されません。シーンが存在しない場合、BuildPlayerではエラー扱いです。シーンリストから削除するか、シーンファイル(.unity)を復活させましょう。

でもこれ、ビルド失敗のエラーメッセージとは離れた位置に表示されるし、これが原因だなんて普通分からんて…。

# まとめ

各ユーザーのローカル環境では問題なくても自動ビルドでは失敗するというのはよくある話で、そういうときはログファイルだけが頼りです。

ログファイルではapkやAssetBundleのファイルサイズだけでなく、AssetBundle内の個々のファイルのサイズも分かるので(※AssetBundleBrowserで表示されるファイルサイズはウソです！！)、いつもEditorからビルドしている方もコマンドラインビルドを試してみてもと良いと思います。

そんな場合にこれが参考になれば幸いです。