Blog
X’masが刻一刻と迫っています。祢次金です。
前回、「クリエイティブなC++ライブラリ “Cinder” の紹介」と題して、Cinderをご紹介しました。今回はそのCinderを使って、iPad上で動作するパーティクル生成アプリケーションを実装してみたいと思います。
前回同様、環境はMacOSXを想定し、iOS SDKのバージョンは4.2、iPadのiOSは4.2.1とします。
なお、旧バージョンのiOS SDKの場合、iPad simulatorではFrameworkのリンクに一部失敗しているようで、うまくアプリケーションが起動しませんでした。旧バージョンでビルドしたものでも実機では問題なく動作しますが、実装される場合は最新SDKの利用をおすすめします。
雛形の作成と修正
TinderBoxを使ってプロジェクトの雛形をつくります。名前を “TouchTest” とし、Targetは “Cocoa Touch” を選択します。TouchTestAppクラスが定義されるTouchTestApp.cppという名前のソースが生成されているはずです。前回のエントリではTouchTestAppクラスはAppBasicを継承していましたが、TargetをCocoa Touchにしているので今回はAppCocoaTouchを継承しています。
デフォルトのままではiPhone向けの設定になっているので、ここでプロジェクト設定をいじります。ベースSDKをiOS 4.2、Targeted Device FamilyをiPadに変更しておきましょう。
(Base SDK Missingとなってビルドできない場合はXcodeを再起動してみてください。)
以上の設定で、とりあえずビルドしてSimulatorで実行してみると以下のようになります。
カラフルな立方体が中央でぐるぐる回っています。
Cinderでのタッチイベントの処理
今回実装するアプリでは、タッチした位置にパーティクルを生成させます。マウス押下を拾う場合はmouseDownメソッドをオーバーライドしていましたが、タッチ関連のイベントを拾う場合はtouchesBegan、touchesMoved、touchesEndedというメソッドをオーバーライドします。iOSアプリではおなじみのメソッドですね。指が触れタッチが始まった時にtouchesBegan、タッチが移動した時にtouchesMoved、指が離れタッチが終了した時にtouchesEndedが呼ばれます。それぞれのメソッドにはTouchEventが渡され、タッチの位置やIDを知ることができます。
void TouchTestApp::touchesBegan(TouchEvent event) {} void TouchTestApp::touchesMoved(TouchEvent event) {} void TouchTestApp::touchesEnded(TouchEvent event) {}
また、デフォルトではマルチタッチを処理してくれないので、以下のようにprepareSettingsというメソッドをオーバーライドしてマルチタッチを有効にしておきます。
void TouchTestApp::prepareSettings(Settings *settings) { settings->enableMultiTouch(); }
パーティクル生成アプリ
それではアプリの実装です。複数のタッチに対応し、タッチした位置に色とりどりのパーティクルを発生させます。雛形生成でできたデフォルトの実装はほとんど使いません。まずはTouchTestApp.cppファイルにある、メインとなるTouchTestAppクラスから。
#include <map> #include <vector> #include "cinder/app/AppCocoaTouch.h" #include "cinder/app/Renderer.h" #include "cinder/Rand.h" #include "ParticleSource.h" using namespace std; using namespace ci; using namespace ci::app; class TouchTestApp : public AppCocoaTouch { public: void prepareSettings(Settings* settings); void touchesBegan(TouchEvent event); void touchesMoved(TouchEvent event); void touchesEnded(TouchEvent event); void setup(); void update(); void draw(); map<uint32_t, ParticleSource> particleSources; }; void TouchTestApp::prepareSettings( Settings *settings ) { settings->enableMultiTouch(); } void TouchTestApp::setup() { gl::setMatricesWindow(getWindowWidth(), getWindowHeight()); } void TouchTestApp::touchesBegan(TouchEvent event) { for (vector<TouchEvent::Touch>::const_iterator touchIter = event.getTouches().begin(); touchIter != event.getTouches().end(); touchIter++) { Color color(CM_HSV, Rand::randFloat(), 1.0f, 1.0f); particleSources[touchIter->getId()] = ParticleSource(touchIter->getPos(), color); } } void TouchTestApp::touchesMoved(TouchEvent event) { for (vector<TouchEvent::Touch>::const_iterator touchIter = event.getTouches().begin(); touchIter != event.getTouches().end(); touchIter++) particleSources[touchIter->getId()].setLocation(touchIter->getPos()); } void TouchTestApp::touchesEnded(TouchEvent event) { for (vector<TouchEvent::Touch>::const_iterator touchIter = event.getTouches().begin(); touchIter != event.getTouches().end(); touchIter++) particleSources[touchIter->getId()].close(); } void TouchTestApp::update() { for (map<uint32_t, ParticleSource>::iterator sourceIter = particleSources.begin(); sourceIter != particleSources.end();) { sourceIter->second.update(); if (sourceIter->second.hasParticle()) sourceIter++; else particleSources.erase(sourceIter++); } } void TouchTestApp::draw() { gl::clear(Color::white()); for (map<uint32_t, ParticleSource>::iterator sourceIter = particleSources.begin(); sourceIter != particleSources.end(); sourceIter++) sourceIter->second.draw(); } CINDER_APP_COCOA_TOUCH( TouchTestApp, RendererGl )
タッチ毎にParticleSourceというクラスのインスタンスを生成し、タッチのIDに紐付けて追跡します。タッチが続いている間は、ParticleSourceからParticleが発生し続けます。1つ注意点があり、iOSではOpenGLの座標系がデフォルトでxy方向とも-1.0から1.0の範囲になっているようなので、setupメソッドでスクリーンの大きさに設定し直しています。
続いて、ParticleSourceクラスです。ParticleSource.hとParticleSource.cppを作成し、以下のように記述します。
class ParticleSource { public: ParticleSource(); ParticleSource(ci::Vec2f location, ci::Color color); void setLocation(ci::Vec2f location); void close(); bool hasParticle() const; void update(); void draw(); protected: std::list<Particle> particles; ci::Vec2f location; ci::Color color; bool closed; };
#include "cinder/gl/gl.h" #include "cinder/app/AppCocoaTouch.h" #include "ParticleSource.h" using namespace std; using namespace ci; ParticleSource::ParticleSource() { } ParticleSource::ParticleSource(Vec2f _location, Color _color) { location = _location; color = _color; closed = false; } void ParticleSource::setLocation(Vec2f _location) { location = _location; } void ParticleSource::close() { closed = true; } bool ParticleSource::hasParticle() const { return particles.size() > 0; } void ParticleSource::update() { for (list<Particle>::iterator iter = particles.begin(); iter != particles.end();) { iter->update(); if (iter->isDead()) iter = particles.erase(iter); else iter++; } if (!closed) particles.push_back(Particle(location)); } void ParticleSource::draw() { gl::color(color); for (list<Particle>::iterator iter = particles.begin(); iter != particles.end(); iter++) iter->draw(); }
ParticleSource毎に独立してParticleクラスのインスタンスを管理します。描画の色もParticleSource毎に設定します。Particleクラスについては前回のエントリで用意したものとほぼ同じなのでここでは割愛します。前回ご紹介した内容で、Particle.hとParticle.cppを用意しておいてください。これで完成です。
実機での実行例
それでは実行してみましょう。少々見づらいですが、以下のように画面上に色とりどりのパーティクルが発生します。
おわりに
Cinderを使ってマルチタッチ対応のiPadアプリケーションを実装してみました。ターゲットをiPadにしても、上記程度のものならやはり簡単に実装することができます。
これまでの実装例はすべて2次元での表現ばかりでしたが、OpenGLを利用しているのでもちろん3次元表現も可能です。iPadや最近のiPhone/iPod touchであればOpenGL ES 2.0に対応していますので、ブログラマブルシェーダを使った凝った表現も可能でしょう。私はまだまだOpenGLについては勉強中の身なので、いずれ何か面白いものができましたらご紹介したいと思います!
それでは!
Tag