JavaScript/ゲームを作る/横スクロールアクション/3.背景の描画
ゴール
マリオでいう「ブロック」とそうでない所を描画してする。
当たり判定は次回。
今回の完成コード
コードの説明
Mapオブジェクトの作成
例によって、[[Javaでゲーム作りますが何か?>http://javagame.skr.jp/]]を大部分参照。これがなかったらかかる時間がかなり違ったな。
まず、マップもオブジェクト化してみた。
グローバル変数を追加して、
初期化処理でオブジェクト生成。
// タイルの幅
tileWidth:32,
// 背景。1がブロック、0が移動可能領域。
mapTable:[
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1],
[1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
],
// マップの描画
drawMap:function(){
var div = document.createElement("div");
div.id = "map";
div.style.position="absolute";
div.style.zIndex="1";
for(var i = 0; i < this.mapTable.length; i++){
for(var j = 0; j < this.mapTable[i].length; j++){
var masu = document.createElement("div");
masu.style.width = map.tileWidth + "px";
masu.style.height = map.tileWidth + "px";
masu.style.styleFloat = "left";
masu.style.cssFloat = "left";
masu.style.align = "left";
switch(this.mapTable[i][j]){
case 0:
masu.style.backgroundColor="white";
break;
case 1:
masu.style.backgroundColor="brown";
break;
}
div.appendChild(masu);
}
var br = document.createElement("br");
br.style.clear="both";
div.appendChild(br);
}
document.getElementById("canvas").appendChild(div);
}
};
英訳調べるのを端折ったため、マップの1マスを表す変数名が、「masu」になっていてとてもかっこ悪い…。
最後に描画
z-index属性の記述
画面上で、マップとキャラクターは重なって表示されるが、重なりの順序があるらしく、最初マップしか表示されなかった。(キャラクターはマップの裏側に描画されていた。)
調べてみたところ、CSSでの重なり順序はz-index属性(JavaScriptで操作する場合はzIndex)で指定するようだ。
というわけで、キャラクター・マップ共に、z-indexを指定してみた。
マップ
キャラクター
style="z-index:99;position:absolute;background:gray;width:32px;height:32px;" />
みごと完成。
ふたたび今回の完成コード
まとめ
今回は、単純なDOM操作で要素いっぱい書いておしまいなので、とても簡単でした。
補足・疑問
マップもキャラクターもオブジェクト化(?)してて、初期化処理内で構造書いてるけど、怪しさがぬぐい切れません。
何が怪しいのかもさっぱりわからん。
とりあえず、Div要素を内包してるのはいいんだろうか、悪いんだろうか。
JavaScript/ゲームを作る/横スクロールアクション/4.衝突判定
ゴール
キャラクターがブロックに重ならないようにする(上下左右)
今回の完成コード
コードの説明
丸投げの話
当たり判定はをいろいろ書いて試してみたんですが、キャラがいきなり加速したりいなくなったりワープしたりカオスな結果にしかならなかったため、
最終的に[[Javaでゲーム作りますが何か?>http://javagame.skr.jp/]]から丸ごと写すという結果に…。
ああ、とても残念だ。でもいろいろやってみて楽しかったなー。惜しかったなー。
というわけで、今回追加されたコードはほぼコピペですよ、と。
動きとしては今のところバッチリ(当たり前)なので、前向きにヨシとすることにしよう。ありがとうございました!
地面判定の変更とブロック判定ロジックの実装
前回までのコードではjimenという変数をY座標の一番下の位置として利用していたが、ブロックの当たり判定を利用するにあたってこれを廃止。
まずは、渡されたX,Y座標に対する当たり判定を行うメソッドを、マップオブジェクトに実装します。
// 省略
,
// X,Y座標に向かう時に衝突するかどうか。
// 衝突するとき:衝突座標が格納されたPointオブジェクトを返す
// 衝突しないとき:nullを返す
getTileCollision:function(newX, newY){
var fromX = Math.min(character.x, newX);
var fromY = Math.min(character.y, newY);
var toX = Math.max(character.x, newX);
var toY = Math.max(character.y, newY);
var fromTileX = this.pixelsToTiles(fromX);
var fromTileY = this.pixelsToTiles(fromY);
var toTileX = this.pixelsToTiles(toX + map.tileWidth – 1);
var toTileY = this.pixelsToTiles(toY + map.tileWidth – 1);
for (var x = fromTileX; x <= toTileX; x++) {
for (var y = fromTileY; y <= toTileY; y++) {
// 画面外は衝突
if (x < 0 || x >= 20) {
return new this.Point(x, y);
}
if (y < 0 || y >= 15) {
return new Point(x, y);
}
// ブロックがあったら衝突
if (map.mapTable[y][x] == 1) {
return new this.Point(x, y);
}
}
}
return null;
}
,
// 座標を表すクラス
Point:function(x,y){
this.x = x;
this.y = y;
}
,
// 座標をタイル位置(配列の要素番号)に変換
pixelsToTiles:function(pixels){
return parseInt(pixels / map.tileWidth);
}
,
// タイル位置(配列の要素番号)を座標に変換
tilesToPixels:function(tiles){
return parseInt(tiles * map.tileWidth);
}
};
キャラクターとブロックの当たり(重なり)判定
キャラクターが次に進みたい横方向を求めて、その方向にブロックがあるか判定。ブロックがあればぶつかる位置以上は進ませない。
というロジックになっている。
これを、進行方向に対して一気に判定するのではなく、縦と横別々に判定することで単純化している。
ちなみにコメントも全部原文ママです。
// x方向の当たり判定
// 移動先座標を求める
var newX = character.x + character.vx;
// 移動先座標で衝突するタイルの位置を取得
// x方向だけ考えるのでy座標は変化しないと仮定
var tile = map.getTileCollision(newX, character.y);
if (tile == null) {
// 衝突するタイルがなければ移動
character.x = newX;
} else {
// 衝突するタイルがある場合
if (character.vx > 0) { // 右へ移動中なので右のブロックと衝突
// 位置調整
character.x = map.tilesToPixels(tile.x – 1);
} else { // 左へ移動中なので左のブロックと衝突
// ブロックにめりこむ or 隙間がないように位置調整
character.x = map.tilesToPixels(tile.x) + map.tileWidth;
}
character.vx = 0;
}
// y方向の当たり判定
// 移動先座標を求める
character.vy += character.gravity;
var newY = character.y + character.vy;
// 移動先座標で衝突するタイルの位置を取得
// y方向だけ考えるのでx座標は変化しないと仮定
var tile = map.getTileCollision(character.x, newY);
if (tile == null) {
// 衝突するタイルがなければ移動
character.y = newY;
//衝突してないということは空中
character.onGround = false;
} else {
// 衝突するタイルがある場合
if (character.vy > 0) { // 下へ移動中なので下のブロックと衝突(着地)
// 位置調整
character.y = map.tilesToPixels(tile.y) – map.tileWidth;
// 着地したのでy方向速度を0に
character.vy = 0;
// 着地
character.onGround = true;
} else { // 上へ移動中なので上のブロックと衝突(天井ごん!)
// 位置調整
character.y = map.tilesToPixels(tile.y + 1);
// 天井にぶつかったのでy方向速度を0に
character.vy = 0;
}
}
character.drawCharacter();
}
Good!おしまい!
ふたたび今回の完成コード
まとめ
今回の追加処理で、かなりイケてるプログラムになったように思う。
補足・疑問
いまのところ、あまりなし。
JavaScript/ゲームを作る/横スクロールアクション/5.ミニゲームとして仕上げ
ゴール
いままでの成果をもとに、スタートとゴールを設定してミニゲーム風に完成させてみる。
今回の完成コード
コードの説明
ちょっとあとで!
ふたたび今回の完成コード
まとめ
補足・疑問
できたはいいけど、殺風景だな。

