p5js : Gulp + Browserify + Babel で p5を使えるテンプレート

p5.jsがWEBGL対応し、Javascriptでできる表現の幅が広がりそうです。

本家プロジェクトではEditorとかいろいろアプリのScaffolding的なものも用意されているのですが、いわゆるフロントエンド系の環境にならって個人でアプリプロジェクトのテンプレを作ってみました。

gulpによる自動化

下記のように、npm スクリプトによるJavascriptのビルドなども例としてはあるのですが、今回はgulpで自動化タスクを記述しました。

maxmechanic/simple-p5js-browserify · GitHub

タスクにしておいたほうが取り回しがいいのと、後々拡張しやすいと思ったので。 Gruntでもいいのですが、せっかくなのでgulpにしてみました。

あとp5.jsのbowerによる配信は非公式かつ、requireでの解決ができません。 npmならモジュールなので、requireで読み込むことができます。ES6のモジュールベースで作るプロジェクトと相性がよいのでnpmの配信を使います。

gulpfileは下記で公開されているものがよかったので、ベースにしました。

まずはuglifysource mapなどを入れてJavascriptビルドを盛りつつ、いらないタスクを削ります。

Babel

続いて本命のBabelです。

Processingは作りとしてはOpenGL的な命令セットで、明示的にオブジェクト指向のようなパラダイムをもっていませんが、言語としてはサポートしているのでコードが大きくなってくるとやはりオブジェクト設計をしたくなります。そしてファイルも分割したいと。

JavascriptではCommonJS、AMDなど歴史的にいくつかのモジュール構文が存在しますが、今後の筆頭はやはりES6だろうということでBabelを利用してこれを実現していきます。

最初は普通にglup-babelでES5に変換してみたのですが、nodeのつもりでCommonJSのrequire構文に変換されるためフロントエンド環境では動きません。

gulp.task('js', function() {
    return gulp.src(config.js.files)
        .pipe($.babel({
            presets: ['es2015']
        }))
        .pipe(gulp.dest(config.js.output.directory));
});

ブラウザでBabelによる変換後のコードを動かすためにBrowserifyと合わせてビルドする必要があるのですが、babelifyの方が一緒にタスク化しやすかったので、 今回はbabelifyを利用します。

Browserify + Babelify

必要なモジュールをインストール。

$ npm install --save-dev browserify babelify babel-preset-es2015

変換は下記のワンライナーで実行できます。

var bundler = browserify('./app/js/appljs').transform('babelify', { presets: ["es2015"]} );

この結果のままだとgulpでpipeできないので、gulpのstreamで扱えるようにしてpipeできるようにします。

いくつか方法があったので、メモしておきます。今回作成したテンプレートでは①を使っています。

① vinyl-source-stream, vinyl-bufferを利用する
gulp.task('js', function() {
    var bundler = browserify('./app/js/app.js').transform('babelify', {presets: ["es2015"]});

    return bundler.bundle()
        .on('error', function(error) {console.log(error);})
        .pipe(source('app.js'))
        .pipe(buffer())
        .pipe(gulp.dest('./dist/js2'));
});

(参考) gulp & browserify (+watchify +babelify) · GitHub

② vinyl-transformを利用する(動作しない)
gulp.task('js', function() {
    var browserified = transform(function(filename) {
        var b = browserify(filename);
        b.transform('babelify', { presets: ["es2015"]});
        return b.bundle();
    });

    return gulp.src('./app/js/app.js')
        .pipe(browserified)
        .pipe(gulp.dest('./dist'));
});

(参考) Gulp+Browserifyでsrc/**/*.jsをdist/**/*.jsにする | 高橋文樹.com

しかしvinyl-transformを利用したコードは、下記にあるように現在の最新(1.0.0)だと動きません。

③ through2を利用する

vinyl-transformが動作しないissueで言及されていますが、下記にあるようにthrough2を使って書き直す方法もあります。

gulp.task('js', function() {
    return gulp.src('./app/js/app.js')
        .pipe(through2.obj(function (file, enc, next){
            console.log('browserify: ' + file.path);
            browserify(file.path)
                .transform('babelify', {presets: ["es2015"]})
                .bundle(function(err, res){
                    // assumes file.contents is a Buffer
                    if (err) {console.log(err);}

                    file.contents = res;
                    next(null, file);
                });
        }))
        .pipe(gulp.dest('./dist'));
});

(参考) exception: browserify + vinyl-transform · Issue #1198 · substack/node-browserify · GitHub

まとめ

というわけで、JSのビルドができました。後はeslintがes2015に対応するようにしたり、p5.jsモジュールでサンプルを実装したりと細々作業を行って…完成!

できたテンプレートはこちらになります。

Google Cast : Chromecast上で動くGameを作ってみる(環境構築編)

それほど世間では騒がれていないですが、AndroidTVが発売されましたね。それに合わせてChromecastの機能がAndroidTVに搭載されて、「Google Cast」と名称が変わりました。

少し前まではVideoやAudioのCastがメインだった印象のChromecastですが、調べてみるとGame向けという名目でInteractiveなアプリ制作のためのAPIも用意されているようです。

これを使えば子供とTVを使って遊べるアプリが作れそうなので、ちょっと調べてみました。まずは開発環境からサンプルランまでです。

Google Cast SDK

Google Castで作成できるアプリはGame以外にも幾つかありますが、ここではGameカテゴリのAPIについて記載します。

developers.google.com

公式Developer サイトによると、Game 向けには以下2種類のAPIセットが用意されているようです。

Game Manager APIはサーバ側に公開したHTML/CSS/Javascriptで作られたアプリとAndroid/iOS/Chrome用に作成されたアプリとの間を通信するためのフレームワークAPIセットを提供しています。

一方Remote Display APIはまだベータ公開で、こちらはAndroid/iOSなどのデバイスから直接CastデバイスにレンダリングできるAPIセットを提供しています。つまりサーバ側アプリが必要ありません。

今回は、Game Manager APIを使ってアプリを作ってみようと思います。

Game Manager API を使ったサンプルラン

github.com

公式サイトで公開されているGame Manager APIのサンプルを使います。ここではSpritedemoを動かしてみました。

上記のDeveloper Guideにあるように、CastのアプリはSenderとReceiverが存在し、目的に応じて幾つかの実装タイプがあるようです。 Game Manager APIではGameManagerを介して通信するモデルになっています。

Sender

まずはAndroidのSenderを動かしてみます。

サンプルはReceiverが既にDeployされているらしく、ビルドしてインストールすればChromecast上でReceiverも動作しました。 Senderでやっているのは、ざっくり

  • 同じNetwork上のChromecastの探索
  • ReceiverアプリのLaunch
  • GameManagerClientの取得

あたりです。GameManagerClientが取得されたら、ユーザ操作に応じてメッセージを送信しているだけのシンプルな作りになっています。

自分の環境で動作させるReceiverと接続させるには、AppIDを変更します。サンプル内のCastConnectionManagerで参照されているStringを変更するだけです。 それぞれ、コンストラクタでデバイスサーチのときに、おそらくCapabilityを見るパラメータと、ReceiverアプリのLaunchをかけるところで使われています。

コンストラクタ

mMediaRouteSelector = new MediaRouteSelector.Builder().addControlCategory(
    CastMediaControlIntent.categoryForCast(mAppIdProvider.getCastAppId())).build();

ReceiverアプリのLaunch

Cast.CastApi.launchApplication(mApiClient, mAppIdProvider.getCastAppId())
    .setResultCallback(new LaunchReceiverApplicationResultCallback());

AppID(values/app_id.xml

<resources>
    <string name="app_id">D6120C32</string>
</resources>

AppIDはGoogle Cast SDK Developer Consoleで取得できます。取得方法は後ほど説明します。

Receiver

GameManagerを実装した、Custom Receiver ApplicationをCastデバイス上で動かします。

Custom Receiver ApplicationはHTML/CSS/Javascriptで書かれたアプリで、SDKとしてcast_receiver.jsがCDN経由で提供されています。Game Manager APIもこの一部になっています。Receiverの構成は公式サイトを引用すると、このようになっています。

https://developers.google.com/cast/images/receiver_diag.png

Receiverは任意のサーバに公開することが可能ですが、 公開するためにはSSHがサポートされていることが必要です。Debug時には必要ありません。

先のサンプルのGameManagerSamples/spritedemo/receiver/bin/以下をサーバに配置します。 どこでも良いと思うのですが、ここではとりあえず自分が運営しているサイトのレンタルサーバ上に配置しました。

サンプルそのままだと動かしたときに見た目で違いがわからないので、Spriteの画像を適当に変更します。 receiver/bin/assets/icon.pngを置き換えます。

Google Cast SDK Developer Console

任意のサーバに公開されたReceiverをGoogle Cast SDK Developer Consoleに登録し、AppIDを発行します。 またChromecast上でテストするために、デバイスの登録も必要です。

Registration  |  Cast  |  Google Developers

最初にGoogle Cast SDK Developer Consoleへの登録が必要です。Registration feeは$5.00です。

Google Cast SDK Developer Console

デバイス登録

デバイスの登録を行います。この登録がされていないと開発中のアプリをChromecast上で実行することができません。

Overviewもしくは、Deviceから「ADD NEW DEVICE」を選択します。 f:id:tomoyukim:20150922173634p:plain

この登録画面上で、Chromecast ExtensionからCastを実行します(PCのChrome上で登録ページを開いている必要があります)。 すると、Chromecast上にシリアル番号が表示され、読み上げられます。

表示されたシリアル番号を入力して、登録を勧めます。登録完了まで15分ほどかかりますのでコーヒーでも飲んで待ちましょう。

アプリ登録

続いてアプリの登録を行います。Overviewもしくは、Applicationsから「ADD NEW APPLICATION」を選択します。Application typeはCustom Receiverを選びます。 f:id:tomoyukim:20150922173206p:plain

先ほどアップロードしたReceiverのURLを入力して登録を進めます。 するとAppIDが発行されるので、それをSenderにコピペして再度ビルドします。

これで準備は完了です。これでSenderから実行すると、icon.pngを入れ替えたReceiverと動作するサンプルの実行が確認できると思います。

Debugging

最後にデバッグです。Google DevTools相当のデバッガをChromecast上で動くReceiverに対して利用することができます。

Debugging  |  Cast  |  Google Developers

Receiverを実行している対象のChromecastと同一ネットワークに接続しているPCでChromeを開き、ChromecastのIPを9222ポートで開きます。 するとデバッグ用のページが表示されるので、「Remote Debugging (App Engine)」でデバッガを開きます。

※設定によってはシールドアイコンでJavascriptの実行を許可する必要があります。

まとめ

これでGame Manager APIを使ったCastアプリケーションの開発環境が準備できました。

Receiverアプリ側では、任意のJavascriptライブラリやCanvasも利用できるようなので、様々な表現が可能と思われます。

また、Sender側のAndroidiOSアプリでは、ボタン操作だけでなくジェスチャやカメラを入力にしたコントローラを作ることもできそうです。

この後は、いろいろ試して子供と遊べるアプリを作ってみたいと思います。

Reactive Programming : retrolambda を使ってAndroidでラムダ式を導入してみる

Java8からStream APIラムダ式など、Reactive Programming的なモダンなパラダイムや文法がサポートされ、Androidでも利用したいと思うようになってきました。

RxAndroidなどを利用したStreamAPIの導入は、職場などではより一層ハードルも高いですが、ラムダ式などは敷居も低く、できれば導入したいと思うところです。

今回は、retrolambdaというバックポートツールを使ってラムダ式の導入を試して見たいと思います。

導入

基本的に特別なことはなにもないです。

本家のGithubにあるREADMEに従ってGradleを変更すればOKです。

github.com

RxAndroidとあわせて導入している事例は下記が参考になりました。

sys1yagi.hatenablog.com

build エラーに対応する

Android StudioではJava7でビルドを仕掛けますが、retrolambdaではJava8を利用するようで、その指定が必要です。

しかし、GithubにあるようにJAVA8_HOMEの指定をしても、うまくビルドが通せません。

Error:Execution failed for task ':app:compileDebugJava'.
> When running gradle with java 5, 6 or 7, you must set the path to jdk8, either with property retrolambda.jdk or environment variable JAVA8_HOME

これはAndroid Studio環境変数を参照していないことが原因です。 つまりターミナルで設定しているローカルな環境変数(.bash_profileなど)は、別のプロセスで動作するAndroid Studio には影響を与えられない、ということのようです。

Setup problem? · Issue #17 · evant/gradle-retrolambda · GitHub

従って、上記にあるように環境変数のスコープを広げるか、ローカルな環境変数が設定されたプロセスでAndroid Studioを起動するかすれば解決できることになります。

とりあえず、コマンドラインから起動して、ローカル環境変数を有効にしてみます。

$ cd /Applications/Android Studio.app/Contents/MacOS
$ ./studio

これでビルドできました!