Osaki.js 第一回目は @hidesuke によるハンズオンでした。
(当日スライドは前回ブログをご参照ください)
提示された課題は3点ありました。
- クマが画面の端についたら、折り返すようにしてみましょう
- タップした方向に向かって、クマが進むようにしましょう
- なんかゲームっぽくしてみましょう
ハンズオンのあと1時間ぐらいを使って、1と2まではさっくりと実装できました。
私(@inuwarumono)の書いたものがこれ
動作サンプル
ソースコード(github)
書いたものを振り返ってみようと思います。ちなみに僕はPerl/Shellスクリプトを少々書くぐらいで、JavaScriptをほとんど書いたことがありません。かなり基本的なことまで記載しますので、冗長に感じられる方はすみませんが少しご辛抱下さい。
1. クマが画面の端についたら、折り返すようにしてみましょう
くまの移動は、毎フレームごとに呼ばれる関数の中で、bear.x, bear.y を変更することで実装することに、というところまではテンプレートにかかれているとおりです。(テンプレートでは単純に this.x += 3; するだけ)
その増分を、bearオブジェクトのプロパティとして定義してあげることにしました。
bear.dx = 2; // くま移動量のX成分初期値 bear.dy = 1; // くま移動量のY成分初期値 bear.move = 2; // 1フレームあたりの移動距離
X軸方向に +2 (=右に2移動), Y軸方向に +1 (=下に1移動) というのが初期値です。 X軸,Y軸の移動量を制御することで、くまの移動方向をコントロールします。
壁で折り返すには、いわゆる「あたり判定」を書けば良いですね。
if (this.x > game.width - bear.width || this.x < 0) { this.dx *= -1; // くま移動のX成分反転 this.scaleX *= -1; // くま画像のX軸反転 }; if (this.y > game.height - bear.height || this.y < 0) { this.dy *= -1; // くま移動のY成分反転 };
条件式の部分はX軸,Y軸とも「くまが画面の端から出そうになったら」という意味ですね。
(普通 this.x < 0 を先に書く気がするけど気にすんな。)
条件にマッチした時にやっている事は、くまの移動量の±反転で、右に2移動していたものを、左に2移動するように変更する、ということです。
昔アルカノイドというブロックくずしのゲームがありましたが、あのゲームでは反転時に微妙に方向が変わるようにランダムな要素を混入させていましたね。それを書こうとすると、±判定に加えて、ランダム要素を追加すると…と、これは余談。
次に行きましょう。
くまの移動は、1フレーム毎に bear.x, bear.y の値を操作していて
// くま移動 this.x += bear.move * this.dx; this.y += bear.move * this.dy;
ここで bear.moveは一回の移動距離で、要するにスピードですね。
(ちなみに this とか bear とか使い分けがうまく出来てないのは僕が初心者だからです。)
増分に、スピード要素を掛け算したものを、this(=bear).x に足しています。
ちなみに、X += 1; というのは、X = X + 1; と同義で、他にも掛け算ならば X *= 2; のように書けます。
この書き方は、JavaScript に限らずその他たいていの言語でも同様なので、慣れておきましょう。
(+=, -=, *= はわりと書くことがあるけど /= とは書いたことないです。%=とも書けるけど、これも書いたことない。)
2. タップした方向に向かって、クマが進むようにしましょう
いきなり書いたコードをはります。
this.rootScene.addEventListener(Event.TOUCH_START, function(e){ // クリック位置がくまの中心にくるように位置の差を取る var _dx = (e.x - bear.width / 2 ) - bear.x; var _dy = (e.y - bear.height / 2 )- bear.y; // 1フレームあたりの移動距離がbear.moveになるように var _dd = Math.sqrt(_dx * _dx + _dy * _dy); bear.dx = bear.move / _dd * _dx; bear.dy = bear.move / _dd * _dy; // X成分の正負をそのままに値を1にしてscaleX設定に使う bear.scaleX = bear.dx / Math.abs(bear.dx); });
_dx, _dy は、その場限りの変数なので、_で始まる変数を書いてみました。これは何流かは良くわかりませんが、僕はこうやって書くことが多いです。
三平方の定理を使って、タップ位置と、クマの現在位置の距離を _dd に取り、X,Yそれぞれの必要移動距離に一回の移動距離との比率(bear.move / _dd)を掛け算しています。
こうすることで、X,Y方向の一回の移動距離が、結果として bear.move になるように調整しているわけです。
また、bear.scaleX は右方向移動(dxがプラスの場合)には 1 を、左方向移動(dxがマイナスの場合)には -1 を入れたいので、絶対値で割り算をすることで結果が 1 または -1 になるように細工しています。
これは簡潔にうまく書けたなー、と満足。
3. なんかゲームっぽくしてみましょう
ここまでのコードで、反転もうまく出来たし、クマはタップした方向にちゃんと動いてくれました。なので、結果は満たしたと言えますね。
しかしこれを、「ゲームっぽくする」というと話はまったく別でした。
ゲームを作るために、実装が先にあってはゲームになりません。というのも、単純な移動といっても実装方法はいくつかあって
- どっちにどれだけ動くか(僕の実装)
- どこに向かって動くか(僕の実装では表現できていない)
こんな違いがあります。
たとえばシューティングゲームの自機をクリックしたとこに移動させるようなコードを書こうとすると、僕の書いたコードではたいへん問題があります(移動量しか見てないから)。
「オブジェクトを動かす」なんていう単純な実装一つにしても、実装手段がゲーム性に密接に関連している事に気がついた次第です。
動かすにはどうしたら良いかではなくて、どう動かしたいかを先に考えること、これがゲームを作るということなんでしょう。これには企画者の気持ちがないと作れませんね。ゲーム作りが楽しいのはこういうところに起因していそうです。
次回のOsaki.jsでは、「実際にゲームを作ってみましょう」という予定になっています。
先にどんなゲームを作ろうかアイディアを練ってから参加しようと思います。