2012年11月12日月曜日

Unity 3.5 以下でプリプロセッサ定義を上手く使う方法

C/C++/Objective-Cベースのプロダクトのビルドシステムを構築するときに大変便利なのがプリプロセッサのdefineマクロです。defineマクロを使って環境を切り替えたり不要なログコードを削除したりなどは皆さんよくやってるかと思います。C#にも同様のプリプロセッサ定義が用意されているみたいですので、こちらを使ってUnityプロジェクトのビルドシステムを構築できないかと思ってやってみました。


■プリプロセッサ定義の使い方 (Unity 4以降)

Unity 4.0よりプリプロセッサ定義がUnity本体によってサポートされるようになりました!ビルドスクリプトからも自由にアクセスが可能になっており、非常に簡単に扱うことが可能です。
詳しくはけいごさんのブログに完璧にまとめられていますのでそちらをご覧ください。
http://anchan828.tumblr.com/post/32669868103/define


■プリプロセッサ定義の使い方 (Unity 3.5以降)

ここからが本題になります。Unity 3.5以前はプリプロセッサ定義を公式にサポートしていません。そこで以下の4種類のファイルをAssetsフォルダ直下に置くことでプリプロセッサ定義を読み込ませます。
/Assets/smcs.rsp (C#用)
/Assets/gmcs.rsp (C#エディタスクリプト用)
/Assets/us.rsp   (UnityScript、俗にJSと呼ばれている物用)
/Assets/boo.rsp  (Boo用)
このrspファイルにはC#コンパイラのコンパイラオプションとして渡すオプションが記述できます。ですので http://linux.die.net/man/1/mcs などに列挙されているコンパイラオプションであればひと通りなんでも使用可能です。今回はプリプロセッサ定義をやりたいので、以下のように書けばOKです。
-define:DEBUG
複数の定義が書きたい場合は、
-define:DEBUG -define:USE_DEV_SERVER
のようにスペースで空けて複数記載するか、
-define:DEBUG
-define:USE_DEV_SERVER
のように改行して複数記載するか、
-define:DEBUG;USE_DEV_SERVER
のようにセミコロンで列挙して書くことができます。注意点として、C/C++/Objective-Cのdefineマクロと違って
-define:DEBUG=1
のように値を定義することはできないみたいです。あくまでシンボルを作るだけみたいですね。

以下Twitterでのやりとりです。

さて、これでプリプロセッサ定義はバッチリできるようになったのですが、問題はこれをビルドごとに書き換える方法です。最初はビルドスクリプトでこのrspファイルを書き換えていたのですが、どうもこれをUnityが全く認識しないのです。普通にファイルをただ書き換えてもダメなようです。

そこで黒魔術を導入します。以下のスクリプトで使われているコードを利用して、無理やり書き換えた後のrspファイルをUnityに認識させます。
https://github.com/prime31/P31UnityAddOns/blob/master/Editor/GlobalDefinesWizard.cs#L131
AssetDatabase.Refresh();
reimportSomethingToForceRecompile();

private void reimportSomethingToForceRecompile()
{
 var dataPathDir = new DirectoryInfo( Application.dataPath );
 var dataPathUri = new System.Uri( Application.dataPath );
 foreach( var file in dataPathDir.GetFiles( "GlobalDefinesWizard.cs", SearchOption.AllDirectories ) )
 {
  var relativeUri = dataPathUri.MakeRelativeUri( new System.Uri( file.FullName ) );
  var relativePath = System.Uri.UnescapeDataString( relativeUri.ToString() );
  AssetDatabase.ImportAsset( relativePath, ImportAssetOptions.ForceUpdate );
 }
}
これでC#で書かれたコードに対しては無事にrspファイルへの変更が反映されるようになります。

しかしながら更に落とし穴があります。UnityScript(JavaScript)で書かれたファイルに対してはこの黒魔術が効きません。したがってUnityScriptで書かれたファイルに対して動的にus.rspファイルの定義を適用することができないようです。すべてのJSファイルに対して上記の黒魔術を適用しても効果がなかったため、おそらくダメだと思われます。


■結論

UnityScriptは使わないようにしましょう。