UnityiOS向けビルドを別アプリに含んで起動する


概要

最近Unityで遊んでるのだけど、ちょっと思う所あってゴニョゴニョと弄ってみてた。


今回試したのは、iOS向けにビルドしたUnityのアプリパッケージを全く弄らず

Xcodeで自前作成したアプリケーションから起動してみようぜ、というもの。

追求したい利点としては

・端末へのインストールをもうちょい楽に自動化したい

いろいろとまあ、なんだ、そのままやると大変。


iOSのネイティブアプリケーション部分のバージョン管理がしやすくなる

本命。

iOSのプロジェクト本体と、まるっと生成されるUnityでのアプリケーション、

従来ならUnityに完全包含されてしまう、iOS側の設定がとっても楽になる。


などなので、決して、非プロ版で起動スプラッシュ外そうぜとかそういうノリでやっている物ではない。

、、プロ版は、、、いいぞ、、、!!



完成系のイメージ

自分で適当に制作したアプリケーションに、任意のUnityのプロジェクト一式を読み込んで、

任意のタイミング、コード上のポイントから、Unity起動~終了、などが扱えるようになる。


手順

①UnityでiOS用にプロジェクトをビルドしておく。プロ版とか、そうでない版のプロジェクトを用意


②所変わって、XcodeでiOS用の新規プロジェクトを用意、ここではSampleProjectって名前にする

ちなみにARCは使用不可

Unityが吐き出すApplicationがバリバリにAutoreleasePoolとか使ってるので。

スクリーンショット 2012-12-04 2.13.22.png

③SampleProjectのprefixに設定を追加する 


作成したアプリケーションのXcode上でのグループ

SampleProject/Supporting Files/SampleProject-Prefix.pch 箇所に下記を足す。


#ifdef __cplusplus

extern "C" {

#endif

    void UnitySendMessage(const char* obj, const char* method, const char* msg);

#ifdef __cplusplus

}

#endif


#define printf_console printf


#define IPHONE_TRAMPOLINE 1


こいつは、Unityのアプリケーションを足した際、主にprintf_consoleの為に使う。

UnitySendMessageも大事だけど。


④下記のframeworkを追加


SystemConfiguration.framework

CoreLocation.framework

MediaPlayer.framework

UIKit.framework

OpenGLES.framework

QuartzCore.framework

OpenAL.framework

libiconv.2.dylib

CFNetwork.framework

AudioToolbox.framework

iAd.framework

CoreMedia.framework

CoreVideo.framework

AVFoundation.framework

CoreGraphics.framework

CoreMotion.framework

GameKit.framework


すべてUnityに含まれているもの。


⑤UnityをBootするクラス UnityBooter を作成する

NSObjectを拡張してクラスを一つ作る。

起動部分でC++を使うため、Obj-C++、拡張子は.mm にする。


この後で、Unityのプロジェクト一式を含むようにするため、

このクラスの初期化メソッドからUnityを起動できるようにしむける。


下記が UnityBooter.mm の内容。


#import "UnityBooter.h"


#import "AppController.h"

#import "RegisterClasses.h"

#import "RegisterMonoModules.h"


static const int constsection = 0;

bool UnityParseCommandLine(int argc, char *argv[]);



@implementation UnityBooter

- (id) initWithApp:(UIApplication * )application withLaunchOptions:(NSDictionary * )launchOptions {

    if (self = [super init]) {

        RegisterMonoModules();

        

        AppController * unityApp = [[AppController alloc]init];

        [unityApp application:application didFinishLaunchingWithOptions:launchOptions];

    }

    return self;

}

@end


この時点で、下のようなプロジェクトの構成になっているはず。

スクリーンショット 2012-12-04 2.12.43.png

で、initWithApp メソッドをAppDelegateから適切な引数で呼ぶようにする。

UnityBooter.hにも書いておこう。


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

{

    self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];

    

  UnityBooter * booter = [[UnityBooter alloc]initWithApp:application withLaunchOptions:launchOptions];

    

    self.window.backgroundColor = [UIColor whiteColor];

    [self.window makeKeyAndVisible];

    return YES;

}




⑥UnityでiOS用 Device用ビルドを、自作プロジェクトに放り込む

ほんとにそのまま放り込む。

ここでは、Unityから吐き出したiOSアプリ用プロジェクトを、iOSUnityという名前にした。

(Device用に設定したため、Deviceでしか動かないが、過去の記事を参考に何かすると、Simulatorでももちろん動かせる。)

http://sassembla.github.com/Public/2012:12:01%2012-03-18/2012:12:01%2012-03-18.html

一度SampleProject フォルダに放り込んだ後、XcodeからSampleProjectへとaddする。

スクリーンショット 2012-12-04 2.14.32.png



そのままプロジェクトに含んだ結果。

スクリーンショット 2012-12-04 2.16.32.png

⑦Unityから持ち込んだ main.mmファイルを、remove reference する

Xcodeグループ内、iOSUnityApp/Classes/main.mm の参照を消す。

mainが2つあると、そもビルドが通らないので、referenceのみでいいからremoveする必要がある。

ここだけはどうしようも無かった。 ファイルを捨てる必要は無い。

スクリーンショット 2012-12-04 2.26.17.png

⑧C++ Standard Library の設定を、Defaultに変更

スクリーンショット 2012-12-04 2.33.43.png

Compiler Default に変更する。

スクリーンショット 2012-12-04 2.33.51.png


⑨Valid Architectures を、armv7 armv7s から、armv7 のみに変更する

Unityで使用しているライブラリが、armv7sに対応してないので、armv7のみをValidなアーキテクチャとして設定する。 

(Unityは、ターゲットがarmv7s対象のみ、とか設定したら、7s対応したVersionのライブラリを吐くのかもしれない。未確認。)

スクリーンショット 2012-12-04 2.51.05.pngスクリーンショット 2012-12-04 2.56.23.png

⑨Build Active Architecuture Only に No と入力

ビルド時にarmv7のみを設定するようにしたため、この部分がNoでないと矛盾してエラーが出る。

スクリーンショット 2012-12-04 3.20.33.png

⑩Build Phases に、UnityのDataをアプリケーションビルド時に内部にコピーするスクリプトを追加する

スクリーンショット 2012-12-04 2.58.40.png

スクリーンショット 2012-12-04 2.58.46.png

位置 = 実行順序をドラッグして調整

スクリーンショット 2012-12-04 2.58.53.png

内容として、下記を入力。太字部分はUnityのプロジェクト名


rm -rf "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Data"

cp -Rf "$PROJECT_DIR/iOSUnityApp/Data" "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Data"

if [ "$PLATFORM_NAME" == "iphonesimulator" ]

then cp -f "$PROJECT_DIR/iOSUnityApp"/*.png "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/"

                          fi

これで、iOSUnityApp内に含まれるDataフォルダが、アプリケーションビルド時に、.app内に含まれるようになる。

これをやらないと、Unityの起動後、シーンデータなどが読み込めずに落ちる。


おまけ①:非プロ版の場合、Applicationに自前で含まれている Supporting Files 下のスプラッシュを消す必要がある

Unityのプロジェクトに含まれているものが使われないと、落ちる。

素晴らしいフェイルセーフ。異論は無い。


アプリケーションの中の SampleProject/Supporting Files の中のpngを削除。

これで、自動的にUnityプロジェクトに含まれるものが使われる。


スクリーンショット 2012-12-04 3.15.42.png


おまけ②:実機にインストールする場合、一度Xcodeを再起動しないと、Valid Architectures の設定が読み込み直されない。

Xcode終了して再度プロジェクト開けばOK。




以上で、ビルドは通り、実機を繋げば実行できる、、ハズ。


参考までに、Unityのプロジェクトを含まない外装一式をgithubに置いておいた。

https://github.com/sassembla/UnityFreeLocation_SampleProject


起動からボタンを表示するViewをつけて、ボタンが押されたらUnity起動、という遷移のスクリーンショット

めざめたおっさんが走り出す。 時間が戻ってるのはすいません、キャプチャミスった。

非プロ版だと、ちゃんとUnity起動時に powered by Unity のSplashが出る。

スクリーンショット 2012-12-04 3.58.48.pngスクリーンショット 2012-12-04 3.58.56.png


ツッコミ等あれば、@toru_inoueまで。