OQRをenchant.jsで移植したんですよ


 一部で伝説となったミニゲーム「ObakenoQ-taroRevolution 」を、webで遊べるようにJavaScriptのゲームエンジン「enchant.js」(
http://enchantjs.com/ja/)を使ってブラウザで遊べるようにしたんだけど、そんときに色々と悩んだり解決したりしたことをメモとして書いておくよ!

0.この文章の意義

僕は以前、いろいろあってJavaScriptのテキストベースでミニゲームを作ったことがあった。そのときに初めてJavaScriptの使い方を学んだという程度の初心者なんだけど、そういう初心者がenchant.jsを利用してゲームを作る際、どんなことを考えててどんなところでひっかかったのか、初心者の視点として残しておくことで僕のようにenchant.jsで気まぐれにミニゲームを作りたくなった暇人の参考になれば、ということでメモしてる。

1.enchant.jsにした理由

「JavaScriptで何か作ろう」という動機は、「ブラウザ上で動くものならコンパイル無しで動くしっていうのと、ブラウザのみならテキストエディタでどんな場所でも書けるなーって思ったから。あと実行結果がすぐにブラウザで見られるのは実行結果が素早く可視化できてやる気につながると思った。
数あるJavascriptのゲームライブラリのなかでenchant.jsを選んだ理由は、単純に近所のBookOffでenchant.jsの本が売ってたから。言語習得は基本やりたいことをネットで検索して覚えるスタイルだけど、とっかかりをまとめて読んでおきたかったのと、買ったという事実を残して「この金額分は楽しまないとな」という気持ちにしたかった。まあ実際ほぼ使わなかったけど。

2.OQRについて

OQR(Obakeno Q-Taro Revolution)は、1999年に同人サークル「NEXT」が発表したフリーのWindows用ゲーム。当時コアな格ゲーファンに熱い支持を受けた「ストリートファイター3」シリーズの、核となるシステムである「ブロッキング」を練習しようという大義名分で制作されている。
ブロッキングは「相手の攻撃のタイミングに合わせてレバーを前に入れると、攻撃をノーダメージで受け流せる」という非常にアグレッシブなシステムだったものの、ゲーム中で練習できるタイミングがボーナスステージ(ショーンが投げるバスケットボールをブロッキング)しかなかったため、特に初代の時期はなかなか定着しなかった。僕たちもこのブロッキングシステムの敷居が高すぎて(なにしろガードとは全く反対のレバー入力が必要なので)試合中に手さぐりで試す他なかったんだけど、これを簡易的に練習できたらいいよねという話の流れから、NEXTのプログラム担当サガヲさんがほぼ一晩で作ったミニゲームだった。なお、作中で聴ける声と歌はすべてサガヲさんのもの。

3.Web版制作にあたって

OQRはミニゲームとして優秀で、キーひとつで操作できてそこそこ遊べることと、初見のインパクトが非常に大きいことから、思ってた以上に覚えている人が未だに多かった。僕はこの制作現場を後ろで眺めてたりと思い入れもあり、システムがシンプルなので複雑な処理も要らないだろうという予想の元「Javascriptの勉強の一環でこれを再現するのも面白いかな」という流れで着手。
また、Web版にすることで追加要素に「スマートフォンのパネルタッチで遊べる」って機能を実装したいなとも思っていた。ボタン一つだけで遊べるんだから絶対スマホとの相性も良いだろう。これまでWinでしか動かなかったことを考えると、ブラウザさえ起動すれば大体のデバイスで遊べる可能性がある方がやる意義があるかなと。
せっかくやるならJava + AndroidStudioでandroidアプリ化は?とも思ったんだけど、なにしろこのゲームが愛されてる要因の一つである「サガヲさんの不思議な歌」がネックでねえ・・・これを外すと魅力半減だろうということで見送り。歌外してキャラに目線入れたらイケるかな。

4.仕様など

ゲームの仕様の策定について、通常の作業なら元のゲームのソースを見て判断すればいいんだけど、今回は「手元にバイナリファイル(.exe)しかない」+「作者が既に亡くなっている」ということで、作業開始以前の段階で完全再現は不可能であることが前提。なので、ゲームを実際に遊んでおおよそのアタリをつけることと、当時のNEXTメンバーに製作途中のものを確認してもらいつつ、なるたけ本家に近づけるように調整していく。
・本家から継承
操作は1ボタン(キーボード右キー)
ライフ制で無くなったらゲームオバー
画面右のドロンパがボールを投げ、これをQ太郎がブロッキング
謎の歌

・カンで調整が必要なところ
ボールの軌道:ドロンパの手元からQ太郎の頭上へ放物線を描いて投げられる。ボールの始点と終点は必ず同じ位置。スピードと高さはランダム。
当たり判定:ボールとQ太郎が重なったらダメージなんだけど、後述するブロッキングの仕様により、多少食い込んでもOKなくらい当たり判定は下にずらしている。
ブロッキング判定:ブロッキングはキーを押すと構え、数フレームで構え解除、構え中にボールとブロッキングの手とが重なったときにブロッキング発生し、ボールは弾き飛ばされて点数とコンボ数が加算される。ブロッキングの手のほうの当たり判定は見た目より縦が狭い(ボールが若干広く取られている)
スコア計算:詳しい点数計算方法が解らなかったので、遊んでみてのおおよそで「基礎点数1,000 + (継続コンボ数 × ひきつけてブロッキングボーナス)」というかんじ。ボーナスはブロッキング可能な地点からやられ判定までの距離が短いと点数が上がるしかけ。

・新要素
オンラインで遊べる(ブラウザゲーム)
スマホ対応(タッチ操作可)

5.制作中に悩んだ箇所

5-1 ボールの軌道

ドロンパが投げるのはバスケットボール等、ドロンパとQ太郎の間には樽があって、ボールが樽を超えて放物線を描いてQ太郎に投げつけられるというのがボールの挙動なんだけど、enchant.jsに付属のプラグインにあるbox2dで物理エンジンくっつけられるとはいえ後ほど挙げる条件を満たすためにそれ使うのはなかなかに骨が折れる、ということでこの動きに関しては自分でなんとか処理しちゃう。本家の動きを参考にしつつ、ざっくりそれっぽく見えるようにするポイントを考えた。
x軸の動き:スピードは乱数で決め、一定速度で動く
y軸の動き:上限となる高さを決め、頂点はQ太郎とドロンパの中間点とする
始点と終点:常に始点はドロンパの手、終点はQ太郎のブロッキングモーションと重なる位置
やっかいなのはy軸の動き。いわゆる「弧を描いて」なので、始点終点と頂点とでは増減の幅を可変させないといけない。始点から頂点までは始点に近い方が、頂点から終点までは終点に近い方がy軸の増減幅が大きくなる。さらにx軸に関してはボールがどの高さで投げられたとしても投げる位置と落ちる位置を統一しないといけないという縛りがある(これがあるんでbox2dはとりあえずおいといた)
しっかし放物線の計算なんてこんなことするまで考えもしなかったんで、Google様と己の遠い過去の記憶を総動員して2日くらいうろうろした末に思いついたのは「y=x^a」みたいなやつ。xに対してa乗するみたいなのあったなー。ということでプログラムに組み込むにあたっては以下の点をおさえて考えた。
(1) べき数は乱数で決める
(2) ドロンパ~Q太郎の中間でボールが頂点にくるよう投げさせる
(3) 頂点の上限下限を決める(高すぎて画面外に出たり低すぎて樽に被らないように)
(4) y軸の値の増減は画面の上辺が0であることを忘れない
単純に放物線の計算をすると計算上放物線の頂点はxy共に0になったときということになるので、そのまま計算すると「ボールの頂点が一定の場所」((4)でわざわざ書いたのは時々ここで混乱するので自戒のため)
べき数をa、頂点の上限をy=16、下限をy=96、中間がx=80だとすると、ボールが中間にきたときに最も高い軌道が「16=80^a」、低い軌道が「64=80^a」になるようにべき数を設定する。とはいえぴったりその数字にする必要はないんで、上限1.056、下限1.036くらいで設定しといた。
そして実際の計算では、投げる前に中間に到達したときの頂点の高さを計算しておき、頂点と現在地の差を加えることで始点と終点の高さを同一にさせている。まあこれでゲーム的には問題なさそうだぞ。
// 放物線の軌道を計算する冪数
var ball_height = (Math.floor(Math.random() * (1050 + 1 - 1041)) + 1041) / 1000;
// 放物線を押し下げボールの始点を同一にするための係数
var height_max = 112 - Math.floor(Math.pow(ball_height, 88));
// 放物線の計算(縦軸のみ)
this.y = height_max + (Math.ceil(Math.pow(ball_height, Math.abs(this.x - 136))));
(この方法で軌道計算すると、横軸のスピードが一定なのでボールの軌道の頂点が高いほど落下速度が早く見えるという難点があるんだけど今回は無視したよ!だってそこまで計算するとめんどくせえし!)

5-2 ボールの管理

ボールの管理については当初「配列にボールの番号を格納して、ブロッキングされたものから外していく」という方法で処理していたんだけど、これだと低確率だけどブロッキングしてないボールまで弾かれるというバグに悩まされていた。で、改めてenchant.jsのアーカイブに同梱されていたサンプルを読み直してみると、シューティングゲームにおける敵弾の管理方法があったので解読してみると
(1) enchant.Class.createで敵弾の処理をクラス化する(おおまかな記述法は後述)
(2) scene側では敵弾の情報をいれる配列を用意
(3) 生成するとき、その時点での経過フレーム数(game.frame)を配列番号にする
という方法で処理していた。まじかよ。詳しくはexamples\expert\shooting\main.jsを参照なんだけど、そんなでっかい配列番号でデータを格納するっていう考え方は無かったな。まあ仮に30分ノーミスでゲームが続行できたとしても総フレーム数は108,000なので、そのくらいならなんとかなるのか。
このゲームでのボールの挙動は「ドロンパが投げる放物線」「ブロッキングされたあと前斜めに落ちる」「ダメージ時に消える」の3つ。ボールの状態を示す変数を用意しとけばそれぞれの状況に対応できるんで、それほど悩まなかった。
□ Spriteでクラスを作るときの基本的な記述方法
<クラスの作成>
var クラス名 = enchant.Class.create (enchant.Sprite, {
initialize: function(引数){
enchant.Sprite.call(this,スプライトのx,スプライトのy);
(プログラムの記述)
}
});
<呼び出し方>
var 処理用変数 = new クラス名(引数);
そもそもJavaScriptはクラスという概念自体が無いので、この記述方法はenchant.js特有のもの(代用でいろいろできるみたいだけど割愛) 上記のクラス作成ではenchant.jsのSpriteを継承している、という形なのだそうな。画像としてクラスを作成しますよーということですな。継承は、継承親のパラメータを引き継ぎますよというような意味で、この場合Spriteなのでimage・x・y・frameが指定できる。呼び出し側で引数にxとyとその他の引数を指定したら、initializeのfunctionで受ければクラス内でこれを利用できる。
また、似たようなクラスが存在するときはSpriteではなくそのクラス名をそのまま継承することも可能だそうな(enchant.jsのアーカイブ内、examples\expertShooting\main.jsを参照。shootってクラスを確認してみて。) ほんでSprite以外にもいろんな設定があるみたいだけどどうやって使うんだろう・・・。
この書き方が良いのは、先述したボールなど同じ処理を行うときにこの方法を使うと管理が大変楽だということ。イベントリスナーを使うときは通常「シーン名.addEventListener」と記述するけど、クラス内で記述するときは「this.addEventListener」とするとイベントリスナー処理内でもthisで対象クラスを指定できるよ。

5-3 ブロッキングの判定

Q太郎のブロッキングは「マウスクリックor画面タップで構え状態→数フレームで構えを戻す」
「構え状態のときにボールと重なったときブロッキング発動」「ブロッキングされたボールは弾かれ斜め前に飛んでいく」という判定。練習用にしたわりに攻撃に併せてレバー前(ブロッキング構えは存在しない)という本家スト3のブロッキングとは異なる挙動だけどそこはご愛嬌。あと本家はブロッキング成功時に3~5フレームくらい?動作が全て止まるので、コンボ等の連続攻撃への待機時間があるんだけどこれも省かれている。これと下段ブロッキングも含めてそのうち別ゲームにしたいねえ(今更やる理由もないんだけど・・・)
また点数に関しては「基本点+コンボボーナス」「Q太郎に近い位置でブロッキングするとさらにボーナス」という仕様っぽい。なのでQ太郎に多少食い込んでいてもOKにしておく必要がある。ゲームで使用する絵は背景色を抜いて画面に貼り付けるけど、その絵をそのまま当たり判定に使うと体に触れてないのにダメージを食らうってことになるので調整が必要。
で、検索すると「絵とは別にSpriteを用意して、imageを使わずに判定だけ置く」という方法があったのでこれを利用した。ボールはほぼ見たままなので絵を当り判定に利用。Q太郎側は腕の内側あたりにブロッキング判定を付与しておく(具体的な記述方法のサンプルは後ほどオブジェクトの項を参考) この当たり判定用のSprite、せっかく透明なのにブロッキングするたび表示/非表示を繰り返すのも無駄なんで、当り判定用のSpriteはremoveChildせず表示しっぱなしにしておいて、ブロッキング構えのときにボールが重なってるかで判定とする。比較的軽めに処理ができて助かった。

5-4 キー入力と分岐

今回のenchant.js版はwebで遊べる仕様なので、スマホ対応のためにタッチ(マウスで画面をクリックまたはスマホで画面タップ)で入力ができるようにしている。だけど過去作のプレイヤーや、わりと本気で遊びたいというプレイヤーのために、本家の仕様どおりカーソルキーの右でもブロッキングができるようにしてある。そうなると一応「キーボードとマウス(タッチ)のどちらかor両方が入力されてるときはどうするか」という処理まで考えなければいけなくなる。
加えて、先述のブロッキングは構えモーションがあり、さらにブロッキング成功時にブロッキング時のモーションと当ったときのエフェクトも表示する必要がある。じゃあブロッキング成功時のモーション表示時に再度キー入力があったら?例えば右キーでブロッキング成功した直後に右キー押しっぱなしの状態でマウスクリックがあったら? もしかするとゲームシステム上あんまりやってほしくない入力ができそうなので、そういうのは事前に可能性を洗い出して潰しちゃおう。

実装したい処理:
・右キーまたはタッチでブロッキング構え状態
・キーを話すと構え解除
・キーを押しっぱなしにしていると20フレームめで構え解除
・構え解除後は押しっぱなしにしていてもブロッキング判定は無い
・構え状態でボールが当たるとブロッキング成功
・ブロッキング成功すると、ブロッキング成功モーションに移行しヒットマークが表示される
・成功モーションは5フレームで解除
・成功したあともキーが押しっぱなしのときは構え解除後と同じく判定無し
・二つ当時に押されても判定は一回のみ
・キー入力後にもう一方のキーが入力されても、先に入力されたキーを基準に20フレームカウントする

入力パターン:
・マウスクリックまたは右キー入力があるとき+両方とも入力があるとき
→押しっぱなしの状態でもう一方のキーが後から入力されたとき
→両方押されている状態でどちから一方のキーが先に離されたとき
→ブロッキングが成功したとき
→ブロッキング成功後も押しっぱなしの状態のとき
→ブロッキング成功後も押しっぱなしでもう一方のキーが押されたとき
→ブロッキング成功後、成功モーション中にボタンが離され再度入力があったとき
・何も入力が無いとき
enchant.jsにおけるキーボード側の入力はGameのプロパティで真偽で扱う(右キー入力だとgame.input.right、入力があればtrue、なければfalseを返す) マウス入力の判定はイベントリスナー(キー入力判定などをリアルタイムで判定するenchant.js用の命令)として扱うので、マウス入力の方は変数を作って真偽を返させるようにする。

//(0)処理に使用する変数を用意
var mouse_touch = false;
var flaginput = false;
var counter = 0;
//(1)フレームごとの処理開始
core.rootScene.addEventListener('enterframe',function(){
//(2)マウスの入力状態を確認
core.rootScene.addEventListener('touchstart',function(){
mouse_touch = true;
});
core.rootScene.addEventListener('touchend',function(){
mouse_touch = false;
});
//(3)入力の条件分岐
//(3-1)マウスまたはキーのいずれかが入力状態のとき
if (core.input.right || mouse_touch) {
//(3-1-1)入力フラグがfalse(先に押しっぱなしが無い状態)で現在の画像フレームが0のときブロッキング構えの画像にする
if (flaginput == false && obq.frame == 0) {
obq.frame = 1;
}
//(3-1-2)入力フラグをtrue、カウンター開始
flaginput = true;
counter++;
//(3-2)何も入力がないとき
} else {
//(3-2-1)キー入力がない+カウンターは0以上=離した直後に画像を通常時に戻す
if (counter > 0){
obq.frame = 0;
}
//(3-2-2)入力フラグをfalse、カウンターを0に戻す
flaginput = false;
counter = 0;
}
//(4)構え状態で20フレーム以上経過したときは構え解除
//入力フラグとカウンターはそのままにしておく(押しっぱなし判定のため)
if (obq.frame == 1 && counter > 20) {
obq.frame = 0;
}
//(5)構え状態でボールが当たり判定に重なったらブロッキング成立
if (ball.intersect(obq_block) && obq.frame == 1) {
blocking();
//(6)構え状態でないとき(通常、ブロッキング成功画像)に当ったらダメージ判定
} else if (ball.intersect(obq) && obq_frame != 1) {
damage();
}
});

(1):addEventListenerの最初の指定値「enterframe」は、この枠内に書かれた命令は各フレームごとに処理してねという意味。ただし判定するのは一番手前のシーンなので、たとえばシーンを幾つか追加していて、ひとつ後ろのシーンにこの命令を送ってもそこではフレームごとの判定はされないので注意。
(2):addEventListenerの「touchstart」と「touchend」はどちらもマウス左ボタンまはたスマホの画面タッチに対応していて、enterframeの内外に関係なく処理してくれる。今回はこの中に、(0)で用意した「mouse_touch」という変数を入れておき、タッチ中はtrue、離すとfalseになるようにしている。
(3-1):タッチもしくはキー入力のいずれかが入力されている場合の処理。ちなみに条件式は「((A && B) || (B && C))」という表記でもOK。あとtrueであることを条件とするときは「== true」は省略できる。
(3-1-1):キー入力の状態はリアルタイムで「game.input」に格納される。inputの後ろにどのキーかを指定して確認するので、今回は右キーのみを使う(game.input.right) あと、(0)で用意した「flaginput」という変数は入力が2重に判定されることを防ぐ(先に入力がある状態でもう一方も入力、または両方入力されていて一方を離したあと再度入力など)ためのフラグにしていて、ここの処理では「先に押しっぱなしになってるキーは無い→fraginput == false」「画像が通常時になっている」の2つを確認したうえで構えをブロッキングに移行している。
(3-1-2):キー入力フラグはここでtrueにする(以降両方のキーを離すまでtrueのままにすれば入力判定の重複が回避できる) これに加えてcounterもカウント開始。
(3-2-1):どちらの入力もないときにカウンターがまだリセットされていなければ、この時点で画像を構えから元の状態に戻す。
(3-2-2):flaginputはfalseにして、カウンターはこの時点でリセット(ここでリセットすることで、カウンターが0の時は枚フレーム構え画像を0にされることを防ぐ)
(4):どちらかのキーが押しっぱなしのままの状態で20フレーム経過したら、自動的に構えを解除する。そうしないと「攻撃を受ける瞬間にレバー前」というスト3のブロッキング入力とはあまりにも違いすぎる内容になっちゃうもんね。
(5)(6):それぞれ、ブロッキング成功時の関数(function)呼び出しとダメージ時の関数呼び出しなので割愛。

5-5 点数とコンボ表示

本家での文字やスコアは昔かたぎの「1枚のビットマップにアルファベットと数字といくつかの記号が並べられてて文字に対応するフレーム番号で呼び出す」というものだった。最初に組み上げたときはほぼ手動で画像を呼び出してたんだけど、enchant,jsには「ui.enchant.js」という便利なプラグインがあって、テキストを入れると指定した画像から対応するフレームで呼び出してくれる「MutableText」って関数が用意されてる。これは楽だわー。
 利用するためにはいくつか準備が必要で、ui.enchant.jsをenchant.js同様htmlから呼び出しておくのと、文字や数字の絵のファイルを「font0.png」という名前で用意するか、またはui.enchant.js内の該当部分(今僕が持ってるものだと50行目と550行目にある「font0.png」)を指定したいファイル名に書き換えるかすると読み込みが可能。元のfont0.pngの配列を真似れば流用が可能なんで、自分で用意したフォントにしたい!という人はそれ用に文字用の画像入れ替えることもできるぞ。
 ところで、ui.enchant.jsの中でその関数の使い方が簡単に記載されてるんだけど、僕が確認した時点では記載が間違ってる(または誤解を招きやすい)表記なので、ここで改めて使い方を載せておく。
var text_a = new MutableText(0, 0); // 変数を用意
text_a.text = 'Hello, world!'; // 変数.textに文字列を入れる
game.rootScene.addChild(text_a); // シーンに追加
MutableTextはプロパティ.textに、その拡張機能であるScoreLabelはプロパティ.scoreにそれぞれ文字列や数字を入れるのだ。どうやらTimeもあるっぽいけど、このゲームでは使わないので割愛。ちなみにScoreLabelは得点をカウントアップして表示するようになってるので一度試してみてね。そしてリアルタイムで点数を表示させるときはちゃんとaddEventListenerの中にaddChildを書いて表示させていこう(enterframe内で毎フレームだと重くなるので、ifで点数が変化したときだけ描画するとか)
あ、念のためだけどui.enchant.jsはfont0.png以外にもいくつかの画像を呼び出しているので、利用するときはその他の画像も同じフォルダに入れてあげよう(でないとエラーになる)

5-6 オブジェクト化の積極利用

最初に書いたプログラムは、実験しながらどんどん書き足していったんで最終的に自分でも把握が面倒なスパゲッティ状態になっていた。なのでこれを解消するために改めてプログラムを書き直し、その際に各要素(Q太郎とかボールとか)をクラス化して管理しようということになった。そこでわかった(というかうっかり引っかかった)ことを適当にメモしていく。
□ 読みやすくて管理しやすい内容を目指す
 僕も全くの初心者なので、これまで書いてきたスクリプトは殆ど「メインの処理で全てを終わらせる」というタイプのものだった。ふつう、一人で書いててそれほど長くなければこの方法でも大したトラブルは起きないんだけど、僕の場合だとこのゲームのプログラムはいろんな要素を足せば足すほど長くなっちゃうんで、後でトラブルに気付いたときに原因が特定しづらくなってた。なので改めて書き直してるんだけど。
 先日、現役のwebプログラマにこのゲームの話をしたら「オブジェクトを利用したらもっと単純化できるでしょ」とのこと。プログラムにおけるオブジェクトっていうと「オブジェクト指向」のことだよなあ、しかし具体的にどう扱うのが効率的なのかよくわかってないんだよね・・・と相談すると、とりあえずてっとり早く使えるテクニックを教えてくれた。いやー餅は餅屋だね!
□ オブジェクトを利用して管理しやすく
 いろんな処理を沢山追加していくと、完成間近になってきたときに「どこでどう間違ってるんだ?」という状況になりがち。僕みたくズボラな奴だと、バグの起こるポイントでは「想定した返り値でないので処理がおかしくなってる」というケースが結構多くて、一度に行う処理が多ければ多いほどその罠にハマっていく感がある。
 なのでゲームの場合、変数は最初の設定の段階で「画面に表示されたモノごとに作る」という法則を決めて設定すると間違えにくいかも、ということは感じた。具体的に言うと、Q太郎用に用意した変数を「obq」としたときに、関連する変数を個別につくるより、一つオブジェクトを作ってそのプロパティとして関連するものを付け足す方がいいだろう、ということ。
 ところでオブジェクトってどうやって作るの?という話なんだけど、基本的に(めちゃくちゃ雑な言い方だけど)「ひとつの入れ物に対していろんな値が含まれているもの」は総じてオブジェクトと考えていいみたい enchant.jsならSpriteもオブジェクトになってる。そして関数もオブジェクトのひとつだ。
<当初やってた書き方>
//Q太郎のスプライト
var obq = new Sprite(96,48);
obq.x = 16;
obq.y = 112;
obq.image = game.assets['obq_g.jpg'];
scene.addChild(obq);
//Q太郎のブロッキング判定のスプライト
var obq_blocking = new Sprite(32,16);
obq_blocking.x = 16;
obq_blocking.y = 128;
//ブロッキング判定は画像指定無し
scene.addChild(obq_blicking);
//体力カウンター
var obq_life = 3;
<書き直ししたとき>
//Q太郎関連の関数設定
var Obq = function() {
//太郎のスプライト
this.obq = new Sprite(96,48);
this.obq.x = 16;
this.obq.y = 112;
this.obq.image = game.assets['obq_g.jpg'];
scene.addChild(this.obq);
//Q太郎のブロッキング判定のスプライト
this.obq_blocking = new Sprite(32,16);
this.obq_blocking.x = 16;
this.obq_blocking.y = 128;
//ブロッキング判定は画像指定無し
scene.addChild(this.blicking);
//体力カウンター
this.life = 3;
}
//変数として扱えるようにする
var obq = new Obq();
<関数に引数として渡すとき>
//当初の書き方
motion(obq,obq_blocking,obq_life);
//書き直した方
motion(obq);
これでわかるかなあ。この例では、以前はQ太郎のスプライト・ブロッキング判定・残りライフで3つ変数を作ってたんだけど、これを全てobqというオブジェクトのプロパティ(.と名前をオブジェクトの後ろにくっつけてぶら下げる)で設定することにした。これによって変数をまとめて管理できるので引数の指定もめちゃくちゃ楽だし抜けも少なくなる。作業終盤になってくると色んな数値やフラグを扱わないといけなくなるんでバグも発生しやすいことを考えると、値の渡し忘れみたいな凡ミスで悩まされる心配が軽減されるのよ。プロパティを追加するときは変数の宣言(var)も付けずに書いてしまってOK。あと上の例でもあるように、オブジェクトのプロパティをオブジェクトにできる。
 もう一つ大きな利点は、関数に引数として渡されるのが通常の変数の場合、関数内で起こった変数の中身の変更は関数内で戻り値(return)として指定しないと元の変数には反映されないんだけど、オブジェクトは特に指定しなくても関数内での変化を保持しておけるので(えーと、プロトタイプのコンストラクタを覚えてるとかっていうやつだっけ)ゲームのように一気にいろんな部分が時間と共に変化する場合はオブジェクトに収めておいたほうが使いやすい!ということになる。
 また、プロパティでぶら下げるという形をとることで、後で追加で判定したい項目が増えたときなども関連するオブジェクトのプロパティとして追加すれば引数の中に自動的に含まれることとなるので、劇的に管理が楽になるのですよ。例えば先ほどの例で、ゲームオーバーになったときのQ太郎が画面外に落ちる処理のときの速度が必要なときは「obq.speed」みたいなプロパティを追加すると関数にまとめて渡してもらえるという寸法。いやー現役エンジニアの教えは尊いですな。唯一難点があるとすれば、沢山の項目を一つにまとめようとすると、どうしても名前が長くなっちゃうってことね。工夫次第でどうとでもなるけど、可読性も考慮して設計しましょ。
 ・・・ちなみにenchant.jsではgroupという関数もあるみたいなんだけど今回は調べてないので無かったものとします。

6.その他

このゲームを作るにあたって一番役に立ったのはVisual Studio Codeかもしれない。なんだかんだで入力補助やfunction化はとっても便利。ちょいちょいやっちゃうカッコの閉じ忘れとかもちゃんと換地してくれるし、タブによる階層構造のを可視化してくれるので管理も楽。複数ファイルをタブで管理し、2つのソースを横並びで参照することもできるのはよかったな。これと併せてGoogleChromeのデバッグツール(F12押すとでてくるやつ)には動作に問題があったときにエラーメッセージが表示されるので、この2つを組み合わせて作業している。
 プログラムの知識に関しては、すでに1度JavaScriptを少し触ってたということもあるんだけど、おおよそはenchant.jsに付属しているサンプルとネット検索でなんとかなった。最初からコレを作りたい!という明確な目標があるときは、本とか読むより目的に併せて解決方法をネットで確認したほうが早いっちゃ早い。で、どうしても詰まるところはそもそも基礎がよくわかってないところなので、そのときに文献をゆっくり読むっていう感じで充分作れるなーと思う。これもしかしたらJavaScript殆ど知らない段階で始めてもカンがよければサクサクとゲーム作れてしまうんじゃなかろうか。
 ただ、僕がド素人の状態から短期間で一気に作り上げられたのは、そもそも既にあったゲームを移植するという行為だったからに他ならない。まあ元のゲームのソースが閲覧できなかったんで完全移植とは口が裂けても言えないわけだけど、おおよその処理方法は本物のほうを参照すれば類推できるんで、何もない状態から作業を始めるのに比べれば何倍も早いんだろうなとは思う。つまりこれ逆に言えば、1ボタンでできる簡単なゲームを自分なりに移植するんだ!というスタートでプログラムを勉強するほうが効率は高いかもしれないってことだよね。1本ゲームを作ってみると、この言語や環境で出来ることや難しそうなことが見えてくるから、まずはお手本を参考に1つ作ってみるっていうのは大事かも。そういや僕も曲作り始めたとき、最初にやったのは人の曲のコピーだったな。
 というわけでメモは以上。このゲームを、制作に協力してくれたNEXTのメンバーの皆さんと作者であるサガヲさんに感謝を込めて捧げます。さあ次はアレやっぞ!