その1、その2に引き続きの第3弾。今回もプレイヤーの挙動を見ていきます。
では次のコードを見ていきましょう。
player.isMoving = false; // プレイヤーが動いているかどうかのフラグ。最初止まっているのでfalse
player.direction = 0; // プレイヤーの方向
player.walk = 1; // プレイヤーの歩行画像パターン
directionとwalkの組み合わせで前回紹介したプレイヤーの画像を特定しています。それらの関係については次のようになっていますよ。

↑ まぁ、わかりやすいですね。
初期値だとプレイヤーの方向が「0=下向き」、歩行画像パターンが「1」なので次のようになっています。

↑ ですね。
この関係を頭にたたき込んだ上で次のコードを見ていきましょう。
player.addEventListener('enterframe', function() { // フレーム毎の処理。
this.frame = this.direction * 3 + this.walk; // プレイヤー画像をdirectionとwalkで設定。
プレイヤーのframeは元画像とSprite設定したときのサイズによって次のように規定されています。

↑ またしても無駄に作り込んだぞ・・・w。
というわけで、direction*3でどの行を選ぶかが決まり、walkによってどの列が選ばれるかが決まるってわけですな。
さて次にいきますか。だんだん複雑になっていきますよ。
if (this.isMoving) { // プレイヤーが動いているとき
(ちょっと後回し)
} else { // プレイヤーが動いていないとき
this.vx = this.vy = 0; // x,y軸でどれだけ動くかの変数。まず0で初期化。
var n = Math.random(); // これなんだ? → 【追記】 消し忘れっぽい
if (game.input.left) { // 左矢印が押された!
this.direction = 1; // directionを1に設定(プレイヤーの画像設定に使う)
this.vx = -4; // 左へ4ピクセル移動しようぜ!
} else if (game.input.right) {
this.direction = 2;
this.vx = 4; // 右へ4ピクセル移動しようぜ!
} else if (game.input.up) {
this.direction = 3;
this.vy = -4;
} else if (game.input.down) {
this.direction = 0;
this.vy = 4;
}
(後述)
}
さてここではまずプレイヤーが動いている場合とそうでないかで処理が分岐します。
最初は「isMoving=false」なんで下の方からみていきましょう。上のコードを見ればわかりますが、まずはvx, vyを初期化し、キー入力によってdirectionとvx, vyの値を変えています。directionは上述したようにプレイヤーの画像を特定するのに必要ですね。
次にvxやらvyを使って移動後の座標を計算し、衝突判定をしています。
if (this.vx || this.vy) { // vxかvyが0じゃない場合=動こうとしている場合
var x = this.x + (this.vx ? this.vx / Math.abs(this.vx) * 16 : 0) + 8;
var y = this.y + (this.vy ? this.vy / Math.abs(this.vy) * 16 : 0) + 8;
if (0 <= x && x < map.width && 0 <= y && y < map.height && !map.hitTest(x, y)) {
this.isMoving = true;
arguments.callee.call(this);
}
}
ここらへんは実際の数値をあてはめてみるとわかりやすいかもです。
まず初期状態だとキャラクターのx, yはそれぞれ6*16, 10*16+8、mapの広さは480*480です。その状態から「右矢印」を押したと想定してみます。この場合、this.vxが4、this.vyが0になります。したがって・・・。
if (4 || 0) { // 4がtrueなので続行。
var x = (6*16) + (4 ? 4 / Math.abs(4) * 16 : 0) + 8; // 120 = (6*16) + 16 + 8 = 現在地から16ピクセル右にいって、さらに8ピクセル右。
var y = (10*16+8) + (0 ? 0 / Math.abs(0) * 16 : 0) + 8; // 176 = (10*16+8) + 0 + 8 = 現在地から8ピクセルだけ下。
if (0 <= 120 && 120 < 480 && 0 <= 176 && 176 < 480 && !map.hitTest(120, 176)) { // 衝突していないから続行
this.isMoving = true; // 動いているフラグを立てる
arguments.callee.call(this); // 呼び出し元関数を再度呼ぶ
}
}
大丈夫ですかね・・・プログラミング初心者だと必ずはまるw三項演算子はいいかな?「(this.vx ? A : B)」というのは、「this.vxがTrueだったらA、FalseだったらB」という意味です。
あと、ちょっとわかりづらいのが衝突判定をするマップが16×16であるのに対し、キャラクターが16×24である点ですね。y方向に必ず8が足されるのはそのせいですな(逆にいうとx方向に8足されるのはなんで?って気がしないでもない・・・問題はないですが → 【追記】 コメント欄参照のこと)。
ついでなのでちょいと図解してみました。右に行く場合ね。

↑ 図を作るのが面倒でついに手描きにw。
これ、上下左右でシミュレーションしてみるとわかりますが、必ず衝突判定したいマスの上部中央で判定しているようですね。左上で判定しても良さそうなものなのですが・・・詳しい人がいたら教えてください。
それから次の「arguments.callee.call(this);」は呼び出し元の関数を呼ぶ決まり文句ですね(「arguments.callee();」で良くなかったっけ?)。これ、なぜこうしているかというと・・・なんでだろw。ぶっちゃけわからないので詳しい人、教えてください・・・(汗)。 → 【追記】 コメント欄参照のこと。
そこは他力本願にしつつ、今度はちょっと前に戻ってisMovingがTrueだった場合のコードを見てみます。
if (this.isMoving) { // isMovingがTrueの場合
this.moveBy(this.vx, this.vy); vx, vyの分だけキャラを移動
if (!(game.frame % 3)) { // 現在のフレーム数を3で割ったあまりが0じゃない時=1か2のとき現在のフレーム数を3で割ったあまりが0のとき=3フレームに一回、ですな。
this.walk++; // walkを1増加
this.walk %= 3; // walkを3で割った余りに=0,1,2のどれかにする
}
if ((this.vx && this.x % 16 == 0) || (this.vy && (this.y-8) % 16 == 0)) { // 次のマスに移動しきったら
this.isMoving = false; // isMovingをFalseに
this.walk = 1; // walkを1に
}
}
ここの処理は前半と後半に分かれますね。
まずは前半でキャラの歩行画像パターンを切り替えています。フレーム毎にwalkを1増加させつつ、3で割ったあまりにすることで、walkのパターンを0,1,2に振り分けています。ただし個人的には「this.walk = game.frame % 3;」でいいんじゃね?と思ったり。なぜこうするんだろ。せわしない動きになるからかな・・・。 → 【追記】 コメント欄参照のこと。
後半は「キャラが次のマスに動いたら停止する」という処理をしていますね。ここでもマスの大きさとキャラの大きさが違うのでy方向では8ピクセル引いています。
ふー・・・プレイヤー部分についてはこんなところですかね。最後にパッドについてと、マップの移動についての記述をして終わりかな、っと。
なぜこうなる?
【追記】 コメント欄にてすべて解決しました!ご協力いただいた皆様、感謝感激です!
それから今回はよくわからない点が多かったので個人的に「???」だった点をまとめておきます。誰かわかる人がいたら教えてください・・・。
- 衝突判定のxを求めるときに最後に+8するのはなぜ?なくてもよくない?
- 「arguments.callee.call(this);」ってなんであるの?
- this.walkを計算するところって「this.walk = game.frame % 3;」でもよくない?
- 途中にある「var n = Math.random();」ってなんだ?