enchant.jsのサンプルコードを解読する(RPG編その3)

SPONSORED LINK

Pocket

その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();」ってなんだ?

ツイッターもやっています!

SPONSORED LINK

    • chick307
    • April 24th, 2011

    arguments.callee();
    だと
    arguments.callee.call(arguments);
    と同じ意味になってしまうはずです。

    • taguchi
    • April 24th, 2011

    おぉ、そうなのですね・・・が、やはりここらへんは理解が・・・。そもそもなぜこの一文があるのでしょうか・・・。おわかりなら教えてください!

    • chick307
    • April 24th, 2011

    >「arguments.callee.call(this);」は呼び出し元の関数を呼ぶ決まり文句
    と書いてありますが、arguments.callee は自分自身です。
    (呼び出し元は arguments.caller)

    フレームごとの処理(enterframeイベント)が、
    「移動フラグ立てる→移動し始める→移動中→動き終わる→移動フラグ立てる→・・・」
    だと動き始めるまでに1フレームの間プレイヤーが止まったままで、動きがもたつくので
    「移動フラグ立てて移動し始める→移動中→移動終了→移動フラグ立てて移動し始める→・・・」
    のように移動フラグを立てたあとすぐに動き始めるようにしているのだと思います。

    試しにコメントアウトしてみると動きがスムーズじゃ無くなるのがわかると思います。

  1. ・衝突判定のxを求めるときに最後に+8するのはなぜ?なくてもよくない?
    →これは多分、バグ対策じゃ無いかと思います。キャラクターを一番上まで移動すると頭が切れている事が分かると思います。背景も含めて -8 の位置から描画が行われている様です。その分の差を補正する為に入っているのだと思います。

    ・「arguments.callee.call(this);」ってなんであるの?
    →arguments.callee.call(this); はそのフレームの間に歩くアニメーションを開始しておきたいからでは無いでしょうか?

    この部分が無いとアニメーションの開始が1フレーム遅れた次のフレームから開始となります。

    ・this.walkを計算するところって「this.walk = game.frame % 3;」でもよくない?
    →これはアニメーションがせわしなくなるのを防ぐという理由で合っていると思います。

    ・途中にある「var n = Math.random();」ってなんだ?
    →これは多分、デバッグか何かで使ってたものの消し忘れだと思います。

    • taguchi
    • April 25th, 2011

    な、なるほど・・・よくわかりました!ありがとうございます!

    • taguchi
    • April 25th, 2011

    もろもろありがとうございます!勉強になりました・・・。

    それから、

    > ・衝突判定のxを求めるときに最後に+8するのはなぜ?なくてもよくない?
    > →これは多分、バグ対策じゃ無いかと思います。キャラクターを一番上まで移動すると頭が切れている事が分かると思いま> す。背景も含めて -8 の位置から描画が行われている様です。その分の差を補正する為に入っているのだと思います。

    ですが、これはy方向?!y方向に8px+しているのはわかるのですが、x方向に8pxずらす意味がよくわからず・・・。僕の理解不足かもしれませんが・・・もし可能でしたら教えていただけるとうれしいです!

  2. >ですが、これはy方向?!y方向に8px+しているのはわかるのですが、x方向に8pxずらす意味がよくわからず・・・。僕の理解不足かもしれませんが・・・もし可能でしたら教えていただけるとうれしいです!

    これは恐らく、

    キャラがy方向に-8の位置から描画している事に気づかずに壁をすり抜ける症状が発生

    対処療法的にy方向を+8してみた所、上手く行った

    念のため、x方向にも+8しておいた

    という展開ではないかと思うので特に気にしないで良いかと思います。

    • taguchi
    • April 25th, 2011

    おぉ、なるほどw。ありがとうございます!すっきりしました!今度これを作った人に聞いてみますw

  1. No trackbacks yet.