5.1さらうどん

@giginetの技術ブログ。ゲーム開発、iOS開発、その他いろいろ

cocos2d-x 3.0でクロスプラットフォームなインディーゲームを開発した話

『Wave Weaver』リリースしました!

シンプル操作で脅威の中毒性。波をよけ続けるゲーム『Wave Weaver』をリリースしました!

詳しくは以下の記事とトレーラーをチェック!

波をかわして曲を紡ぐ、カジュアル中毒ゲー『Wave Weaver』をリリースしました - 5.1さらうどん

Google PlayApp Storeで大好評配布中です!非常に多くの方に遊んで頂けているようで驚いています。

for iPhone / iPad

https://itunes.apple.com/jp/app/waveweaver/id841280819?at=10l8JW&ct=hatenablog

iTunes の App Store で配信中の iPhone、iPod touch、iPad 用 WaveWeaver

for Android

Android app on Google Play

Wave Weaver - Android Apps on Google Play

この記事は

この記事はWave Weaverの開発話を交えて、cocos2d-xを使ったクロスプラットフォームなゲーム開発の知見をお伝えするつもりです。

Wave Weaverは、cocos2d-x 3.0のβ版を使用しており、国内では最速レベルのプロダクトだと思われます。そのため、今後使う人に使用感など含め、お伝えしていきたいと思います。

Global Game Jam Sapporo

f:id:gigi-net:20140323102644j:plain

このゲームのプロトタイプは、48時間でゲームを開発するハッカソン、GlobalGameJamの中で作られています。48時間で基本的なプロトタイプはできあがっていた!

詳しくは、gihyo.jpに僕が寄稿したイベントレポートをご参照ください。

ソーシャルゲーム,ボードゲーム,ファミコンまで!? Global Game Jam 2014参加レポート:レポート|gihyo.jp … 技術評論社

cocos2d-x 3.0を使ってみた

最終的に、本作ではcocos2d-x 3.0beta2を使っています。

現在ではRC版が出ているのですが、GGJ当時(1月末)の最新版はβ1でした。

lambda超便利

3.0最大の恩恵は、C++11で実装されたlambdaに完全対応しているところ!

特に、今作ではエンディングなど、逐次的な処理があったので、大体はAction + lambdaで、cocos2dのBlocksライクにかなりお手軽に記述できました。今まではわざわざコールバックを定義して、なんてことをやっていたので、非常に利便性が上がっています。

反面、lambdaを使いすぎると、コードの可読性が落ちて大変でした。特にGGJでは尚更。

さらにC++のlambda-captureと、cocos2d-xのautoreleaseが相性最悪な感じで、captureしたと思ったら、autoreleaseされててぬるぽ、みたいなことがよく起きたのもハマりどころでした

クロスプラットフォーム化が楽

3.0からプロジェクトを生成した時点で、全てのプラットフォーム用のテンプレートが生成されるので、クロスプラットフォーム化がかなり楽でした。ありがたい変更。

alpha, beta, rcで仕様が変わりまくった

まだリリース前なので、仕様がコロコロ変わって大変でした。

以前、alpha版を基準にスライドを作ったのですが、それからGGJで利用したbeta1は、ArrayやDictionaryが廃止されていたりと、結構大きな変更が。

cocos2d-x 3.0 + C++11で始めるゲーム開発超入門

最近リリースされた3.0のrc版ではプロジェクトの生成方法が変わっていたりして、あんまり原形を留めていない感じです。

開発スピードが速すぎて仕様が安定しなさすぎるので、正式リリース前のフレームワークを使うのは危険な感じ。

クロスプラットフォーム + Universal対応

今回はゲーム自体がシンプルだったのもあって、プラットフォーム別に4種類画面を作りました。

f:id:gigi-net:20140323104935p:plain

左からiPhone(4.0インチ)、 iPhone(3.5インチ)、iPadAndroid

iPhone / iPad

iPhone開発はノウハウがあったので、いつも通りこちらを基準に作りました。

iPhoneRetinaとNon Retinaの画像を用意する必要があるのですが、Retina版を基準に作り、Pythonで簡単なスクリプトを書いて、Non Retina用に自動変換をしていました。PIL便利。

4インチ対応は背景画像だけ別に用意して、あとは同じリソースを画面サイズから相対位置で配置するように切り替え。

iPad対応もほぼ同様。iPad版は実は実装上はiPhone版より画面が狭いです。反面、非常にボタンが押しやすくなっていて割と別ゲーに。

Retina iPad用にはSplash画像を別途用意しただけで特に何もしていません。意外と見た目は気にならない感じ。

Android

Android対応は初だったので、動けばいいや的な感じで、あまり機種ごとの最適化は行わずに、一律iPhone3.5インチのNon Retina版と同じ表示になるように作りました。Androidはこの辺が大変ですね。

ビルドにはGradleを使用。NDKの対応にはこちらの記事が大変参考になりました。

gradleでCocos2d-x3.0alpha1のアプリをbuildしてAndroidのapkを作成する - きょこみのーと

古い機種だと重たいという話も聞こえており、今後の課題ですね。

ちなみに、開発中は幻のOUYA版が存在してましたが、コントローラーへの対応が面倒だったので、リリースは見送りました。

Jenkins + TestFlight/DeployGateで高速テストプレイ

ゲームの質は、開発者が如何に遊びまくったかが全てなので、テストプレイ超重要です。

今作では、Jenkinsを用いて、コミットと同時に、各プラットフォーム向けに自動ビルドをしておりました。

f:id:gigi-net:20140323105404p:plain

コミッタはiPhone 4.0インチ環境で開発するだけで、あとはチームメンバーが勝手に確認したりテストしたりしてくれるので、非常にスピード感がありました。

今回のようなクロスプラットフォームの開発では、いちいち全機種ビルドして確認してられないので、CIは必須な感じがしますね。

gitは共通言語

f:id:gigi-net:20140323110020p:plain

ゲーム開発はプログラマの仕事が増えると大変なので、gitを共通言語にして、皆がリポジトリにコミットできる環境を作るべきだと感じました。

例えばデザイナーに「画像が差し替わったのでDropboxに上げましたー」とか言われると、わざわざ持ってきて、ファイル名を変えて、差し替えたのか差し替えてないのかわからずに面倒なことになります。

今回はgitを使えるメンバーが多かったので、その辺が非常に楽でした。プログラマ以外のデザイナーやコンポーザーもgitを使うべき。

辛かったところ

逆に今回の開発環境で上手く行かなかったところについて。

SpriteStudio for cocos2d-xが辛い

f:id:gigi-net:20140323111825p:plain

今回は演出を少しでもリッチにするために、アニメーション作成にSpriteStudioというソフトウェアを使用していました。

SpriteStudioはGGJのスポンサーも努めており、インディー開発者向けに、プロユースの物と同等の機能を持ったモノが無償で提供されています。ありがたい!

「超汎用」データ制作 あらゆるゲーム開発シーンに対応。2Dスプライトアニメーションデータ作成ツール「OPTPiX SpriteStudio」 | ウェブテクノロジ

しかも公式でもcocos2d-xをサポートしていたため、今回導入してみたのですが、標準で用意されてるものを再実装していたり、インターフェイスが独特だったりと、ライブラリのできがあまりよろしくなくて大変でした。

補助ツール・ライブラリ群・サンプルのダウンロード | OPTPiX Help Center

リリース前だったので、当然3.0には対応しておらず、動くように書き直したのですが、アルファブレンディングなどがちゃんと動かずに、そのまま使えない自体が多発。

結局、SpriteStudioで作ってもらったアニメーションを目コピーして、コードで実装したりしていました。

さらに、現在Mavericksで動かないのも辛いところ。わざわざVM立ち上げて作業してました。

ライブラリも1000行ぐらいあって、読み解いたり修正が大変な感じだったので泣く泣くそのまま使用。今後とも使っていくのであれば、しっかりと書き直したい感じですね。

とはいえ、今回のように趣味で開発する場合は、SpriteStudioは無償で使えるという点でかなり魅力的。

お金があるのであれば、標準でサポートしているSpineなどを買うのが良いと思いますが・・・・・・。

CocosDenshion::SimpleAudioEngineが辛い

cocos2d-xの音周りは、SimpleAudioEngineってのがついているんですが、必要最低限の機能しかついていない上に、なぜか実装がいろいろと酷い

などなど。

そのため、ゲーム途中でアプリをバックグラウンドに送ったときの音周りの挙動などに苦労しました。

前作の『VOXCHRONICLE』はサウンドを動的に操作する必要があったので、自前でOpenALガリガリ書いていたため、音周りの混乱はありませんでした。iOS版のみのリリースであったことも幸いして、クロスプラットフォームを意識せずに設計できたのも楽な点。

もう少し時間があればガッチリと手を入れていきたいところ。結構基本的な機能も揃っていないので、割とみんな困ってると思うんですけど、みなさまどうしてるんでしょうね。何かご存じでしたら教えてください。

ビルド時間が長すぎて辛い

cocos2d-x 3.0は2.xに比べて、かなりビルド時間が長くなっているように感じました。

特にiOS版はObjective-C++を使っている関係で、ビルドがタダでさえ遅いのに加え、本プロジェクトではboostを使っていたため、さらにビルド時間が長くなりました。

特にMacBook Airでは、クリーンビルドだと5分以上待たされることもザラ。48時間しかないGGJでは致命的な感じでした。

レベルデザイン

『Super Hexagon』を意識しまくった

f:id:gigi-net:20140323110707p:plain

このゲームは、『Super Hexagon』というインディーゲームに強くインスパイアされています。

『Super Hexagon』の中毒性を踏襲すべく、以下のような仕組みでレベルデザインを行いました。

  • 波の排出や、数秒間待機するなどの命令をOrderと呼ぶ
  • 各Orderには排出開始位置や、波の色、Easingなどが設定されている
  • 一連のOrderのまとまりをPatternと呼び、レベル毎にPatternを何種類か定義し、ランダムに出現順を変えている

このように、毎回適当に波を出すのではなく、ある程度のパターンをデザインし、ランダムで出すような仕様になっています。

これによって、一見突破不可能な波であっても、何度も遊ぶことで記録が伸びていき、中毒性の高いゲームに仕上がっています。

レベルデザインについては様々な声を頂いています。ありがとうございます。

レベルデザインや、波のエミッター周りの実装は、『Super Hexagon』を最高レベルまでやり尽くした[twitter:@tcptr]さんが作り込んでくれたので本当に良いモノができました。

ちなみに、だいたい1時間ぐらいでエンディングを見れるように調整してみたのですが、概ねそんな感じになっているようで何よりです。

タダでもここまでできる!

最近はゲーム開発用の環境もだいたいはOSSで揃うようになってきたので、お金をかけずにここまでの開発が個人でできます。良い時代ですね!

他の人の開発裏話

デザインや音楽などにも、細かなところに工夫が生きています。ご興味のある方は是非。

ゲーム開発したい方は

f:id:gigi-net:20140323111018p:plain

札幌ゲーム製作者コミュニティKawazでは、プロアマ交えて楽しくストイックにインディーゲーム開発をしています。是非ご参加ください!

おまけ

ありがたい

*1:最初はTestFlightを使っていたが、サービス終了に伴い移行した

Tuneup JS+Travis CIによるiPhoneアプリ自動UIテストまとめ

最近、iPhoneアプリの開発に自動UIテストを取り入れてみたので、手に入れた知見を共有してみたいと思います。

この記事について

iOSアプリケーションの自動UIテストを行うためのノウハウについて解説します。

この記事におけるUI自動テストとはiOSシミュレーターや実機を自動で起動し、予め記述していたとおりに操作させ、アプリケーションが問題なく動いているかどうかをテストする手法のことです。

今回はTuneup JSと呼ばれるライブラリを用いて、アプリの自動再生、要素のチェック、画像比較によるテストを行い、最終的にTravis CI上で動かすところまでを書いています。

iOS開発の知識のほか、JavaScript, Rubyを知っていると良いかも知れません。

ここで紹介するもののいくつかはRuby製であり、RubyGems, Bundler, Rakeなど、最低限のユーティリティが動く・使える環境を想定しています。

また、Unit Testingについては、下記の資料が素晴らしく良くできているのでご参照ください。

Cocoa 勉強会関西で Unit Test について話しました - cockscomblog?

TuneupJSって?

http://www.tuneupjs.org/

Apple謹製のUITesting FrameworkにUIAutomationというものがあります。
UIAutomationでは、テストケースをJavaScriptで記述できます。

大変良くできているのですが、単体では利用しづらい部分が多いです。それを補うユーティリティ集がTuneupJSです。

例えば、UIAutomation単体で行うのは面倒だったAssertionなどのテストの記述が、より他のBDDフレームワークライクに記述できるようになります。

ここでTuneupJSを利用した場合と、していない場合のコードを比較してみます。

before
UIALogger.logStart("Checking the navigation bar right button title");
var target = UIATarget.localTarget();
var title = target.frontMostApp().navigationBar().rightButton().name();
if (title != "Books") {
  UIALogger.logError("Expected 'Books' but was '" + title + "'");
  UIALogger.logFail("Some tests failed"); // failed
} else {
  UIALogger.logPass("Tests passed"); // passed
}
after
test('Checking the navigation bar right button title', function(target, app) {
  assertEquals(target.frontMostApp().navigationBar().rightButton().name(), "Books");
});

例えば、上記のコードは、ナビゲーションバーの右側のボタンのタイトルをテストするテストケースです。

このように記述量に大きく差が出ます。

TuneupJSの導入

TuneupJSはCocoaPodsで導入可能ですが、バージョンが古いため、submoduleで入れた方が良いと思います。

git submodule add https://github.com/alexvollmer/tuneup_js.git

TuneupJS+UIAutomationでテストを書く

TuneupJSは、UIAutomationを拡張するものであるため、基本的なテストの記述はXcodeに付属しているInstruments上で行えます。

Instruments

Instrumentsを使うと、iOSシミュレーターの操作から自動的にJavaScriptのコードを生成してくれます。

あなたのiOSプロジェクトをProfileビルドしましょう。Instrumentsが起動するのでAutomationメニューを開くと、以下のような画面が立ち上がります。

f:id:gigi-net:20130918002257j:plain

左カラムからJavaScriptを新規作成し、画面下の録画ボタンを押してiOSシミュレーターを動かしましょう。自動的にJavaScriptが生成されます。

f:id:gigi-net:20130917214444j:plain

基本的にこれをコピペするだけで、動作を自動再生することができるのですが、実際にはアニメーションのタイミングなどでdelayが必要です。

target.frontMostApp().navigationBar().rightButton().tap();// トランジションが入るから
target.delay(1.0); // 1秒待っておく
assertEquals(target.frontMostApp().mainWindow().staticTexts()[0].label(), "foobar");
 // そうしないと表示されるまえに評価されてテストが落ちたりすることがある

サンプルコード

例えば、ここでは、ボタンを50回連打したときに表示されるラベルが正しいかどうか、というテストを書きました。人手で50回叩くのはムダっぽい感じがするので、こういうことは自動化しましょう。

#import "../../tuneup_js/tuneup.js"
test('Tap button fifty times', function(target, app) {
  target.frontMostApp().mainWindow().buttons()[0].tapWithOptions({tapCount:50});
  target.delay(1.0);
  assertEquals(target.frontMostApp().mainWindow().textViews()[0].value(), "Pressed 50 times");
});

テストを走らせる

TuneupJSにはテスト実行用の簡易なスクリプトがついています。今回はこのスクリプトを利用してテストを走らせてみましょう。

テストの実行には

  • xcodebuildを用いてアプリケーションをビルドする
  • TuneupJS付属のスクリプトを走らせる

と言った手順が必要です。今回は簡易なRakefileを実装し、UIテストを行う一連の流れをタスク化してみました。

以下、Rakefileの例を示します。


gist6595415

細かい説明は省略しますが、要はテストを実行するには

tuneup_js/test_runner/run <実行ファイル.app> <Spec.js> <出力用ディレクトリ>

を呼び出してあげればOKです。詳しくは以下のドキュメントをご参照下さい。

http://www.tuneupjs.org/running.html


上手く行くと、下記動画のように自動的にシミュレーターが起動し、記述したとおりの動作が実行されるはずです。

http://gifzo.net/2wbLx1aLwL.gif
http://gifzo.net/GLdCEQz880.gif

Screenshot Assertion

TuneupJSの特徴の一つにScreenshot Assertionが挙げられます。Screenshot Assertionは、予め想定されるiOSスクリーンショットを用意しておき、実行時に自動的に撮影されたスクリーンショットと比較して、画像間の差異が一定以下であればテストを通す仕組みです。

これを使うことで、画像1枚用意するだけで、表示崩れなどをおおざっぱに判定することができます。

仕組みとしては、内部でImageMagickに付属しているcompareを利用しており、その出力の値によって判定しています。
そのため、実行にはImageMagickの導入が必要です。

brew install imagemagick
Pull Requestしました

僕が使い始めた時点でのTuneupJSは、ImageAssertionの画像比較結果の許容値が固定値であり、ほぼ同じ表示であるにも関わらず、テストが通らないケースが続出しました。(例えばカーソルの点滅やインジケーターのアニメーションの差異などで)

そのため、Pull Requestを送り、テストケース毎に画像の誤差の許容値を指定できるようにしました。
これは既にmasterにmergeされており、現在のバージョンのTuneupJSで利用可能です。

Add argument for assertScreenMatchesImageNamed to set diff threshold. by giginet · Pull Request #57 · vkolgi/tuneup_js · GitHub

テストケースの記述

まず、テスト用の正解画像を用意します。このとき、ステータスバー上20px分を切り取ったスクリーンショットである必要があります。

f:id:gigi-net:20130917235511p:plain

次にテストケースを書きます。こちらなどを参照に書くといいです。
http://www.tuneupjs.org/assertions.html

  createImageAsserter('../../tuneup_js',  './',  '../../specs/image');
 // ImageAsserterの初期化
// 何らかの操作を行う
  assertScreenMatchesImageNamed("tap_text_view", "a screenshot of text view is not matched.", 1500); // 比較

createImageAsserterの引数のパスはinstruments実行時に生成されるRunディレクトリからの相対パスを書くようです。

また、assertScreenMatchesImageNamedの第3引数は、僕の送ったPull Requestから追加された引数で、画像比較の差異を指定することができます。デフォルトの値(引数を渡さない場合)は1になっているようです。

閾値はとりあえずデフォルト値に設定しておいて、テストが落ちた場合は

compare --metrics MAE <教師画像> <テストが落ちたときのスクリーンショット画像> diff.png

と実行し、手前の数字を参考に決めると良いです。

Travis CIで動かす

手元の環境で動くようになったので、今風っぽくCIしたいですね。ご自宅のJenkins氏で走らせるようにしてもよいですが、なんと嬉しいことに、全く同じテストが全てTravis CI上で動きます。

実はTravis CIには、Mac workerが存在し、Objective-Cのテストが行えます。

xcodebuildでiOSアプリのビルドができるのはもちろん、特に設定せずともHomebrewが利用できるほか、今回紹介したシミュレーターの起動さえもTravis CI上で行えます。

.travis.ymlの設定方法

Travis CIでプロジェクトをCIするためには、リポジトリルートに.travis.ymlというYAMLを配置しておく必要があります。

このように書くと上記のテストと同じモノがTravis CIで走ると思います。

language: objective-c

before_script:
  - brew update
  - brew install imagemagick

script:
  - rake

before_scriptでImageMagickのセットアップを行っており、スクリーンショットの比較もTravis CI上で行えます。

Travis CIの環境では、テストの実行毎にクリーンな状態にロールバックされるため、テストの実行の度にImageMagickをビルドしていることになります。実に富豪的

UIテストは通常の操作と同じように行うため、非常に時間がかかるのですが、20~30分は動かしても怒られないと聞いたことがあるため、問題なくテストが実行できます。

f:id:gigi-net:20130918000129j:plain

Travis CI - Test and Deploy Your Code with Confidence

これだけ負荷をかけても無料で実行可能。良い時代ですね。

ここでは特に解説しませんが、TestFlight APIなどを使って、テストが通ったら自動的にTestFlightを飛ばすようにしてもおもしろそうです。

サンプルプロジェクト

今回、紹介した物は以下のリポジトリに大体は含まれているので、ご参照ください。

GitHub - giginet/AutomationTestDemo: This is a demo project for ui testing with TuneupJS

その他便利ユーティリティ

bwoken

GitHub - bendyworks/bwoken: iOS UIAutomation Test Runner

UIAutomation用テストランナー。導入するとRakeを書いたりせずに、コマンド一発でテストが走ってお便利。

TuneupJSのドキュメントではこれの利用が推奨されている。
さらに、通常のUIAutomationに加えて、以下のような機能がサポートされている。

  • 元々Rakeのタスクが定義されていてrakeコマンド一発でテストが走る
  • テストケースがCoffeeScriptで記述でき、テスト時に自動コンパイルしてくれる
  • 外部ライブラリをgithubから直接インポートできる

こう聞くと、かなり良い感じのライブラリに見えるけど、これ単体では微妙な部分が多い。

確かに普通に自動テストを走らせる分にはTuneupJS付属のスクリプトより相当楽だけど、カスタマイズ性が低く使い勝手が悪いのが残念。例えば、projectの代わりにworkspaceを使ったり、schemaやtargetを変えたり、シミュレータを変えたりといったことが内部に手を加えないとできない

それと、テストケースをCoffeeScriptで書くのも微妙な感じです。確かに嬉しいけど、前述の通りInstrumentsがJSのコードを自動生成してくれるので、書き直すのもなんだかかっこ悪い。

さらに1年近く保守されていないのもマイナスポイント。だれかforkして直すと良いと思います。

Nocilla

GitHub - luisobo/Nocilla: Testing HTTP requests has never been easier. Nocilla: Stunning HTTP stubbing for iOS and Mac OS X.

Objective-C製のstubbingライブラリ。例えば「Twitterクライアントを作りたいけどテストしにくい!」なんて時に、表示部分だけであれば、Requestをstubして、ダミーのデータを返してやればテストしやすいです。

ただ、若干機能面で使い勝手がよろしくないですが、これ以上に優れたものも見つからなかったので、上手い感じで使えば良いと思います。

ios-sim

GitHub - ios-control/ios-sim: Command-line application launcher for the iOS Simulator

iOSシミュレーター用のCLI。これでシミュレーターをコマンドライン上でを起動、制御できる。

TuneupJSには現状、シミュレーターを制御する機構がないので、例えば4インチ環境でテストしたい、なんて時に、これでシミュレーターを切り替えてやる必要があります。

Homebrewで入って導入が簡単なのも嬉しい。

brew install ios-sim
mechanic

GitHub - jaykz52/mechanic: A CSS-style selector engine for iOS UIAutomation

UIViewをCSSセレクタ風に取得できるようにするライブラリ。TuneupJSなどと一緒に使えます。

正直、Viewの構造がわかりにくいし、Instrumentsを使ってGUIで選択した方が早い気がするので使ってません。良さそうだったら教えて下さい。

その他のTesting Framework

本稿で紹介したUIAutomationの他にも、iOSのUIテストフレームワークには、Objective-Cで記述できるKIFや、Cucumberで記述できるcalabash-iosなど、様々なUI Testing Frameworkがあります。

GitHub - kif-framework/KIF: Keep It Functional - An iOS Functional Testing Framework
GitHub - calabash/calabash-ios: Calabash for iOS

KIFは最近のバージョンでさらに良くなったみたいですね。あんまり使ってないのでよくわかりません。

calabash-iosは試してみたのですが、CucumberのSpecをDSLみたいなので書かないといけないので微妙に使い勝手が良くないです。
せっかくCucumberを使っているので、calabash-iosでFeatureは日本語を使って書けないか試してみたんですけど、現時点でi18nには対応していないそうです。

まとめ

手間がかかるので、ここまで厳密にテストすることはないと思うのですが、Travis CI上で動き出すとなかなか嬉しいですね。

アプリの開発以外にも、例えばCocoaControlsにUIコンポーネントを提供するときも、Travis CIのバッヂがリポジトリ上に表示されているとカッコイイ感じです。

スクリーンショットによるアサーションは凄そうな技術に見えるけど、あんまり期待しない方が良くて、ざっくりとした表示の確認にのみ使い、挙動などは要素をチェックする二段構えでテストした方が良さそう。

手前味噌ですが、iOSのUIテストの手法がこれだけまとまってる記事は日本語はもちろん、世界中探しても皆無だったので、書いて良かったと思います(小並感)。少しでも多くのiOSデベロッパーのお役に立てば幸いです。
がんばって書いたので、気に入ったらはてブ+シェアをしてくれたら嬉しいです。

機会があれば、同内容を札幌のiPhone開発コミュニティであるdevsapでお話しできたら良いなあと考えています。

チラシの裏

9月21日は僕の誕生日です。23歳になります。

Amazon.co.jp

iPhoneゲームでインタラクティブミュージックに挑戦してみた話

先日、『VOXCHRONICLE -オクスクロニクル-』というiPhoneゲームを公開しました!
AppStoreにて完全無料で配布中です。(広告も一切ありません!)

f:id:gigi-net:20130512145921p:plain

VOXCHRONICLE - gigi-net.net

音を奏でる奥スクロールRPG『VOXCHRONICLE -オクスクロニクル-』リリースしました - 5.1さらうどん

f:id:gigi-net:20130512161149p:plain

このゲームは、音楽の動的生成をウリにしていて、他のゲームにはない尖った作品に仕上がっています。

このような音楽の自動生成は最近のゲーム業界では「インタラクティブミュージック」と呼ばれています。最近の事例としては、昨年のCEDECで紹介された『ファンタシースターオンライン2』のBGMのプロシージャル生成の例が記憶に新しいです。

4Gamer.net ― [CEDEC 2012]PSO2の「途切れないBGM」はこうやってできている。セガが語る「BGMのプロシージャル生成」

この記事では、『VOXCHRONICLE』を一例に、iPhoneゲームでのインタラクティブミュージックへの挑戦をご紹介したいと思います。

インタラクティブミュージックとは

インタラクティブミュージック(IM)」とは、狭義では従来、固定された音楽を鳴らしているだけのBGMに過ぎなかった音楽を、プレイヤーの操作により、状況に応じて動的に生成していくような試みのことだと思います。しかし、広義では音楽の動的生成はインタラクティブミュージックの一面にすぎず、音楽とゲームを幅広い形で融合させていこうという試みです。

詳しくは、インタラクティブミュージックについての勉強会などを主催している「インタラクティブミュージック研究会」のページをご覧ください。

InteractiveMusicLaboratory

VISS -オクスインタラクティブサウンドシステム-

概要

『VOXCHRONICLE』では、プレイヤーのコマンド入力によって動的に音楽を生成し、没入感を高めています。このシステムをVISS(ヴァイス)と呼んでおり、今作品のキモとなっております。

まず、以下の動画を見てください。

『VOXCHRONICLE』では、音楽の1小節がゲーム中の1ターンに対応しており、プレイヤーが選択したコマンドにより、次の小節が動的に変化していきます。

VISSは各小節毎に「メイン」「リフ」「ドラム」と呼ばれる3つの音声ファイルを同時に再生することで、様々な状況に応じてリアルタイムに音楽を作り出しています。

ゲームに使われている音声ファイルは、以下のような1小節毎の形になっていて、このような音を規則に沿って3音同時に鳴らしていると考えてください。

http://www.kawaz.org/commons/preview/1607/volca_voxattack0.mp3:sound

また今作ではステージによってガラッと曲調が代わっているのも特徴です。

メイントラック

メイントラックは主人公(オクス・ラスカ)の行動を表現したチャンネルです。

曲のメインメロディに相当します。

プレイヤーの入力したコマンドによって曲が切り替わっていく一番わかりやすい変化です。上記の動画を見て頂ければイメージは掴めるかと思います。

コマンド毎に1小節で終了する技や、連続で使い続けることで4小節が繰り返されるコマンド(剣、杖)があります。

また、イントロやアウトロ、ボス戦QTE時の専用ループなど、ゲームの進行に関わるメロディもメイントラックとして鳴らされます。

リフトラック

リフトラックはモンスターを表現したチャンネルです。

モンスターが固有の音を持っており、今一番近いモンスターが持っている固有音が再生されます。
リフは各音セットに4種類の音があり、出現する各モンスターに割り当てられています。

例えば、以下の動画がリフが一番わかりやすい「遺跡」のステージで一番近くのモンスターを切り替えていった例です。おわかり頂けるでしょうか。
カーソルが載っている敵がマスクと宝箱のモンスターどちらかに注意して音を聞いてみるとわかりやすいかと思います。

マスクのリフ:http://www.kawaz.org/commons/preview/1603/ttn_counter0.mp3:sound
宝箱のリフ:http://www.kawaz.org/commons/preview/1604/ttn_counter1.mp3:sound


また、音量も一番近い敵との距離によって変わります。
ちなみに、ボス戦においてはボスが使用した技によって細かくリフが変化しているのですが、音屋さんとの意思疎通ミスで、非常にリフの変化がわかりにくくなってしまいました。ちょっと心残り。

ドラムトラック

ドラムトラックは場全体を表現したチャンネルです。

場の盛り上がりを何となく推定し、それに応じてリズムを切り替えています。
例えば以下の動画、「草原」のステージがわかりやすい例。プレイヤーが何も操作せずに敵が近づいているだけで、どんどんドラムが盛り上がっていくのがわかると思います。

おそらく、遊んだことのある方はDSの『メテオス』などを思い出すのではないでしょうか。


具体的には、ドラムレベルという0~4の5段階の隠しパラメータがあり、場全体の状況からドラムレベルを推定し、それに応じた5種類のドラム音を切り替えるという処理を行っています。こちらのHPが低かったり、敵が多かったり、パワーが溜められていたり、ピンチの局面にはドラムレベルがどんどん上がっていきます。
(ちなみに、上記のような仕組みを開発内ではDSKY(どすこい・ドラムシステム空気を読む)と呼んでいました)

例として、以下は「深海」ステージのドラムです。

ドラムレベル0:http://www.kawaz.org/commons/preview/1601/negi-3c_drum0.mp3:sound
ドラムレベル4:http://www.kawaz.org/commons/preview/1602/negi-3c_drum4.mp3:sound

ドラムトラックとは便宜上の呼び方で、ステージによってはドラム自体はメイントラックに組み込んでしまい、代わりにパーカッションが使われていたり。例えば「炭鉱」ステージが好例。

上記の5種類の他に、このチャンネルは「インパク」と呼ばれる特殊なドラムを4種類持っています。これは普通の音楽のフィルインに相当します。

このゲームには「パワーチャージ」と呼ばれるキャラクターの強化コマンドが存在しており、チャージしたパワー4段階に応じて、普段のドラムを無視して強制的にインパクトが挿入されます。

例えば、以下の動画のドラムを注意深く聞いて頂くと、パワーの解放タイミングでシンバルなどが鳴っています。

インパクト1:http://www.kawaz.org/commons/preview/1605/volca_impact0.mp3:sound
インパクト4:http://www.kawaz.org/commons/preview/1606/volca_impact1.mp3:sound

これらの音をどうやって繋げているの?

これらの音をどのような仕組みで繋いでいるかは、以下の図解を時系列に沿って見て頂ければご理解頂けるかと。

1. 曲の再生開始=ターンの開始。3音が同時に鳴り始めます。
f:id:gigi-net:20130520171811p:plain

2. 0.5小節が経過。四つ打ち曲なら裏拍のタイミング。モンスターが行動します。
f:id:gigi-net:20130520171816p:plain

3. 0.9小節が経過。この時点でコマンド入力を打ち切り、現在の状況に応じて次のトラックを各チャンネルに挿入します
ここでは何もコマンドが入力されていないので、次のメイントラックは待機音
f:id:gigi-net:20130520171820p:plain

4. 次の小節へ。例えばここでアタック(剣)のコマンドを入力します
f:id:gigi-net:20130520171829p:plain

5. また0.9小節になったタイミングでアタックのトラックを次のトラックに挿入
f:id:gigi-net:20130520171834p:plain

6. アタックコマンド音の1小節目がなり始めます
f:id:gigi-net:20130520171838p:plain


このように、チャンネル毎に待ち行列を持っていて、今の状況に応じて次々と次のトラックをpushしていく作りになっています。

音作りの難しさ

僕は作曲については一切携わっていないので、詳しい解説は曲を作った方々の記事に期待するとして、開発の現場で言われていたことは以下のようなことでした。

1小節しかない行動のメロディ差別化

普通の曲は同じようなフレーズを複数回繰り返して曲を繋げていくのが一般的ですが、このゲームの場合、弓やノックバック、回復など多種多様の行動は1小節で全く異なるメロディを作成しなくてはならず、メロディのバリエーションを差別化するのが非常に困難でした

どのコマンドからでも破綻なく繋がる曲展開

ゲームの仕様上、ほぼ全てのコマンドに遷移してしまうため、どの順番で曲が繋がってもコード進行が破綻しないように作曲する必要がありました。また、溜めの後はスラッシュや魔法に繋がりやすい、といったゲームルールやレベルデザインを完全に理解し、プレイ傾向に応じた曲遷移を考える必要がありました。

オクスとラスカの差別化

今作はオクスとラスカのダブル主人公、キャラチェンジが実装されていて、その二人のコマンドを大きく差別化させるのが難しかったようです。例えば、リズムを変えたり、楽器を変えたり、拍子を変えたりと、各楽曲様々な趣向で二人の行動を差別化させています。

全14曲の圧倒的ボリューム

『VOXCHRONICLE』はプロ含め12人の音屋さんによる14つの曲セットという圧倒的ボリュームを秘めています。

上記の解説のような曲データを全部持つと、各曲セット毎に50小節前後の音声ファイルが必要になります。ゲーム中の全小節数の合計はなんと738小節。この規模感が伝わるでしょうか?

また、曲セット毎にかなり細かく音声の共通化設定を行えるような設計になっています。(例えばオクス・ラスカのドラム音を別々にするか、共通化するかなど)。そのため、最も少ない曲セットで43小節、最大構成で64小節の構成となっています。

ゲーム中で利用している楽曲はgithubにて視聴できますので、ご興味のある方はどうぞ

VOXCHRONICLE/VOXCHRONICLE/Resources/Music at master · giginet/VOXCHRONICLE · GitHub

iPhone上での実現の問題点

iOS上でインタラクティブミュージックを実現するにあたって、一番の問題点はメモリと曲の聞こえ方のトレードオフでした。

最初に、何も考えずに逐次ロードを試してみたところ、シームレスに音楽が繋がっているように聞こえるために、デコードなどの遅延がオーバーヘッドとなり、スムーズに音が繋がらない場合が多々ありました。(特にiPhone4などではまともに遊べなかった)

グラフィックでの数フレームの遅延はなかなか目視できないモノですが、音楽の場合は1フレーム途切れただけでもすぐにわかってしまいます。

そのため、音声データをバッファとしてメモリ上に全て展開しておく方針に転換しました。
こちらはこちらで、ロードを効率よく、かつ適切なタイミングで行わなければ、大きなロード時間が発生したり、メモリリークが発生したりするという新たな問題が発生しました。

そこで、要件としては以下を満たす必要がありました

  1. 小節切り替えに合間ができない
  2. 曲セットの変わるタイミングでロードが入らない
  3. メモリリークが起きない
  4. デコードはなるべく高速化
  5. アプリ容量は極力削減しなくてはならない

『VOXCHRONICLE』の場合は、1ステージ中に同時に使用される可能性のある小節数は多くても60程度、さらにステージ間でステージセレクト→イントロが鳴って操作不可能な時間が存在するので、そのタイミングで今まで使っていたステージの曲セットを解放し、同時にそのステージ中で利用するファイルを全て展開しています。

メモリ上に全て展開することで1の問題、操作不能なタイミングで1小節ずつ非同期ロードを行うことで2の問題は解決しました。

3, 4, 5の問題を解決するためには、音声フォーマットの選別と圧縮が必要でした。

WAVEは音質、デコード速度共に優れていますが、アプリ容量の面から難しく、mp3はループサウンドに不向きという特徴、OGGiPhone上でのデコードが難しいという理由からそれぞれ採用を見送りました。

結局、iPhone上でこのような動的変化をさせるには、CoreAudio一択となります。
具体的にはafconvertでCoreAudioのIM4 Codec、ステレオ22050bpsで固定することで、音質を損なわずにメモリ、容量の削減を行うことに成功しました。

コンシューマ機から見るとまだまだいろいろと甘いんでしょうが、今作はこれでiPhone3GSでもほぼ遅延なく動作できるように実装できたので、及第点ではないでしょうか。

ご興味を持った方は

自分でやっておいて言うのもなんですが、インディーズゲームでは前代未聞の規模感、仕様で実装できたなあと思っています。(おそらく無料の限界!)

もし、インタラクティブミュージックや、ゲーム開発そのものにご興味を持って頂いた方は、インタラクティブミュージック研究会(IM研)や札幌ゲーム制作者コミュニティKawaz、そしてこの『VOXCHRONICLE』をよろしくお願いします!

札幌ゲーム制作者コミュニティKawaz

VOXCHRONICLE for iPhone, iPod touch, and iPad on the iTunes App Store