Visual Studio Tools for Unityでライブラリをつくろうと思っていたら謎のエラーに嵌ったのでメモ。
前提知識
Visual Studio Tools for Unity (VSTU)とは?
Visual Studio Tools for Unity (VSTU)は、 言わずと知れた統合開発環境Visual StudioでUnity用のコードを記述・デバッグするためのプラグインです。 元々SyntaxTreeという会社が開発していたUnityVSという有料プラグインだったのですが、 MicrosoftがこのSyntaxTree社を買収し、新たにVisual Studio Tools for Unityとして無料公開しました。 またUnity5.2からはUnity自体がVSTUをサポートするようになり、 Windows環境での開発には欠かせないツールとなりました。
主な機能として
- ブレークポイントなどを利用したUnityコードのデバッグ
- Unity風のプロジェクト階層表示
- MonoBehaviourのメッセージテンプレート(On~みたいなやつ)の作成
- API Referenceの表示
などがありますが、詳細はググってください。
micro mscorlibとは?
Unityでモバイルプラットフォーム向けにビルドするときにファイルサイズを小さくするためのオプションとして"Stripping Level"というものがあります。 この項目で"Use micro mscorlib"を選択したときに使用されるmscorlibがmicro mscorlibになります。
そもそもmscorlibというのは何なのかというと、 Unityの基盤である.NET Frameworkの標準ライブラリのコードを含むアセンブリ(DLL)のうち最も重要なものになります。 System.Object型やSystem.String型などの定義はすべてこの中にあります。 ちなみにMultilanguage Standard Common Object Runtime Libraryの略らしいです (Microsoft Core Libraryかと思ってた)。
Unityでビルドすると通常このmscorlib.dllが含まれるのですが、 この中にはゲームには滅多に利用されないクラスがたくさんあります。 そこで、Unityが使わないクラスをすべて除いた特別なmscorlib.dllを利用することでファイルサイズを減らそうという試みが"Use micro mscorlib"オプションになります。 そしてこのとき用いられる特別なmscorlibを便宜上micro mscorlibと呼びます。
一見、"Use micro mscorlib"オプションは常に有効にしておけばいいようにも思えますが、 実際「Unityが使わないクラスを除いた」というのはかなり大胆な方法で、 何も考えずにゲーム開発をしているとうっかり除かれたクラスを使ってしまいます。 この場合、もちろんビルドが通らずエラーとなります。
問題なのはこのエラーがモバイル向けにビルドするまでわからないということです。 誤って除かれたクラスを使って書き進めてしまうと地獄の修正作業が待っています *1。
VSTUを用いたライブラリの作成
VSTUでは誤って使用できないクラスを参照しないように専用の対象フレームワークオプションを用意しています。 Visual Studioからプロジェクトのプロパティを表示し、アプリケーションタブの『対象のフレームワーク』を変更することで設定できます。
選択できるのは以下の4つ。
- Unity 3.5 .net full Base Class Libraries
- Unity 3.5 .net Subset Base Class Libraries
- Unity 3.5 Micro Base Class Libraries
- Unity 3.5 Web Base Class Libraries
通常は上の2つを使います。 どちらを使うかはUnityの設定によって変わります。 "Api Compatibility Level"で".NET 2.0"を選択している場合にはfull~を、".NET 2.0 Subset"を選択している場合にはSubset~を利用します。 Web~はその名の通り、Web向けにビルドするときに利用します。 そしてMicro~が今回対象とする"Stripping Level"で"Use micro mscorlib"を選択した時のオプションになります。
Microに設定すると参照できるアセンブリに制限がかかります。 この制限はプロジェクトの『参照』を右クリックして『参照の追加』を選択すると確認できます。
mscorlibはUnityが用意したmicro mscorlibになり、 参照できないアセンブリを追加しようとすると「対象フレームワークが変わるけどいい?」という旨の警告が出ます。
これで開発中に使用できないクラスを参照しようとすると、「そんなクラス知らないよ」というエラーを表示するようになります。 もちろんIntelliSenseの候補にも表示されません。
謎のエラー
んで、本題。
Microを対象フレームワークとして設定してビルドしてみるとエラーがでます(正確には警告だけれども)。
フレームワーク アセンブリであるプライマリ参照 "System.Core" は、現在ターゲットされているフレームワークで解決できませんでした。 ".NETFramework,Version=v3.5,Profile=Unity Micro v3.5"。 この問題を解決するには、参照 "System.Core" を削除するか、"System.Core" を含むフレームワーク バージョンにアプリケーションを再ターゲットしてください。
要はSystem.Coreアセンブリを参照しようとしているけどそんなものはないよ、というエラーです。 System.CoreアセンブリはMicroでは使用できないように設定されているため参照できないのは当然です。 しかし、そうなると参照に追加できないので参照していないはずです。
どういうことかとしばらく調べまわったところ、どうやらコンパイラが勝手に参照として追加してくれるみたいです。 何故かというとSystem.Runtime.CompilerServices.ExtensionAttributeがSystem.Coreアセンブリで定義されているから。 これはメソッドが拡張メソッドであることを示す属性で、拡張メソッドの含まれたコードをコンパイルするためには必須となります *2。 きっとコンパイルで必要になるから勝手に追加しとくねってことみたいですが、う~ん、ありがた迷惑。 一応コンパイルオプションで無効にできるみたいです。
なんとかなりそうだ、ということを確認して胸をなでおろしたのも束の間、事件が続きます。
ライブラリでUnityのAPIを使うためにはUnityEngine.dllを参照に追加する必要があります。 マニュアルによると、 Program Files\Unity\Editor\Data\Managed\UnityEngine.dll にあるそうなのでこれを追加します。 すると、また別のエラー(警告)が現れます。
プライマリ参照 "UnityEngine" は、現在ターゲットされているフレームワークで解決できなかったフレームワーク アセンブリ "System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" に間接的に依存するため、解決できませんでした。 ".NETFramework,Version=v3.5,Profile=Unity Micro v3.5"。 この問題を解決するには、参照 "UnityEngine" を削除するか、"System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" を含むフレームワーク バージョンにアプリケーションを再ターゲットしてください。
UnityEngineがSystem.Coreアセンブリを参照している!!
ついでにSystemアセンブリ(同じく使用不可)も・・・
これはもう、なんというか、どうしようもない気がする・・・
対策
結局、問題は解決できていないのですが、自分の考えられる対策としては2つ。
- あきらめて"Unity 3.5 .net Subset Base Class Libraries"を使う
- 所詮警告なので無視する
ひとつめ、MicroはSubsetのさらにサブセットなのでツールの利便性を捨てて、使用できないクラスを逐一調べる方法。 実はUnityのメインプロジェクト(ゲーム作成時に自動的に作成されるプロジェクト)では"Stripping Level"の設定に関わらずSubsetの方が使われています *3。 なので、まあそれほど悪い選択でもないかなという感じ。
ふたつめ、警告を無視してそのまま使う方法。 結局はUnityに取り込んで、うまい感じに参照解決されるだろうからいいんじゃない?という楽観的な方法です。 アセンブリのメタ情報に関してはあまり詳しくないのでわかりませんが、 ライブラリのアセンブリに記述された情報とUnityが実際に使うアセンブリの情報が食い違って問題が起こる可能性がないとも言えないので微妙な選択です。 なにより、警告を放置するというのが非常に気持ち悪い。
自分は今までも手動で調べていたのでSubsetで妥協することにしました。
おわりに
どういうわけか"Unity 3.5 Micro Base Class Libraries"に関する情報が検索にほとんど引っかからず、今回かなり時間を浪費しました。 そのうえ、結局は妥協。 なんだかなあ・・・
こんな記事書いておいてなんですが、最近micro mscorlibに使うほどの価値があるのか疑問に思ってます。 Unityのインストールフォルダ内にある通常のmscorlibのサイズは2.38MB、micro mscorlibは1.94MB、 もちろんここからネイティブコードにコンパイルされるわけですが、実際そんなに減ってないんじゃないかな、と。 まあ、ビルドして測ってみないとわかりませんが。
余談:(拡張子)
本題とはまったく関係ありませんが、拡張メソッドの話がでたのでついでに。
日本語版Visual StudioのIntelliSenseで拡張メソッドを見ると何故か最初に『(拡張子)』と書かれています。 拡張メソッドは英語で"extension method"、拡張子は"extension"。
・・・まあ、それだけです。
ちなみにもうずいぶん長いことこのままです。 もしかすると面白いからそのままになっているだけなのかもしれません。
使用したVSTUのバージョン:2.2