水無月の余韻 開発Sc.

プログラミング関連の雑記

大量のパーティクルを描画する場合のヒント

COCOS2D-X 3.0 を使用してゲームを作っています。

iOS版から着手して、あらかた形ができたのでAndroid版の対応をしだしたところで問題が発生しました。

問題

エフェクトを表示するために、大量に同じパーティクルを使用する場面があります。その場面を表示する際、Androidで一部の描画がされなくなるのです。

例えば、壁にボールをぶつけて、衝突したときのエフェクトをパーティクルで描画しているとしましょう。

ボールがひとつで、パーティクルもひとつ描画するときには、きれいに表示されました。 ボールをどんどん増やして、あちこちで衝突が起きるようになると、パーティクルやボール、または壁が表示されなくなってしまうのです。

解決策

ボールの数を減らすと発生しないため、ボールまたはパーティクルの描画方法を変えることで解決できそうです。

既にボールはSpriteBatchNodeで描画していたため、パーティクルの描画を変更したいです。

パーティクルの描画をまとめる方法を探してみると、 ParticleBatchNodeというクラスがあることがわかりました。

cocos2d-x: ParticleBatchNode Class Reference

(描画の高速化関連の記事ではほとんど言及されていないクラスです。ParticleSystemを使うことって少ないの?)

サンプルコード

Scene::init() 内で、パーティクルで使用する画像ファイル名を指定して、ParticleBatchNodeを生成します。 メンバ変数に格納しておくとよいでしょう。

// Scene::init()
_effectNode = ParticleBatchNode::create("hit_effect.png");
this->addChild(_effectNode);

パーティクルを追加する際には、生成しておいた、ParticleBatchNodeに対して行います。

auto hitEffect = ParticleSystemQuad::create("hit.plist");
hitEffect->setAutoRemoveOnFinish(true);
hitEffect->setPosition(someX, someY);

_effectNode->addChild(hitEffect);

この方法でパーティクルの描画をしたところ、Androidでも描画されるようになりました。

パーティクルの描画が多くて、重くなっている場合にもこの方法が使えるかもしれません。

Mavericksにアップデートしたら、rbenvが消えた件

備忘録です。

MBPのOSをMavericksにアップデートしたら、terminal上で、rbenvが見えなくなりました。
$ which rbenv
rbenv not found

rbenvは、Homebrewを使ってインストールしたので、brewコマンドで存在を確認します。 brewによると、rbenvは存在するようです。

$ brew list rbenv
/usr/local/Cellar/rbenv/0.4.0/bin/ruby-local-exec
/usr/local/Cellar/rbenv/0.4.0/bin/rbenv
/usr/local/Cellar/rbenv/0.4.0/completions/ (2 files)
/usr/local/Cellar/rbenv/0.4.0/libexec/ (25 files)
brewの状態を確認した

--

依存関係がおかしかったり、問題はあるのですが、rbenvが見えなくなった件とは関係ありませんでした。

--

brewがおかしいのかもしれないので、まずチェックしました。

$ brew doctor
Warning: Some installed formula are missing dependencies.
You should `brew install` the missing dependencies:

    brew install freetype libpng

Run `brew missing` for more details.
Error: Homebrew doesn't know what compiler versions ship with your version
of Xcode (5.1). Please `brew update` and if that doesn't help, file
an issue with the output of `brew --config`:
  https://github.com/mxcl/homebrew/issues

Thanks!

Warning: Your Homebrew is outdated.
You haven't updated for at least 24 hours, this is a long time in brewland!
To update Homebrew, run `brew update`.

依存関係の解決が必要なのと、Homebrew自体が古いと言われました。

先にupdateしたほうがいいかと思ったのですが、エラーになりました。しかも、解決しにくい。

$ brew update
Error: undefined method `to_sym' for nil:NilClass
Please report this bug:
    https://github.com/mxcl/homebrew/wiki/troubleshooting
/usr/local/Library/Homebrew/cmd/update.rb:134:in `report'
/usr/local/Library/Homebrew/cmd/update.rb:132:in `each_line'
/usr/local/Library/Homebrew/cmd/update.rb:132:in `report'
/usr/local/Library/Homebrew/cmd/update.rb:36:in `update'
/usr/local/Library/brew.rb:91:in `send'
/usr/local/Library/brew.rb:91

brewのupdateは置いといて、指示された順番に、依存関係の解決からやりましょう。

コマンドも指示されているので、そのまま実行。

$ brew install libpng freetype
==> Downloading https://downloads.sf.net/project/machomebrew/Bottles/libpng-1.6.10.mavericks.bottle.tar.gz
######################################################################## 100.0%
==> Pouring libpng-1.6.10.mavericks.bottle.tar.gz
🍺  /usr/local/Cellar/libpng/1.6.10: 17 files, 1.3M
==> Downloading https://downloads.sf.net/project/machomebrew/Bottles/freetype-2.5.3_1.mavericks.bottle.tar.gz
######################################################################## 100.0%
==> Pouring freetype-2.5.3_1.mavericks.bottle.tar.gz
🍺  /usr/local/Cellar/freetype/2.5.3_1: 60 files, 2.5M

この後、update実行かと思ったのですが、チェックするとエラーなくなってる。

$ brew doctor
Your system is ready to brew.
brewインストールしたコマンドが入る場所が空になってた

brewでインストールしたコマンドは通常、/usr/local/binに入ります。 が、見てみたらbrewしかいなかった。

$ ls -l /usr/local/bin
-rwxr-xr-x  1 hoge  admin   813B  3 25 11:50 brew

brewのインストール済みリストと違う。

$ brew list
autoconf    git     jpeg        lua     phantomjs   rbenv       sbt
freetype    gnuplot     libpng      mysql       pidof       readline    scala
gd      imagemagick libtool     openssl     pkg-config  ruby-build

エイリアスは消えちゃったみたいです。

再インストールしようにも、インストール済みの場合は、なにもしてくれない。 (インストールには、Xcode Command Line Toolsも必要です。)

$ brew install rbenv
Warning: rbenv-0.4.0 already installed

なので、アンインストールしてから、インストールしました。

$ brew uninstall rbenv
Uninstalling /usr/local/Cellar/rbenv/0.4.0...
$ brew install rbenv
  (中略)
$ which rbenv
/usr/local/bin/rbenv

地味に手間取ってしまった。残念。

--

追記 brew上ではインストール済みだったので、リンクだけ復活させればよかったかも。

brew link rbenv

testemを使ったjavascriptテストの自動化

javascriptのテスト実行に、testemを使ってみました。

testem

testem は、テストランナーです。 https://github.com/airportyh/testem

大きな特徴は、3つです。

  1. 複数のテストフレームワーク(jasmine, mocha, QUnitなど)に対応
  2. Node.js、phantomJSの他、ブラウザ上での実行に対応
  3. Coffeescript, browserify などのプリプロセッサをサポート
インストール

npm を使用してインストールが可能です。(Node.jsが予めインストールされている前提です。)

npm install -g testem
設定ファイルの作成

testemの設定ファイル(testem.json)を作成します。

testem.ymlとすることもできますが、今回は、testem.json です。

{
  "before_tests":
    "grunt pre-spec"
  ,

  "framework": [
    "jasmine"
  ],

  "src_files": [
    "src/coffee/**/*.coffee",
    "spec/coffee/**/*.coffee"
  ],

  "serve_files": [
    "spec/js/**/*.js"
  ]
}
  • framework
  • src_files
  • serve_files
    • テスト実行時に使用するファイルを指定します。
    • 'spec/coffee/**/*.coffee' をコンパイルして、'spec/js/**/*.js' を生成し、js ファイルをテストで使用します。
  • before_tests
    • イベントハンドラのひとつです。
    • 記述しておくと、そのタイミングでコマンドが実行されます。
    • テスト実行前に、coffeescript→js のコンパイルを実行する必要があるため、その grunt タスクを指定しています。
    • プリプロセッサ対応の実態は、ここに適切にコマンドを指定することでできます、ということ。
テストの実行

テストコードと対象のコードを用意して、 testem コマンドを実行すると、はじめはこんなメッセージが表示されます。

TEST'EM 'SCRIPTS!
Open the URL below in a browser to connect.
http://localhost:7357/

    Waiting for runners...

f:id:ichiko_revjune:20140305193131p:plain

表示されているURLにアクセスすると、そのリクエストでテストが実行されます。

同時に、コンソールにも結果が表示され、こんな画面になります。(テストが1件の場合)

f:id:ichiko_revjune:20140305192837p:plain

ブラウザを開いた状態で、 src_files で指定しているファイルを編集し、保存すると、テストが再実行されます。 (ブラウザもリロードされます。)

テストコードのファイルを追加すると、そのファイルも実行してくれるようになります。

※画像の背景に映りこんでいるのは、この記事の編集画面です。(/ . \);

MVVMフレームワーク vue.js を使ってみた

MVVMパターンは、馴染がないなと思っていたのですが、調べるとMicrosoftの.NET(WPF)が近いことがわかりました。 なんだ、触れたことあるじゃん、って。

参考

そんな、MVVMパターンjavascript ライブラリ vue.js を使ってみました。

vue.jsのサイト のトップを見ると、"MVVM Made SImple" とあります。 たしかに、UIまわりをシンプルに書けました。

vue.js のさわりは、本家サイトもいいですし、私は、軽量でパワフルなデータバインディングMVVM, vue.jsで遊んでみた - mizchi's blog を参考にさせてもらいました。

MVVMパターン

MVVMパターンの特徴として以下の2点があります。図にまとめてみました。

  • View と ViewModel 間でデータバインディングが行われる
  • View 上のイベント処理は、ViewModel のコマンドによって表現、実行される

f:id:ichiko_revjune:20140225153300p:plain

vue.js では、View層とViewModel層を関連付けて管理するための、Vue クラスが提供されます。

View層に関しては、テンプレートを記述することで、バインディングの指定や、配列の繰り返しなどを記述することができます。これはなかなか便利。

フレームワークの強制力が弱いので、かなり使い易いライブラリだと思いました。 まだ、ほんのさわり程度なので、いろいろできそうなコマンドFilterまわりなど、試していきたいです。

経過時間を記録するアプリ

時間を記録して、直近の記録からの経過時間を表示するiPhoneアプリを作りました。

MMOなどで、一度取得すると一定時間取得できなくなるアイテムがあったりしまして、その把握を簡単にするのが目的です。

開発中にひっかかったとろなどを記録します。

完成図

アプリを起動すると、直近の記録と経過時間が表示されます。

画面1f:id:ichiko_revjune:20140107103628p:plain

「履歴を見る」、から一覧で参照することができ、削除もできます。

画面2f:id:ichiko_revjune:20140107103632p:plain

画面3f:id:ichiko_revjune:20140107103636p:plain

TableView with Storyboard

UIをStoryboardを使用して作成することにしたのですが、UIが反映されないという問題にハマりました。

  1. XCodeEmpty Applicationテンプレートから作成する。
  2. UIをStoryboardで作成することにしたので、プロジェクトに新規のStoryboardを追加する。
  3. TableViewを使用するので、Object libraryから、Table View Controllerを貼り付ける。
  4. TableViewの挙動を書き換えるため、UITableViewControllerを継承したクラスを作成し、プロパティCustom Classに指定する。
  5. そのまま実行してみると、画面は真っ白のままです。
  6. AppDelegate.didFinishLaunchingWithOptionsを変更して、Storyboardで指定した、Custom ClassからUIViewを作成して、windowに追加する。(←ここで間違った!
  7. 空っぽのTableViewが表示されました。(←ここで騙された!
  8. TableViewの項目に、セルの番号(indexPath.row)を設定するように変更する。
  9. 空っぽのTableViewが表示されました。おかしいぞ…

プロジェクトテンプレートをSingle View Applicationとすれば、テンプレートに含まれるStoryboardを使用するようにプロジェクトが設定されています。

Storyboardを使用するようにするために、以下のようにしました。

  1. AppDelegate.didFinishLaunchingWithOptionsを変更して、以下の内容のみとする。
    return YES;
  2. Project Navigatorのルートを選択して、Targets > [Application Name] > General > Deployment Info > MainInterface に、作成したStoryboardを指定する。

Core Data

日時の記録は、Core Dataを使用して保存することにしました。

Core Dataの基本的な使い方を調べて、CRUDを実装しようとしました。 このときの、Core Dataの使い方はこんなイメージです。

f:id:ichiko_revjune:20140107110914p:plain

Core Dataのしくみ、使い方は、以下のページを参考にしました。

その後、TableViewとCore Dataの連携はもっと簡単になるよ、ってことでキーワード NSFetchedResultController で調べました。

参考にしたのは、以下のページです。

NSFetchedResultController と NSFetchedResultsControllerDelegate を使うと、一覧上での削除はずいぶんと簡単になりそうです。差分だけ更新が可能になるので。 画面を表示するたびに、データを取得しなおして、再表示という手間はなくなりました。

ただし、完全になくなったというわけではなく、画面1では、viewDidAppearにて、画面の更新をしています。一覧で最新のレコード(画面1で表示しているデータ)が削除された場合に、画面が戻ってきてから、表示更新までにラグが発生することがあるためです。

Core Dataの使い方は、こうなります。 NSFetchedResultControllerは、各画面ごとに取得・保持しています。

f:id:ichiko_revjune:20140107112115p:plain

ソースコード

コードはgithub.comで公開しています。 実際のコードを確認する方はこちらからどうぞ。

ichiko/SingleTimeLogger

はじめまして

明けましておめでとうございます。 年も改まり、開発関連のブログを別立てすることにしました。

もともとは、水無月の余韻 ブログです。 今後は、開発関連の記事は、こちらのブログに記載していきます。

開発関連といっても、日々のプログラミングなどで調べたこと、成果物を勝手に解説、といった内容を予定しています。 備忘録的な性質が強いかもしれませんが、誰かの役に立つことがあれば幸いです。