やらなイカ?

たぶん、iOS/Androidアプリの開発・テスト関係。

Realm(realm-cocoa)を使うアプリをテストする #realm_jp

iOS/Android向けDBMS+ORMのRealmを利用しているアプリのテストコードの書きかた、またテストコードからIn Memory Storeで使用する方法を試してみました。

realm-cocoa のバージョンは 0.90.6 を使用しています。

テストターゲットの準備

Realmをプロジェクトに取り込む方法はいくつかありますが、今回はCocoaPodsを利用しました。このとき、下記のようにテストターゲット側にもRealm/Headersの利用を宣言する必要があります。

target 'RealmInMemoryStoreExampleTests', exclusive: true do
  pod 'Realm/Headers'
end

テスト用のRealmインスタンスを使用する

Realmのサンプルコードにあるように[RLMRealm defaultRealm]でデフォルトRealmインスタンス*1を使用すると、テストコードからの実行でもテスト対象アプリの利用するDBを使ってしまうため、これに干渉してしまいます。

また、[RMLObject allObjects]といったRealmオブジェクトを指定しないメソッドは総じて、デフォルトRealmインスタンスを使用します。

これを避け、テストコードからはデフォルトとは異なるDBを利用する方法は二通りあります。

方法1: setDefaultRealmPathでデフォルトを置き換える

プロダクトコード(テスト対象アプリ)側で[RLMRealm defaultRealm][RMLObject allObjects]などを直接利用している場合、テストコード側であらかじめ+[RLMRealm setDefaultRealmPath:]を呼ぶことで、以降のデフォルトRealmを置き換えることができます。

通常は[setUp]を下記のようにすれば良いでしょう。

- (void)setUp {
    [super setUp];

    //default realmをテスト専用のものに置き換える(テスト対象アプリのrealmファイルに干渉させないため)
    NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSString *realmPath = [docPath stringByAppendingPathComponent:@"test.realm"];
    [RLMRealm setDefaultRealmPath:realmPath];

    //内容をクリア
    RLMRealm *realm = [RLMRealm defaultRealm];
    [realm beginWriteTransaction];
    [realm deleteAllObjects];
    [realm commitWriteTransaction];
}

これで、以降個々のテストメソッドでは全てのオブジェクトがクリアされたクリーンなtest.realmに対して読み書きを行なうことができます。

なお、DBのクリアは意味的には「後処理」ですが、テストがエラーで落ちる等の要因で実行されないと次(次回)のテストが失敗してしまうこともあります。安定したテスト実行のために[tearDown]でなく「前処理」である[setUp]でクリアを行なっています。

方法2: テスト対象メソッドでRealmインスタンスを受け取る

メソッドの引数、もしくはRealmを扱うクラスのインスタンスフィールドにRealmインスタンスを保持し、これをテストコードから書き換え可能にしておきます。

Realmインスタンスは、デフォルトでなく[RLMRealm realmWithPath:]でファイルパスを指定して初期化できます。 プロダクトコードでは[RLMRealm defaultRealm]を利用し、テストコードからは[RLMRealm realmWithPath:]でファイル名を指定することで専用のRealmインスタンスを使用できます。

プロダクトコード

+ (Employee *)addEmployeeWithName:(NSString *)name salary:(float)salary realm:(RLMRealm *)realm{
    Employee *newEmployee = [[Employee alloc] init];
    newEmployee.name = name;
    newEmployee.salary = salary;
    newEmployee.addDate = [NSDate date];

    //引数で受け取ったRLMRealmインスタンスを使用する
    [realm beginWriteTransaction];
    [realm addObject:newEmployee];
    [realm commitWriteTransaction];

    return newEmployee;
}

テストコード

- (void)testAddEmployee {
    NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSString *realmPath = [docPath stringByAppendingPathComponent:@"test.realm"];
    RLMRealm *realm = [RLMRealm realmWithPath:realmPath];

    //テスト用のRLMRealmインスタンスを渡す
    [EmployeeManager addEmployeeWithName:@"John Cena" salary:2000000 realm:realm];
}

テストデータをファイルで用意して使用する

大量のデータや複雑なクエリをテストするためのデータのinsertは、テストコードで行なうよりも、あらかじめRealmのファイル形式で準備するほうが管理しやすいケースもあるでしょう。*2

これは、上記いずれかの方法でRealmファイルパスを指定してRealmインスタンスを得る場合、あらかじめ用意したRealmファイルをアプリのDocuments下に置き、そのパスを[setDefaultRealmPath:][realmWithPath:]に渡すことで実現できます。

テスト実行環境のファイルの扱いについては、拙著『iOSアプリ テスト自動化入門』の「2.4.1 テストフィクスチャをファイルでバンドルする」「2.4.3 アプリケーションデータを再現してテスト実行する」が参考になるはずです。

In Memory Storeで使用する

Realmは、ファイルでなくメモリ上だけで動作させることもできます。インメモリでの動作は、ファイルI/Oが無いぶん高速、かつ、実行ごとにクリーンな状態のRealmインスタンスを得られるため、テストコードから使うには最適です。

インメモリで動作するRealmインスタンスは、[RLMRealm inMemoryRealmWithIdentifier:]で生成できます。 こちらはデフォルトを置き換えるクラスメソッドは存在しないため、プロダクトコード側が上記「テスト対象メソッドでRealmインスタンスを受け取る」にあるようなRealmインスタンスを差し替え可能な構造である必要があります。

テストコード

- (void)testAddEmployee {
    //インメモリで動作するRealmインスタンスを生成
    RLMRealm *realm = [RLMRealm inMemoryRealmWithIdentifier:@"test"];

    //テスト用のRLMRealmインスタンスを渡す
    [EmployeeManager addEmployeeWithName:@"John Cena" salary:2000000 realm:realm];
}

余り性能は出ないという話でしたが、小さなオブジェクトを1,000件insertする*3テストでは、

  • ファイル : 0.820[sec]
  • インメモリ : 0.189[sec]

となり、1/4程度には抑えられるようです。

サンプルコード

GitHubに置いてあります。

参考資料

Realmのドキュメント

書籍

iOSアプリ テスト自動化入門

iOSアプリ テスト自動化入門

*1:実体はアプリのDocumentsディレクトリ下にdefault.realmというファイル

*2:例えば、RealmファイルはiOS/Androidで同一なので、両プラットフォーム対応の場合には共通化もできます

*3:1レコードごとに毎回commitしています

Realm Tech Talk with JP Simard 1 に行ってきました #realm_jp

iOS/Androidで使用できるDBMS(+ORM)であるRealmのイベント、Realm Tech Talk with JP Simard #1(於 freee株式会社さん)に行ってきました。

Tech Talk with JP Simard from Realm

Realmのメンバー、JP SimardさんによるRealmの紹介。

Realmの特徴

  • オープンソース(ASL 2.0)である。なお、現時点でDBのCore部分はまだクローズドだが、オープンにする方向で進めている。
  • iOS/Androidで同じように使用できる。リリースタイミングのずれはあるが*1、双方、同じ機能を提供する。

既存DBMS(およびORM)との比較

  • iOSのCore DataはSQLiteのORM(Object Relational Mapper)で、古く、遅い。学習コストも高い。
  • SQLiteは早いが、ORMでなくSQLクエリで操作する必要がある。スレッドセーフでない。
  • ほか、FMDB(SQLiteのラッパー)などもあるが、総じて遅い。またAndroid向けには選択肢が少ない。
  • ベンチマーク
    • 単純なinsertは、生のSQLiteが早い(178k件/sec)が、Realmはそれに次ぐ(94k件/sec)。FMDB(47k件/sec)、Core Data(18k件/sec)
    • レコード数カウントは、Realm(30.9回/sec)、SQLite(13.6回/sec)、Core Data(1.0回/sec)。なお、数値はDBに200kレコード入っている状態のクエリ実行回数
    • 抽出クエリは、Realm(31回/sec)、SQLite(14回/sec)、Core Data(2回/sec)

モデルの定義

RLMObjectを継承したValue Objectを定義する。

@interface Employee : RLMObject
@property NSString *name;
@property float salary;
@end

RLM_ARRAY_TYPE(Employee)

@interface Company : RLMObject
@property NSString *name;
@property RLMArray<Employee> *employees
@end

これで、Employeeテーブルと、Employeeの配列(内部的にはrelation)を持ったCompanyテーブルができる。Swiftではデフォルト値の定義も可能。 (コードはその場でメモした擬似コードなので注意)

使いかた

Add(insert)はこんな感じで可能。

let employees = List<Employee>()
let company = Company()

Realm.write{     //transaction
    defaultRealm.add(company);
}

GCDから使う場合

dispatch_async(queue){
    realm.beginTransaction()
    ...
    realm.commitWriteTransaction()
}

(※コードはイメージです)

抽出クエリは、#filter()をチェインしていく形式で記述。

マルチスレッドでRealmを使っても、ちゃんと捌いてくれる。ただし、クエリで取り出したインスタンスはそのスレッドでしか使用できない。別スレッドでは再取得する必要があるので注意。

Notifications

例えばinsertされたタイミングで指定メソッドの呼び出しを登録できる。何がinsertされたかは(今は)わからない。

注意点

  • NSDataは、秒単位までしか保持されない。これはAndroidとの整合性のため。ミリ秒以上の精度はNSTimeintervalを使う。
  • KVOは未サポート

Work In Progress

  • multi process
  • fine-grain notifications(Notificationで操作されたオブジェクトも通知できる)
  • async queries
  • cascading deletes(リレーションのレコードを連鎖で削除する)
  • nullable types(現状はNullを入れられない)
  • sync
  • open source core

質疑応答

理解できたものだけ。

  • Realmのファイルは、iOS/Androidで共通フォーマットなのでファイルコピーで共有できる
  • desktop appで、Realm Browserというのがある。CUIツールは無い
  • coreは、TightDB(旧社名?)
  • in memoryで利用するには、begin transactionのとき指定できる
  • arrayのcountをクエリで取ることはできない(カウントを格納するプロパティを設けるなどのwork around)

realm-javaを使ってみての発表

Chatwork 宮下さんの発表。ChatworkのAndroid版で実際に使用したお話。

Android のORMはそもそも選択肢が少ない。

  • greenDAO: 早いけど準備が面倒
  • ActiveAndroid: insertが遅い(Chatworkさんの事例での話で、大量にinsertが発生すると厳しい)
    • Benchmark: 619件の処理(insert?)で、ActiveAndroidでは1551ms、Realmは414ms。

realm-javaでのモデル定義例

class xxx extends RealmObject{
    @primaryKey private long id;
}
  • RealmObjectはvalue objectでなくてはならず、ロジックは持てない。toString(), equals()とかもオーバーライドできない。
  • Realmのobjectは、スレッド単位でしか利用できない。別スレッドで扱うならqueryしなおす
  • 日本語ドキュメントのバージョンは0.72、最新は0.79.1。かなり変更があるので、最新情報にあたるほうがいい。
  • ButterKnifeと干渉するところがあったが、Gradleの定義で回避できた

所感

iOS/Androidで共用できるシリアライズ機構としては、Dropboxが公開しているmx3も気になっているのですが、こちらはライブラリでなく実装サンプルであり、ORMも含めて考えるとRealmの有望さを感じました。

モデルがvalue objectに限定されることは不便に思いますが、ロジック付きのラッパーを作ればいいので致命的ではなさそう。

最近、DBMSを使う系のアプリを作っていないのですが、止まっている電エースアプリAndroid版ででも使ってみたい。

懇親会では、freeeさんの会社およびプロダクト*2の話をたくさん聞けて、それもまた色々参考になりました。

freeeさん、岸川さん、通訳を買って出てくださった北さん、そしてJPさん、ありがとうございました。

*1:iOSがやや先行するみたい

*2:クラウド会計をユーザとして使いたいと思っていたので

#ABC2014W におけるOculus Rift体験展示のノウハウ #JAGVR

東海大学 高輪キャンパスで行われた、日本Androidの会のイベント「ABC 2014 Winter」においてVR部として一部屋いただき、Oculus RiftなどによるVRコンテンツの展示、またタオバイザー(Cardboard互換HMD)向けアプリ作成のハンズオンを行ないました。

私はほぼ展示に張り付いていたので、そこで得られた知見・改善点などを書き留めておきます。

なお、セッティング等どうしても汎用的な方向に振るしかなく、当日余り良いVR体験を得られなかった方もいらっしゃったはず。お詫びするとともに、できればこれに懲りず、よりご自身に合ったセッティングで体験いただける機会にリベンジしていただけましたら幸いです。

f:id:nowsprinting:20141221100418j:plain

Oculus Rift DK2

Oculus Riftは、Oculus VR社が販売しているHMD(Head Mount Display)です。現在、DK2(Development Kit 2/開発キット)をOculus VR社のWebサイトから購入(個人輸入)することができます。

今回VR部ではこれを2台(2コンテンツ)用意し、VR(Virtual Reality)体験してもらうという展示でした。

セッティングのポイントは以下3点。

  • レンズはA(通常・遠視向け)固定。ある程度の近視の方には裸眼、それで無理そうな方にはメガネ着用で体験していただきました*1。ここは妥協。
  • レンズ距離は一番近くに設定。私としては少しだけ離すのがベストなのですが、今回は両手が空くコンテンツですし、見づらい方には手で保持して顔から離して調整してもらうことに。
  • IPD(瞳孔間距離)は、今回は5.5cmとしました。IPDについての詳細、決め方など『Oculus Riftでオレの嫁と会える本』Chapter 8-1を参考にしました。

VRコンテンツ作成の入門書的な本ですが、VR酔いへの対応策なども丁寧に書かれています。ぜひ手元に置いておくべき一冊。

PC

880M SLI搭載のゲーミングノートを使用しました。OSはWindows 7。ノートのメリットは本体の可搬性と、説明員が状況を確認するためのモニタがあること。展示に使用するのでなければタワー型のほうがコスパいいです。

おすすめ設定は下記を参照。

コンテンツ

展示したコンテンツは、@youten_redoさんチョイスの以下ふたつ。

UnityCoaster2 UrbanCoaster

iWorks(@x68user)氏によるジェットコースターの定番。Oculus SDK 0.4.4対応のV1.1を使用。

Unity-Chan "Candy Rock Star" Live Demo

ユニティ・テクノロジーズ・ジャパン制作の「ユニティちゃん」ライブのOculus Rift対応版。GitHubリポジトリoculusブランチのものからOculus SDK 0.4.4に入れ替えたもの。

展示向けコンテンツの要件

必須要件としては3点。

  • 適度な長さ(この点、ユニティちゃんライブはショートに変えたほうがよかったかも)
  • VR酔いしにくい品質(75fps出ること)
  • 一回再生するごとに停止し、何らかのアクションで次の再生がはじまる(体験者が入れ替わるため)

さらに、3DVR体験としてのわかりやすさから、あったほうがいい特徴3点。

  • カメラに向かってキャラクターが接近する(3Dっぽさの体感)
  • まわりを見渡せる(ヘッドトラッキング
  • 近づいたり、覗きこんだり、VR世界内のキャラクターがこちらを見る(ポジショントラッキング

今回は借り物のアプリですが、展示を回す中で「あったほうが良い」と感じたことがいくつかありました。今後、自作する際には入れていきたいのが以下。

  • Rキーでポジションのリセット。これはOculus SDKのCameraには入っているのですが、Cardboard向けアプリでも何らかのジェスチャーでリセットできるような仕掛けは考慮したほうがよさそう
  • 再生を中断して、アイドル状態に戻る機能*2。これが欲しい理由は以下2つ。
    • 体験者が途中でHMDを外して離脱してしまうことが何度かあった
    • 体験者がいないとき、PCのモニタで再生しておきたい。それを、体験者が来たときに中断したい。

その他、準備したもの

ヘッドホン

没入感を増すため、周囲の雑音を考慮するとあったほうがいいです。より没入感が欲しいコンテンツ(今回のユニティちゃんライブや、Mikulusなど)はノイズキャンセリングが望ましいかも。

ただしノイズキャンセリングの場合、説明員の声が聞こえなくなるデメリットがあります。必要なアナウンスをコンテンツ側に作りこむ、オペレーションを徹底するなどの考慮は必要でしょう。

ウェットティッシュ

HMDのスポンジ部分を拭くために用意しました。余り活用できてなかったので、スポンジ保護シートなどと組み合わせることを考えたほうがいいかも。

フライヤー

これは用意してなかったのですが、どういったコンテンツか、体験時間は何分か、といった情報は紙で掲示しておくとよかったかも。

また、待ち行列ができるのであれば、HMDの調整の仕方なども事前に読んで待っていただけるとよりスムーズになるでしょう。

オペレーション

呼び込み

Ocufesなどとは異なり、「Androidのイベントに来て、講演の合間にうろうろしたらVR部屋があった」というものなので、積極的に並んでまで体験しよう、という方は少なかったです。 従って、遠目に、興味ありそうに見てる人がいたら呼び込んで被らせる形。

皆さん講演の合間に展示を回っている状況なので、待ち時間、終了時間が見える形でないと並んで待ってまで体験するには至らない、と途中で気付きました。今後似たようなケースであれば改善していきたい。

お子様が来られたとき、明らかに13歳未満でしたので泣いて馬謖を斬る思いでお断りしました。この辺りの根拠についてはこちらにまとめられています。ちゃんと断る覚悟を持つために読んでおいたほうがいいです。

セットアップ

体験者に座っていただいてから、スタートまでに気をつけたこと。

  • まず、正しく座ってもらう。荷物のために斜めに座ったりしてしまう方もいたので、極力まっすぐ座ってもらう。バックパックなどはそれほど問題ないと思ったので背負ったままで。
  • メガネの方、外してもある程度は見えるのかを聞く。
    • ある程度見えるのであれば、裸眼でHMDを被ってもらう
    • かなり悪いならば、一旦外してもらってHMDといっしよにかぶってもらう
  • HMDを被せる。ほとんどそのまま被ってもらってしまいましたが、メガネの方とか女性とか、もっと後ろのマジックテープ着脱を活用すればよかった
  • 目とレンズの位置がずれているとボヤけて見えるので、表示されている文字がくっきり見えているか確認。スループットが落ちてでもここはちゃんと調整してもらうほうがいいはず。
    • 「上下左右に動かしてはっきり見える位置」「もしくは顔から少し離してみる」という説明がうまく伝わらないこともあって、表現は検討・改善していきたい。
  • HMDを装着した状態でリラックスした姿勢を取ってもらい、そこでキーボードのRを押してポジションリセット。

スタート

  • コースターは、"Look Here"の文字を凝視するとスタートします。ちゃんと見るべき位置を言ってあげて、細かく観測補正(ちょい右、ちょい下、とか)してあげれば、問題なくスタートできていました(逆にサポートなしだと厳しいと思う)。
  • コースターが動き出してから、「ヘッドホン被せます」と宣言しつつヘッドホンを被せる。相手は視界を奪われているので、かならず宣言してから。
  • まだスピードが遅いうちに、まわりを見渡せることを伝えました。UnityCoaster2 v1.1では左に女の子が座っているので、それを見てもらうのがインパクトありました。
    • 伝えるタイミングはスタート前でもいいかも知れません
    • ノイズキャンセリングヘッドホンを使う場合は、スタート前に。

ゴール

  • ゴールの建物に入ったあたりでヘッドホンを外してあげます
    • このときも、先に「ヘッドホン外します」と宣言してから
    • 早めに外すのは、自分でHMDを先に外そうとする傾向があるため
  • ほとんどの方はHMDは自分で外してもらっていましたが、後ろのマジックテープを外してあげてもよかったかも
  • 何か言わないと気まずい、なんと言っていいかわからない、という人もいそうなので、とりあえず「酔いとかないですか?」とか先に聞いてみるなどしました

その後

  • ほぼ並んでいない状態ですが、「空いたらやってみたい」くらいの人はいるので、できるだけ早めに椅子から立ってもらったほうがいいです。
  • 座ったまま質問などはじめる方もいるので、できれば次の方をスタートさせるまで先にオペレーションする間、待っていただくよう上手く言えるとベスト。難しいですが。
  • 無理にHUDを外そうとした際にレンズ距離が動いてしまったことがあったので、定位置にあるかチェックする

所感

こういった形でOculus Riftの体験展示を行なうのははじめてだったので、オペレーションは一日かけて体験者の方を観察しながら改善していった感じです。

重ねて書きますが、当日余り良いVR体験を得られなかった方もいらっしゃると思います。申し訳ありませんでした。Oculusは、VR技術は悪くないので、ぜひぜひこれに懲りずにリベンジしていただけることを切に願います。

今回、展示は自作でなく借りもののコンテンツだったのですが、それでも体験された方のリアクションを見ているのはとてもとても楽しかったです。ご来場いただいた皆さま、ありがとうございました。

*1:視力1.0未満だとAではぼやけるそうです。裸眼でも1.0ある人は少ないはずなので、Bのほうがよかったのかも

*2:アプリケーションを再起動すればいいのですが

システムテスト自動化カンファレンス2014で「ビルドプロセスとCI」をお話してきました #stac2014

ヤフー株式会社さんで開催された、テスト自動化研究会の旗艦イベント「システムテスト自動化カンファレンス2014」にスタッフとして参加、また、一枠いただいて講演させていただきました。

f:id:nowsprinting:20141215034050p:plain

写真は会場から見下ろせた、見ろ、リアj ミッドタウンのイルミネーション。

システムテスト自動化 標準ガイド

昨年に続き二回目の開催となったシステムテスト自動化カンファレンス2014。今回は、主催のテスト自動化研究会メンバーで翻訳・執筆を行なった書籍『システムテスト自動化 標準ガイド』で得られる知見を中心に紹介する内容でした。

システムテスト自動化 標準ガイド (CodeZine BOOKS)

システムテスト自動化 標準ガイド (CodeZine BOOKS)

  • 作者: Mark Fewster,Dorothy Graham,テスト自動化研究会,伊藤望,玉川紘子,長谷川孝二,きょん,鈴木一裕,太田健一郎,森龍二,近江久美子,永田敦,吉村好廣,板垣真太郎,浦山さつき,井芹洋輝,松木晋祐,長田学,早川隆治
  • 出版社/メーカー: 翔泳社
  • 発売日: 2014/12/16
  • メディア: 大型本
  • この商品を含むブログを見る

翔泳社様による書籍の先行販売も行われ、ご用意いただいた分および、急遽取りに行っていただいた会社在庫分まで完売してしまったとのこと。購入できなかった方は申し訳ないです。

またどこからか「ギア本」という愛称も付けていただいたようで、翻訳・執筆陣の端くれとして有難い限りです。

システムテスト自動化カンファレンス2014

カンファレンスの内容、また他の登壇者の資料については、connpassやテスト自動化研究会のWebサイトなどに順次上がっていくはずですので、そちらを参照してください。

ビルドプロセスとCI

システムテスト自動化 標準ガイド』の第14章「CI(継続的インテグレーション)」を書かせていただいた流れで、CIやビルドパイプラインまわりについてお話させていただきました。

会場は、何らかのCIツールを使っている方が7割くらい。Travis CIなどのサービスを使っている方は5〜6名ほど。

余りテクニカルな話ではなく、ツール起点で考えていませんか?基本に立ち返るために先人の知見に立ち返ってみては?という話。 「言われてみれば当たり前」のことに気付かないことは往々にしてあるはずで、それをちょっと視点を変えて見てみるきっかけにでもなれば幸いです。

実はかなり直前までオチがつかず困っていたのですが*1、「繰り返しはギャグの基本」という言葉を思い出してまとめてみました。基本大事。

なお、スライド内でも紹介していますが、CIやビルドパイプラインまわりのテクニカルな話は『継続的デリバリー』を読むのが一番だと思います。 『継続的インテグレーション入門』はさらに深く、原点に当たりたい人は読むといいと思います。

継続的デリバリー 信頼できるソフトウェアリリースのためのビルド・テスト・デプロイメントの自動化

継続的デリバリー 信頼できるソフトウェアリリースのためのビルド・テスト・デプロイメントの自動化

継続的インテグレーション入門

継続的インテグレーション入門

以下、質疑応答でいただいた内容とかTweet拾ったものとか。

Jenkinsについて

私のオススメは「かえる本」

Jenkins

Jenkins

あとは用途(目的)に応じて、という感じでしょうか。

Walterについて

Jenkinsのプラグインに対するアドバンテージを質問されました。私としては、

という点です。Jenkinsでもジョブ設定の差分を管理できるJobConfigHistory Pluginがありますが、可用性が断然違ってきます。

また、方向性としては、私の後に講演されたYahoo! JAPAN赤根さんのように、ジョブ自体を毎回生成するアプローチもありますね。

Walterについては、作者さんのブログ記事もご参考。

シンプルなビルドパイプラインツールwalterをリリースしました | Advanced Technology Lab

Travis CIについて

口頭で挙げたもの。

iOSアプリのビルド

はい、忘れてました。CloudBeesでもMac OS XのSlaveを選択できるので、iOSアプリのビルドできるはずです。少し前にPriceとか変更になったのですが、以前は有料プランでのみ選択可能だったはず。

【12/18追記】CircleCIもiOSビルドに対応したと発表されました。 see: Announcing the Launch of iOS and Android Support on CircleCI | The Circle Blog

iOSMac OS Xアプリのビルドは、Mac OS Xで実行する必要があります。JenkinsをLinux上に立てている場合はSlaveとしてMacを接続し、そこでビルドが走るようにする必要があります。

上記のTravis CIなどは、Mac OS Xの動作するWorkerを備えています。このような形でMac OS Xを使うのはAppleと別途契約しているという話だったはず。

iOSアプリのビルド自動化については、こちらがおすすめです(自薦)。

iOSアプリ テスト自動化入門

iOSアプリ テスト自動化入門

*1:中だるみについては早々に諦めた

日本Androidの会 VR部 #JAGVR 第1回勉強会に行ってきました

にわかに設立された日本Androidの会 VR部の第一回勉強会に行ってきました。

f:id:nowsprinting:20141116115552p:plain

ざっくりと二部構成で、発表と、班分けしてのVRデバイス体験&もくもく会

発表

ポイントだけ。細かくはそれぞれのスライド参照。

VR部について+AR・VRデバイスと開発環境の紹介(@youten_redo)

AR/VRコンテンツの作り方 - Google スライド

VR部の設立目的として、

  • みんなで(顔を合わせて)あそぶ
  • みんなで(はげましあってモチベを維持して)つくる

が挙げられていました。VRコンテンツとかUnityとか後発組の我々に取ってはありがたい限りです。

スライドの「VRコンテンツ要素の分類」には、Cardboard系のMHDや、ハコスコ(単眼ハコスコ)で体験できるVRコンテンツの違いが分類されていて、営業などでお客様に「まずここから」説明するときに使えそうな感じ。

質疑応答で用語の使い分けについて聞いたこと。

また、12/21(sun)に東海大学 高輪キャンパスで開催されるABC 2014 Winterに「VR部屋」が作られるので、デバイス・コンテンツの持ち寄り、また発表など企画しましょう、とのこと。

ABC 2014 Winter | Android Bazaar and Conference 2014 Winter

Google Cardboard を100円ショップの 材料で作ってみた(@ohwada)

20140820 Google Cardboard in Wearable Meeting

Cardboard本体をレーザーカッターで切り、レンズなどを100円ショップで調達する話。今はタオバイザー、ハコスコ版Cardboard、ハコスコDXとか完成品を購入できるものが出てきましたが、自作派も楽しそう。

自作といえば@kakocomさんが本体から自作なものを持って来られていて、見せていただきました。

VRにおける入力デバイス(@nowsprinting)

自分の発表。ざっと調べた、VR世界への入力方法(デバイスに限らず)の紹介。画像の選定にとても時間を食いました。

しかし、歯ブラシコントローラの素晴らしさを伝えきれていないような気がして、大変心残りです。どこかのVRイベントで見かけたらぜひ体験するべきです。

VRの薄い本について(@l1cml @syyama_net)

今朝見たらアカウントが変わっていた @syyama_net さん。VRの薄い本をRe:VIEWで書くので、執筆したい人募集!のこと。

透過型HMD Google Glass の紹介(@teshi04)

てしさんの人生初LT。最近影の薄い感じのあるGoogle Glassについて、できること、アプリストア、野良アプリなどの紹介。Glass愛を感じました。

普通にGlassをかけて街を歩ける世の中が来て欲しいというのは同感。常々「カメラ無くていいから」と思ってます。

VRデバイス体験&もくもく&雑談

3ブロックに分かれて、持ち寄ったデバイスの体験とか雑談とか。本当はジェスチャー入力系のAssetをアレコレしようと目論んでいましたが、持ち込んだマシンでは色々足りなくて断念。

気になっていたRICOH THETA m15とかステレオカメラ(3Dカメラ)とかの知見を得られたのでよかった。

また、懇親会では小さい方のテーブルでメインフレームの話とかVRの没入感についての話とかもして楽しかった。

まとめ

Androidか何かの下地があって、VRに入る取っ掛かりとしてはいい感じの勉強会だったと思います。 最近では「サマーレッスン」開発の話として「VRの一般化」ということが語られていて、そうなると開発者としても営業としても、人に説明/説得する、その手段として体験してもらう、ということは意識していかないと。

そして、次回開催より先にABC 2014 Winterが来ると思うので、何かしら協力したい。

ABC 2014 Winter | Android Bazaar and Conference 2014 Winter

Cardboardでもユニティちゃんを表示させてみた

Oculus Rift DK2で表示させたユニティちゃんを、できるだけそのままCardboardTaoVisor立体視してみます。

開発環境などは先のエントリを参照。

Durovis Dive Plugin

Durovis DiveというCardboardクローン的プロダクトがあり、そこでiOS/Android向けに簡単に立体視と視点のトラッキングを実現できるUnityプラグインが配布されています。

以下の手順で適用します。

  1. Dive SDKのページからプラグインをダウンロード(執筆時点のバージョンは2.0 r498)
  2. Assets -> Import Package -> Custom package...でインポート
  3. Project ViewのAssets/Dive/Dive_CameraをHierarchy Viewにドラッグアンドドロップ。従来のカメラを無効化(Inspectorでチェックをoff)
  4. Dive_CameraのTransformを適切に設定(Oculus Riftのときと同じ)
  5. スプラッシュスクリーンの表示が必須とのことなので、AssetsにあるDive-Splashscreen.unityをビルド対象に追加(File -> Build Settings...の"Scenes In Build"に追加)

f:id:nowsprinting:20141103153246p:plain

Oculus Riftに比べて視野角が狭いので、外周の歪みも無い(無くていい)みたい。

Androidデバイス向けにビルドする

Androidデバイス向けのビルドを行ないます。

なお、動作を試すだけなら、Unity Remote 4が使えるはずなのですが、Android版とUnity 4.5.5では同期できませんでした。

ビルドの設定

  1. File -> Build Settings... を開き、Platformの"Android"を選択します
    • ここで"Google Android Project"をonにすると、apkでなくAndroidプロジェクト(Ant版)が出力されます
  2. Build Settingsの[Player Settings...]ボタンをクリックするとInspectorの表示が切り替わり、ここでアイコンなどを設定できます
    • "Other Settings"に、"Bundle Identifier"、"Bundle Version"および"Bundle Version Code"の設定があります。"Bundle Identifier"の設定は必須です
    • "Publishing Settings"に、apkに、署名するためのキーストアの設定があります。デバッグ証明書を使う場合はキーストア未選択のままでいいようです
  3. Unity -> Preferences... -> External Tools の"Android SDK Location"を設定(選択)します
  4. これで[Build]もしくは[Build and Run]ボタンでビルドできます(出力ファイルパスを聞かれます)

コマンドラインでビルド

AndroidアプリはUnityコマンドで直接ビルドできないため、Unityプロジェクト内にビルドを実行するスタティックメソッドを作成し、それをコマンドラインから呼び出します。

ビルドメソッドの作成

まず、スクリプトの格納フォルダを作ります。Assets -> Create -> Folderでフォルダを生成。名前をEditorに変更します(namespace UnityEditorをエラーにしないために必須)。

続いて、Project ViewのEditorフォルダを右クリック -> Create -> C# Scriptでスクリプトを生成します。名前は任意ですが、クラス名と一致させる必要があるのでここではBuilder.csとしました。

using UnityEngine;
using UnityEditor;

public class Builder {

    static void BuildAndroid(){
        string[] scenes = {
            "Assets/Dive/Dive-Splashscreen.unity",
            "Assets/UnityChan/Scenes/UnityChan_hw.unity"};

        string msg = BuildPipeline.BuildPlayer(
            scenes,
            "Build/UnityChanVR.apk",
            BuildTarget.Android,
            BuildOptions.None);

        if(!string.IsNullOrEmpty(msg)){
            throw new System.Exception(msg);
        }
        Debug.Log("Build completed ");
    }
}

失敗時は例外をスローすることで、Unityコマンドが終了コード1で終了します。

コマンドライン指定

次のコマンドで、BuilderクラスのBuildAndroid()メソッドを実行できます。

$ /Applications/Unity/Unity.app/Contents/MacOS/Unity -quit -batchmode -logFile /dev/stdout \
    -executeMethod Builder.BuildAndroid

なお、

  • Unityは二重起動できないため、コマンド実行時Unityは落としておく必要があります(MonoEditorは起動していてok)
  • 出力ファイルは上書きしてくれますが、必要なディレクトリ(上例ではBuildディレクトリ)はあらかじめ掘っておきます
  • -logFile /dev/stdoutを指定することで、通常コンソールに出力されるログを標準出力に出力できます

iOSデバイス向けにビルドする

iOSデバイス向けのビルド(正確には、iOS向けのXcodeプロジェクトの出力)を行ないます。

なお、動作を試すだけなら、Unity Remote 4を使うと便利です。

OpenDiveSensor.csの修正

そのままiOS向けビルドを行なうと、Assets/Dive/OpenDiveSensor.csでAndroidJavaObjectが見つからないというエラーが出ます。このオブジェクト宣言自体が不要なので*1以下は削除します。

AndroidJavaObject mConfig;
AndroidJavaObject mWindowManager;

Oculusパッケージをプロジェクトから取り除く

OculusUnityIntegration.unitypackageをインポートしている場合、それがシーンで使用されていなくてもビルドエラー*2が出るようです。iOSデバイス向けでは使用しないので、Project ViewのAssets/OVRを削除します。

ビルドの設定

  1. File -> Build Settings... を開き、Platformの"iOS"を選択します
    • "Symlink Unity libraries"は、UnityのライブラリをXcodeプロジェクトにコピーしないため高速に動作します。XcodeプロジェクトだけCIサーバなどでビルドするのでなければonでいいはず
  2. Build Settingsの[Player Settings...]ボタンをクリックするとInspectorの表示が切り替わり、ここでアイコンなどを設定できます
    • "Other Settings"に、"Bundle Identifier"と"Bundle Version"の設定があります
    • "Bundle Identifier"は、Info.plistのCFBundleIdentifierにセットされますが、末尾は${PRODUCT_NAME}に置き換えられます
    • "Bundle Version"は、Info.plistのCFBundleShortVersionStringCFBundleVersionの両方にセットされます
  3. これで[Build]もしくは[Build and Run]ボタンでXcodeプロジェクトを出力できます(出力ファイルパスを聞かれます)

コマンドラインでビルド

iOSアプリ(Xcodeプロジェクト)もUnityコマンドで直接ビルドできないため、Unityプロジェクト内にビルドを実行するスタティックメソッドを作成し、それをコマンドラインから呼び出します。

ビルドメソッドの作成

Android向けに作成したBuilderクラスに、BuildIos()メソッドを追加します。

using UnityEngine;
using UnityEditor;

public class Builder {

    static void BuildAndroid(){
        (snip)
    }

    static void BuildIos(){
        string[] scenes = {
            "Assets/Dive/Dive-Splashscreen.unity",
            "Assets/UnityChan/Scenes/UnityChan_hw.unity"};

        string msg = BuildPipeline.BuildPlayer(
            scenes,
            "Build/UnityChanVRiOS",
            BuildTarget.iPhone,
            BuildOptions.SymlinkLibraries);

        if(!string.IsNullOrEmpty(msg)){
            throw new System.Exception(msg);
        }
        Debug.Log("Build completed ");
    }
}

コマンドライン指定

次のコマンドで実行します。

$ /Applications/Unity/Unity.app/Contents/MacOS/Unity -quit -batchmode -logFile /dev/stdout \
    -executeMethod Builder.BuildIos

Xcodeプロジェクトのビルド

生成されたプロジェクトは、Xcodeでビルドできます(Xcode 6.0.1で確認)。 Xcodeプロジェクトをコマンドラインでビルドするにはxcodebuildコマンドを利用できます。詳しくは『iOSアプリ テスト自動化入門』のChapter 6などを参照してください。

iOSアプリ テスト自動化入門

iOSアプリ テスト自動化入門

参考

Unity実践技術大全 (GAME DEVELOPER BOOKS)

Unity実践技術大全 (GAME DEVELOPER BOOKS)

ライセンス

ユニティちゃんライセンス

このコンテンツは、『ユニティちゃんライセンス』で提供されています

*1:Android向けに必要な物は"#if UNITY_ANDROID"の中に書かれているので

*2:Cross compilation job Assembly-CSharp.dll failed.

Oculus Riftでユニティちゃんを表示させてみた

にわかに日本Androidの会 VR部が設立されたり、OcuFes DCExpo2014@日本科学未来館でユニティちゃんに歯磨きしてあげたりと盛り上がってきたので、すっかり積みデバイス化していたOculus Rift DK2を開封して触ってみるなどしました。

開発環境

Oculus Rift DK2では75fpsを下回ると快適な体験を得られないとされています。MBPのGeForce GT 650Mではやや非力で、SDKに入っているデモアプリで60fpsくらい。でも開発には支障はないかな*1、というレベルです。

ユニティちゃんを表示する

このページを参考に。でも眺めるだけなので、操作はしない。

せっかくなのでハロウィン仕様で。

f:id:nowsprinting:20141103075837p:plain

Oculus Riftで表示する

このリポジトリのREADME.mdに書いてある手順を参考に、カメラをOVRCameraControllerもしくはOVRPlayerControllerに切り替えるだけ。前者はカメラだけなので位置も方向も固定。後者は頭を動かすとカメラも動きます。

ちなみに、.gitignoreもこのプロジェクトのものを流用しました。

f:id:nowsprinting:20141103075904p:plain

  • カメラは近め・低めくらいがちょうどいいかも。脚がちょうど視界に収まるカメラで、このスクリーンショットの感じ。
  • カメラの調整はHerarchy Viewでカメラを選択した状態でSceneエディタで動かして、GameObject -> Align With View でカメラのTransformを更新できる
  • 左右に表示されていたポーズ変更のウィンドウは、Herarchy Viewでユニティちゃんを選択 -> InspectorでIdle ChangeFace Updateのそれぞれ左のチェックを外すと消えます

プレイヤーをビルドする

プレイヤー(再生アプリケーション)をビルドします。

ビルドの設定

  1. File -> Build Settings... を開き、Scenes In Build右下の[Add Current]ボタンをクリックして、ビルド対象シーンを追加します
  2. Build Settingsの[Player Settings...]ボタンをクリックするとInspectorの表示が切り替わり、ここでアイコンなどを設定できます
  3. Oculus Rift向けの Player Settings -> Resolution and Presentation設定、および Edit -> Project Settings -> Quality設定*2が、OculusUnityIntegrationGuide.pdfに載っていますので適用します。
  4. これで[Build]もしくは[Build and Run]ボタンでビルドできます(出力ファイルパスを聞かれます)

ビルドの後続処理

Mac OS Xの場合、Assets/Editor/PostprocessBuildPlayer(拡張子なし)というスクリプトを置いておくと、ビルド処理の後でこれを実行してくれるそうです。 アーカイブやデプロイなど、またiOSアプリの場合はUnityが出力したXcodeプロジェクトをビルドする必要もあるので、これを利用できます。

ただ、ビルド自体をコマンドラインで(そしてCIで)実行するのであれば、これは利用せずにビルドツール側で書いたほうがいいでしょう。

コマンドラインでビルド

毎回GUI操作はめんどうなので、コマンドラインからビルドできます。

Mac OS X向けビルド

下記コマンドでビルドできます(.appファイルが生成される)

$ /Applications/Unity/Unity.app/Contents/MacOS/Unity -quit -batchmode -logFile /dev/stdout \
    -buildOSXPlayer ./Build/UnityChanVR.app

Windows向けビルド

Mac OS Xからでも下記コマンドでビルドできます。.exeファイルのほかに UnityChanVR_Data/というディレクトリができるので、両方デプロイすること。

$ /Applications/Unity/Unity.app/Contents/MacOS/Unity -quit -batchmode -logFile /dev/stdout \
    -buildWindowsPlayer ./Build/UnityChanVR.exe

なお、

  • Unityは二重起動できないため、コマンド実行時Unityは落としておく必要があります
  • 出力ファイルは上書きしてくれますが、必要なディレクトリ(上例ではBuildディレクトリ)はあらかじめ掘っておきます
  • -logFile /dev/stdoutを指定することで、通常コンソールに出力されるログを標準出力に出力できます
  • -buildXxxPlayerには他に、64bit, Universal, Linux, WebPlayerなどがあります。しかしiOS/Androidの指定は無く、-executeMethodでプロジェクト内のビルドスクリプトを実行する(スクリプトBuildPipeline.BuildPlayerでビルドする)形になります。

コマンドライン引数について詳しくは、本家のオンラインマニュアルを参照。

参考

Unity実践技術大全 (GAME DEVELOPER BOOKS)

Unity実践技術大全 (GAME DEVELOPER BOOKS)

モデリングなどの外部ツールiOS/Androidへのエクスポート、Asset Storeで入手できるオススメAssetなども紹介されています

Unityゲーム開発 オンライン3Dアクションゲームの作り方

Unityゲーム開発 オンライン3Dアクションゲームの作り方

エディタの使いかたなど、基本的な部分からページを割かれています

オンラインドキュメント

ライセンス

ユニティちゃんライセンス

このコンテンツは、『ユニティちゃんライセンス』で提供されています

*1:むしろ、この環境で快適なものを作るべき!

*2:SDK0.4以降、アンチエイリアスはoffにしてもキレイになったのでfpsを優先するためoffでいいそうです。see: 楽しく使う Oculus Rift DK2 「Unity編」シート