水無月の余韻 開発Sc.

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

加賀ゆびぬきの模様シミュレータを作った

「加賀ゆびぬき」の刺し模様をシミュレートするツールを作りました。

以下のURLに公開しています。

加賀ゆびぬき 刺し模様シミュレータ - 水無月の余韻

「加賀ゆびぬき」とは

加賀ゆびぬきとは、石川県金沢に伝わる、裁縫道具です。

縫い物をするときに使う、ゆびぬきはわかりますか? 家庭科の授業で見たのは、革や金属で作られたものが多いでしょう。

加賀ゆびぬきは、裁縫道具としてのゆびぬきに、糸をめぐらせ、丈夫に、そして美しくなったものです。

どんなものかというと、見るのが早いです。 ググってもたくさんの画像が出てきますが、拙作を載せておきます。

f:id:ichiko_revjune:20150105103727j:plain

数がないので、一番はじめに作ったものから。(なかなかひどいw) 右にいくほど新しいです。

加賀ゆびぬきは、針の運びは単純でいながら、幾何学模様を作り出せるおもしろいものです。

キットの販売もありますが、もともとは端切れや、余った糸を使っていたであろう副道具です。 はじめのひとつは、家にあるものでできるかもしれません。

参考図書

今回、参考にした本。

開発過程のメモ

図形の描画と、各種入力があるものの、アクセスが簡単なところに公開したかったので、Webアプリにしました。

ただし、バックグラウンドはなくて、javascriptで完結させています。

Knockout.jsを使いつつ、コードはCoffeeScriptで記述しました。

Knockout.jsは、MVVMパターンのUIフレームワークです。 本家のチュートリアルが充実していてよいです。

アプリケーションのコードは、githubで公開しています。

http://github.com/ichiko/kgyubinuki

ひっかかったこと

使う上で、ひっかかったのは、Knockout.jsを経由したアクション呼び出しの際に、thisの中身が変わるということ。 javascriptorには当然なのかもしれないが、CoffeeScriptなどで便利に記述しているとつい忘れてしまう。

画面には、ViewModel KomaVMの配列をもとにリストを生成しています。 リストの各要素の KomaVM のメソッド addNewIto を呼び出す際、thisの参照解決ができずにはまりました。 (addNewIto のメソッドは見つかるものの、実行時エラー)

アクションに指定するメソッドは、独立したfunctionとして成立するように定義したほうがよいようです。

# viewmodel.coffee (だめパターン)

class KomaVM
    (略)

    addNewIto: ->
        @addIto(DefaultIto.Color, DefaultIto.Round)

    (略)

    addIto: (color, roundNum) ->
        ito = new ItoVM(color, roundNum)
        @itoArray.push ito
        return ito
# viewmodel.coffee (使えるパターン)

class KomaVM
    constructor: (offset, type, komaKagari, config, setDefault = true) ->
        (略)

        @addNewIto = ->
            self.addIto(DefaultIto.Color, DefaultIto.Round)

        (略)

    addIto: (color, roundNum) ->
        ito = new ItoVM(color, roundNum)
        @itoArray.push ito
        return ito

実装上の工夫

始めのバージョンでは、ViewModel-Viewの2階層で考えていました。 しかし、Knockout.jsは、監視対象のオブジェクトに専用のオブジェクトを使用するため、jsオブジェクトの中身が煩雑になりがちです。

Knockout.jsは、ko.observable()などの関数を通して監視対象オブジェクトを生成するため、バインディングがあるメンバには関数を通したアクセスが必要で、バインディングがないメンバには直接の値アクセスができるという状態になります。 バインディングの有無が入り乱れていると、かなり煩雑です。

そのため、だいたいの見通しがたったところで、Model-ViewModel-Viewの3階層で考え直しました。

アプリの根幹は、Modelオブジェクトに保持した設定にしたがって、View(HTML)に模様を描画することにあります。 入力のバインディングを一度排除して、Modelを構成し、それをラップする形でViewModelでバインディングを定義することにしました。

ラップすることによる冗長さはあるものの、Model層で機能の担保がし易くなりました。

(MVVMパターンを習得した人にとっては、これが当然のかたちなのかもしれないな ^ ^;)

色を合わせて撃ち落とす、「Ship Trails」をリリースしました(その2)

前回の記事で、「Ship Trails」のゲームについてはお話ししました。

今回は、開発サイドの話をします。

告知が間に合ってませんが、同様の内容で、今日 Picotachi#19 に参加してきます。 Picotachiは吉祥寺にあるPico Pico Cafeさんで、開催されるクリエイターのためのイベントです。

(11/15 Picotachi#19で使用したスライドを追加)

発端

今回のゲームは、「飛行機雲で、飛行機の軌跡が見えるのいいよね」というところから始まりました。

どうゲームにするかというところでは、「難しいけど面白い」ゲームにしたいという方向性になりました。Super HexagonやWAVE WAVEみたいな、とっても難しいのにはまってしまうやつです。

そのため、かなり難しめのゲームになっています。

(その分、長く遊んでもらえたらいいなぁ。)

開発スペック

前回つくったゲーム「ヤミからの逃走」に比べると、開発規模はだいぶ大きくなりました。

@slightairとの分担も変更しています。メインプログラムを私が担当し、BGM/SEと背景設定を@slightairが担当しました。

フレームワーク& ツール

Cocos2d-xは3.x系になってから更新速度が爆速で、開発中にもどんどんバージョンがあがっていきました。最終的に、Cocos2d-x v3.2を使っています。

あわせてCocosBuilderも使って、一部の画面を構築しました。CocosBuilderは、さっくり作ってすぐに動くという点は便利ですが、変更が入ったときには、手間が増えがちですね。今回のアプリでは、タイトル画面とそこから遷移するOPTIONメニューから下をCocosBuilderで実装しましたが、ボタンが増えて、ボタン位置を変更する、となるとアニメーションの再設定が大変でした。

ツールとしてあげた、KORG M01dは、nintendo 3DS で動くシーケンサーです。各ステージのBGMを@slightairが作曲しており、そのツールとなりました。持ち運べるし、便利に使えるソフトです。

開発期間:約7ヶ月

開発期間が長いですが、挑戦したことが多かったのと、難易度調整に時間がかかったことが大きいです。中だるみもありました。

挑戦したこと

挑戦したことは大きく5つあります。

  1. BGM自作
    実は、これまでのゲームは、SEは用意したもののしっかりとBGMを用意していませんでした。今回はステージ制ということもあり、各ステージに欲しいとなったので、曲数が増えました。雰囲気を変えて曲を用意するのって大変です。@slightair様々です。

  2. プレイデータの収集
    Google Analyticsを使い、プレイ状況を把握できるようにしました。どのステージを遊んだか、クリアできたかといった情報を収集しています。
    その結果、想定外に難しいらしいということがわかっています。。。

  3. プレイ結果のシェア機能
    アプリを知ってもらう機会を増やしたいので、みんなに広めてもらおうという魂胆です。プレイ結果を画像としてシェアできます。
    iOS では、TwitterFacebookへの投稿に対応しました。 Androidでは、Twitterなど、画像投稿に対応しているアプリへ連携するようにしました。
    ハイスコアを更新したら、ぜひ投稿してください。

  4. 広告
    収益化を図るというほどではありませんが、広告を導入しています。今回はAdMobのみの導入です。AdMobメディエーションの機能を使用して、各社の広告を使えるようにすると、いいのかもしれません。

  5. 広告除去アイテムの販売
    初めてのアプリ内課金です。実装は大変でした。
    そして、リストアできないという理由でリジェクトされました。
    リストアボタンを置いていないアプリは散見されたので、購入ボタンひとつで申請したのですが、リジェクトされました。App Storeの仕様で、購入済みアイテムを再度購入しようとしても、再決済は走らないので「購入動作」しか実装していなかったためと思われます。ユーザにリストアできることがわかるとか、明らかにリストアが実行されたと明示しないといけないようです。

難易度調整

ステージの難易度調整は難しかったです。

ここがコンテンツの肝なので、気を抜く訳にもいきません。

ステージデータは、外部ファイルに定義して、アプリで読み込む形式にしています。専用のエディタを作って、ステージ名からオブジェクトの色まで定義しました。

始めは、何が落ちてくるかを完全固定にして作りました。

完全固定では、一度クリアしたらおわり、になりがちです。また、スコア画面に表示する軌跡の模様を重視して構築すると、単調になりがちでした。これは、軌跡の模様を美しくしようとすると、規則的な繰り返しになるためです。

そこで、数種類のパターンを用意して、ランダムに使用することにしました。

これにより、面白みはでたものの、ランダムに抽出するプールが大きくなると、難しすぎてゲームになりませんでした。

パターンをグルーピングして、ランダムに抽出するプールを小さくすることで、現状の難易度を実現しています。

一見矛盾しているのですが、ステージの定義は「簡単なステージを作るのが難しい」というのが感想です。

テスター:7人+α

開発のいろいろな段階で、友人たちにプレーしてもらいました。難易度調整のための気づきは、テストプレイの中から得ることができました。感謝しています。

今後

「Ship Trails」は、出して終わりじゃなく、バージョンアップを繰り返して、アプリをよりよくしていきたいと思っています。

@shiptrails_game で、みなさんのプレイを共有したり、アップデート告知を行っていく予定です。よければフォローしてください。

さらに、貢献してやろうというお方は、@shiptrails_game までフィードバックをいただけると助かります。

よろしくお願いします。

Picotachi#19で使用したスライド

(11/15 追記)

Picotachi#19で使用したスライドをあげておきます。

発表後は、たくさんの反応をいただけました。面白いといっていただけてうれしかったです。 音ゲーとして認識する人もいて、面白かったです。

しっかり育てていかねばな。

色を合わせて撃ち落とす、「Ship Trails」をリリースしました

f:id:ichiko_revjune:20141105092725p:plain

色を合わせて撃ち落とす、「Ship Trails」をリリースしました。

今回も @slightair と共作です。

ダウンロード(無料)

Android版を先行リリース中です。iOS版はしばしお待ちを。 iPhone/iPad版も公開になりました!(11/12)

for Android

Get it on Google Play

for iOS

どんなゲーム?

今回は、ざっくりどんなゲームかを紹介していきます。

f:id:ichiko_revjune:20141105095947p:plain

シューティングゲームに似ていますが、パズル的な要素を含んだゲームです。

自軍は4機の船。船を動かして、敵を的確に落としていくとステージをクリアすることができます。

f:id:ichiko_revjune:20141105100021p:plain

船は、船と同じ色の弾を撃ちます。 敵と同じ色の弾をぶつけることで、敵を撃ち落とすことができます。

敵の動きに合わせて、すばやく船の位置を動かしていきましょう。

f:id:ichiko_revjune:20141105100029p:plain

60秒生き残れたら、ステージクリア!

それぞれのステージで100%を目指してください。

いろいろな雰囲気でステージを用意しているので、ぜひ制覇してくださいね。

スコア画面では、'SHARE' ボタンを押すことで、画像とともに投稿が可能です。

ハイスコアを更新したら、どんどんツイッターなどに投稿してね。

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

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まわりなど、試していきたいです。