2012年01月21日

Javaな人向け、enchant.jsのクラスベース実装入門

会社内で「これ面白いよ」と教えてくれた人がいたので、enchant.jsを触っています。
先月はパソナテックで開催されたワークショップにも参加させてもらいました。
enchant.jsが作られたときの話や、いろいろなアイディアが見られたライブコーディングなど、とても楽しめるイベントでした。感謝!

enchant.js は、SFC時代のような2Dゲームを、Javascriptで簡単に作れるフレームワーク。
「キャラクターが歩く」程度ならさくっと書けます。
しかし私はJavascriptをほとんど使ったことが無いので、基本的なことが難しい……。

私はJavaでプログラミングをはじめたJavaな人なので、考え方がJavaライク。
すべてはクラスに収まっていて欲しいし、クラスのプロパティで状態を制御したいし、インスタンス変数とクラス変数は使い分けたいし、static変数も欲しい。PHPは使っているのでオーバーロードが無いのは大丈夫ですが、オーバーライドは欲しいです。
もちろん、川の流れのように上から下に流れていく短い処理、たとえば業務用のバッチ処理ならクラスは無くても気になりません。
が、ゲームって、複数のオブジェクトがそれぞれに定義づけられた動きを、時には個々で、時には協調して行う、って類のものですよね。

これはぜひオブジェクト指向、クラスベースで整理したい。

ということで初心者的にいろいろ調べたので、入門の役に立つかもということで記事にしてみます。
Javascriptってオブジェクト指向が出来る聞いていたんですが、Javaのオブジェクト指向とはぜんぜん違うんですね……。


クラスの作り方

enchant.js の場合これは簡単。
ダウンロードしたサンプルにある、シューティングゲームが参考に出来ます。具体的にはこんな感じ。

var Player = enchant.Class.create(enchant.Sprite, { // 継承元とする親クラス
    initialize: function(x, y){                     // コンストラクタ
        enchant.Sprite.call(this, 16, 16);          // 親クラスのコンストラクタを呼ぶ。Javaで言えばsuper()
        ...
    }
});

コンストラクタ以外の関数

コンストラクタは出来たので、次にコンストラクタ以外の関数を……とクラスの中にfunctionを追加したところ構文エラーになりました。
原因が分からず時間を食ってしまったのですが、もう一度サンプルをまじまじと見ていたらふと気づいたもの。

var Enemy = enchant.Class.create(enchant.Sprite, {
    initialize: function(x, y, omega){
        enchant.Sprite.call(this, 16, 16);
        ... 
    }, // ← お?
    remove: function(){
        game.rootScene.removeChild(this);
        delete enemies[this.key]; delete this;
    }
});

なんと関数って配列のようにカンマ区切りで列挙するんですか。
カンマを入れたら無事動きました。

インスタンス変数を作る

Javaみたいに、フィールドを宣言する必要はないようです。

var Player = enchant.Class.create(enchant.Sprite, {
    initialize: function(x, y){
        enchant.Sprite.call(this, 16, 16);
        this.foo = "bar";
    },
    
    otherFunc : function() {
        alert(this.foo);
    }
});

上の「this.foo = "bar"」のようにいきなり「this.変数名」と書いて値を設定してしまえば、それが別の関数からでも呼び出せました。へー。
逆にJavaで言うフィールドの部分に var foo = "bar"; というように書いてもエラーになってしまうようです。

じゃあstatic変数はどうするのさ

フィールドを置くことができないとなると、staticってどうするの? と調べてみたら、こちらの記事が参考になりました。

Tricorn Labs » JavaScriptでなんちゃってstatic変数(クラス変数)を実装してみる。

staticって無いんですか。しかもfunctionが状態を持てるのですか。
これ以外にも調べてみると、そもそもenchant.jsでないJavascriptではクラスってこんな風に作るものらしいです。

var Foo = function () {
    // ...
}
var fooInstance = new Foo();

クラスとfunctionの境目がないというか……クラス = インスタンス化できる関数、といったところでしょうか。違和感。あるオブジェクトがいくつかの動作(メソッド)を持っていて、それぞれ必要なときに呼び出せばクラス毎に適切な動きをする、という考え方はしないのかな?

enchant.jsのClassと、JavascriptのClassの違い

上記のようにあれっ? と思っていたところで、enchant.js の開発元のブログで、タイムリーにもほんの数日前、それについて触れられた記事が書かれていました。

enchant.jsのクラス継承とJavaScriptのクラス継承のちょっとした違い

……ちょっとしてないです!
この記事を読んで、いろいろ納得できました。

なるほど、Javascriptは、プロトタイプベースという考え方をするんですね。そしてJavaはクラスベース。
new した段階で、Javaでは完成品のインスタンスが作られるのに対して、Javascriptはまだ未完成な材料だけが提供されて、そのあとで必要なものを肉付け出来る、という感じでしょうか。

Javascriptで、クラス定義のコードがイコール関数定義なのは、コンストラクタ以外の動作はインスタンスを作ったあとから定義していくから、と理解すればいいのかな。


今日調べたのはここまでです。Javascript界をほとんど知らないのでとても新鮮でした。

次は、クラスAとクラスBが、お互い連携しながら動作する、というのをどう書けばいいのかなと考え中。

// プレイヤー
var Player = enchant.Class.create(enchant.Sprite, {
    initialize: function(x, y, omega){
        enchant.Sprite.call(this, 16, 16);
        this.attack = false; // 攻撃中か
    }
});

// 敵
var Enemy = enchant.Class.create(enchant.Sprite, {
    initialize: function(x, y, omega){
        enchant.Sprite.call(this, 16, 16);
        this.attack = false;
    }
});

こういう状態で、「PlayerクラスとEnemyクラスのattackがtrueの状態で接触していたら、相手にダメージを与える」という処理をしたいのですが、そのEventListenerをそれぞれのクラスに持たせるのがなぜかうまく行きません。Player クラスのインスタンスと、Enemyクラスのインスタンスが見えてない感じ。
しかしシューティングゲームのサンプルを調べてみたら、geme.onload の中で生成している enemies という変数を、PlayerShoot クラスのEventListenerの中で呼び出す、という処理があるので、function 間のカンマのように単純なミスなのかな。こ
これもわかったらまた記事に出来ればと思います。

→2012/01/28追記:  続き書きました


Posted by ragi_d at 23:31│Comments(0)TrackBack(0)その他技術系 はてなブックマーク - Javaな人向け、enchant.jsのクラスベース実装入門

この記事へのトラックバックURL

http://trackback.blogsys.jp/livedoor/ragi_d/65639205

コメントする

名前
URL
 
  絵文字