1. HOME»
  2. プログラミング・Web»
  3. JavaScript»
  4. 【JavaScript】マップでキャラを動かせるようにしよう! 小学生からのプログラミング入門

【JavaScript】マップでキャラを動かせるようにしよう! 小学生からのプログラミング入門

小学生からのプログラミング講座こうざ、今回もはじめていきましょう!

前回は、JavaScriptでのマップの作り方を紹介しょうかいしました。
しかし、まだマップを配置はいちしただけで、キャラクターの画像がぞう表示ひょうじさせていませんでした。

今回こんかいはマップ上にキャラクターの画像を表示させ、うごかせるようにしてみようと思います。
また、かべとの衝突しょうとつ判定はんていも作り、キャラクターが壁をすりけられないようにしてみようと思います。

アル

昨日きのうさ、前回おしえてもらったマップチップの表示と、りこちゃんの画像表示を自分で組み合わせて作ってみたんだ

りこ

うまくうごいた?

アル

それが全然(ぜんぜん)。一応表示(いちおうひょうじ)はされたんだけど、りこちゃんが壁(かべ)をすり抜けちゃうんだそれが全然ぜんぜん一応いちおう表示ひょうじはされたんだけど、りこちゃんがかべをすり抜けちゃうんだ

りこ

そうなんだ

山田

それが全然(ぜんぜん)。一応表示(いちおうひょうじ)はされたんだけど、りこちゃんが壁(かべ)をすり抜けちゃうんだじゃあ、今日きょうかべとの衝突しょうとつ判定はんていを作っていくべ!

りこ

山田先生! わーい!

アル

それが全然(ぜんぜん)。一応表示(いちおうひょうじ)はされたんだけど、りこちゃんが壁(かべ)をすり抜けちゃうんだはやくやろう!

目次
  1. マップにキャラクターを表示してみよう!
  2. 壁の座標とりこちゃんの座標を比べて、壁との当たり判定をする
  3. まとめ

マップにキャラクターを表示してみよう!

まずはマップにキャラクターを表示ひょうじさせるところから始めましょう!
第12回目紹介しょうかいした、HTML5とCanvasでキャラクターをうごかす方法ほうほうと、第13回目で紹介した、マップの表示方法を組み合わせて作ります。

まずはcanvasの設定せっていと、コンテキストを取得しゅとくです。

//canvasの設定(せってい)
var canvas = document.getElementById( 'canvas' );
canvas.width = 640;	//canvasの横幅(よこはば)
canvas.height = 640;	//canvasの縦幅(たてはば)
 
//コンテキストを取得(しゅとく)
var ctx = canvas.getContext( '2d' );
アル

ここまでで、Canvasをつかう準備じゅんびができたわけだね

りこ

Canvasを設定せっていして、コンテキスト(プログラムを動かすのに必要な情報)を取得しゅとく

そして今回必要ひつような、オブジェクトや変数へんすうです。
りこちゃんのオブジェクト、マップチップのImageオブジェクト、キーボードのオブジェクト、マップ配置はいち変数へんすうを作っておきます。

//りこちゃんのオブジェクトを作成
var rico = new Object();
rico.img = new Image();
rico.img.src = 'img/rico.png';
rico.x = 0;
rico.y = 0;
rico.move = 0;

//マップチップのImageオブジェクトを作る
var mapchip = new Image();
mapchip.src = 'img/map.png';

//キーボードのオブジェクトを作成
var key = new Object();
key.up = false;
key.down = false;
key.right = false;
key.left = false;
key.push = '';

//マップの作成(さくせい)
var map = [
	[0, 0, 1, 0, 1, 0, 0, 0 ,0 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,0],
	[0, 1, 0, 0, 0, 1, 1, 1 ,0 ,1 ,0 ,1 ,1 ,0 ,1 ,1 ,1 ,0 ,1 ,0],
	[0, 0, 1, 1, 0, 0, 0, 1 ,0 ,0 ,0 ,1 ,0 ,0 ,0 ,1 ,0 ,0 ,0 ,0],
	[1, 0, 1, 0, 1, 1, 0, 0 ,0 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,1 ,0 ,1 ,0],
	[0, 0, 0, 0, 0, 1, 1, 1 ,0 ,1 ,0 ,0 ,0 ,0 ,1 ,1 ,0 ,1 ,1 ,0],
	[0, 1, 1, 1, 0, 0, 0, 0 ,0 ,1 ,0 ,1 ,1 ,1 ,0 ,1 ,0 ,0 ,0 ,0],
	[0, 1, 1, 1, 0, 1, 1, 1 ,1 ,1 ,0 ,1 ,0 ,0 ,0 ,0 ,1 ,1 ,1 ,0],
	[0, 0, 0, 1, 0, 0, 0, 0 ,1 ,0 ,0 ,1 ,0 ,1 ,1 ,0 ,0 ,0 ,1 ,0],
	[1, 1, 0, 1, 1, 1, 1, 1 ,1 ,0 ,1 ,1 ,0 ,0 ,1 ,1 ,1 ,0 ,1 ,1],
	[1, 0, 0, 0, 0, 0, 1, 1 ,0 ,0 ,0 ,0 ,1 ,0 ,1 ,1 ,0 ,0 ,1 ,0],
	[1, 0, 1, 1, 1, 0, 0, 0 ,1 ,0 ,1 ,0 ,0 ,0 ,0 ,0 ,1 ,1 ,0 ,0],
	[1, 0, 1, 0, 1, 1, 1, 0 ,1 ,0 ,1 ,1 ,0 ,1 ,1 ,0 ,0 ,0 ,0 ,1],
	[0, 0, 1, 0, 0, 1, 0, 0 ,1 ,0 ,0 ,1 ,0 ,1 ,0 ,1 ,1 ,1 ,0 ,0],
	[0, 1, 1, 1, 0, 1, 0, 1 ,0 ,0 ,1 ,1 ,0 ,1 ,0 ,1 ,1 ,0 ,1 ,0],
	[0, 0, 0, 1, 0, 1, 0, 0 ,1 ,0 ,1 ,1 ,0 ,1 ,0 ,0 ,0 ,0 ,0 ,0],
	[1, 1, 0, 1, 0, 1, 0, 1 ,1 ,0 ,0 ,1 ,0 ,1 ,1 ,0 ,1 ,1 ,1 ,0],
	[0, 0, 0, 1, 0, 1, 1, 1 ,1 ,1 ,0 ,1 ,0 ,1 ,1 ,0 ,0 ,0 ,1 ,0],
	[0, 1, 1, 1, 0, 1, 0, 0 ,0 ,0 ,0 ,1 ,0 ,0 ,0 ,1 ,1 ,0 ,1 ,1],
	[0, 1, 0, 0, 0, 1, 0, 1 ,1 ,1 ,0 ,0 ,1 ,1 ,0 ,1 ,0 ,0 ,0 ,0],
	[0, 0, 0, 1, 0, 0, 0, 1 ,1 ,1 ,1 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,1 ,0]
]
りこ

マップはたて20、よこ20で、マップチップひとつが32pxだから、縦横640pxのマップができるのね

さらに、メインループとキー入力にゅうりょくがあったときに呼ばれる関数かんすうです。
メインループでは、まず背景はいけいを黒くりつぶし、そこにマップチップを、そのあとにりこちゃんの画像がぞう表示ひょうじさせます。
あとから表示した画像ほど、上にくるので、表示の順番じゅんばんには気をつけましょう。

//メインループ
function main() {
	//塗(ぬ)りつぶす色を指定(してい)
	ctx.fillStyle = "rgb( 0, 0, 0 )";
	//塗(ぬ)りつぶす
	ctx.fillRect(0, 0, 640, 640);

	for (var y=0; y<map.length; y++) {
		for (var x=0; x<map[y].length; x++) {
			if ( map[y][x] === 0 ) ctx.drawImage( mapchip, 0, 0, 32, 32, 32*x, 32*y, 32, 32 );
			if ( map[y][x] === 1 ) ctx.drawImage( mapchip, 32, 0, 32, 32, 32*x, 32*y, 32, 32 );
		}
	}

	//画像を表示
	ctx.drawImage( rico.img, rico.x, rico.y );

	addEventListener("keydown", keydownfunc, false);
	addEventListener("keyup", keyupfunc, false);

	//方向キーが押されている場合(ばあい)は、りこちゃんが移動する
	if ( rico.move === 0 ) {
		if ( key.left === true ) {
			rico.move = 32;
			key.push = 'left';
		}
		if ( key.up === true ) {
			rico.move = 32;
			key.push = 'up';
		}
		if ( key.right === true ) {
			rico.move = 32;
			key.push = 'right';
		}
		if ( key.down === true ) {
			rico.move = 32;
			key.push = 'down';
		}
	}
	//rico.moveが0より大きい場合は、4pxずつ移動(いどう)を続ける
	if (rico.move > 0) {
		rico.move -= 4;
		if ( key.push === 'left' ) rico.x -= 4;
		if ( key.push === 'up' ) rico.y -= 4;
		if ( key.push === 'right' ) rico.x += 4;
		if ( key.push === 'down' ) rico.y += 4;
	}

	requestAnimationFrame( main );
}
//ページと依存(いぞん)している全てのデータが読み込まれたら、メインループ開始
addEventListener('load', main(), false);

//キーボードが押されたときに呼び出される関数(かんすう)
function keydownfunc( event ) {
	var key_code = event.keyCode;
	if( key_code === 37 ) key.left = true;
	if( key_code === 38 ) key.up = true;
	if( key_code === 39 ) key.right = true;
	if( key_code === 40 ) key.down = true;
	event.preventDefault();
}

//キーボードが放(はな)されたときに呼び出される関数
function keyupfunc( event ) {
	var key_code = event.keyCode;
	if( key_code === 37 ) key.left = false;
	if( key_code === 38 ) key.up = false;
	if( key_code === 39 ) key.right = false;
	if( key_code === 40 ) key.down = false;
}
アル

りこちゃんが移動いどうするためのプログラムだね

少々長いですが、全体ぜんたいはこのようになっています。

//canvasの設定(せってい)
var canvas = document.getElementById( 'canvas' );
canvas.width = 640;	//canvasの横幅(よこはば)
canvas.height = 640;	//canvasの縦幅(たてはば)
 
//コンテキストを取得(しゅとく)
var ctx = canvas.getContext( '2d' );

//りこちゃんのオブジェクトを作成
var rico = new Object();
rico.img = new Image();
rico.img.src = 'img/rico.png';
rico.x = 0;
rico.y = 0;
rico.move = 0;

//マップチップのImageオブジェクトを作る
var mapchip = new Image();
mapchip.src = 'img/map.png';

//キーボードのオブジェクトを作成
var key = new Object();
key.up = false;
key.down = false;
key.right = false;
key.left = false;
key.push = '';

//マップの作成(さくせい)
var map = [
	[0, 0, 1, 0, 1, 0, 0, 0 ,0 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,0],
	[0, 1, 0, 0, 0, 1, 1, 1 ,0 ,1 ,0 ,1 ,1 ,0 ,1 ,1 ,1 ,0 ,1 ,0],
	[0, 0, 1, 1, 0, 0, 0, 1 ,0 ,0 ,0 ,1 ,0 ,0 ,0 ,1 ,0 ,0 ,0 ,0],
	[1, 0, 1, 0, 1, 1, 0, 0 ,0 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,1 ,0 ,1 ,0],
	[0, 0, 0, 0, 0, 1, 1, 1 ,0 ,1 ,0 ,0 ,0 ,0 ,1 ,1 ,0 ,1 ,1 ,0],
	[0, 1, 1, 1, 0, 0, 0, 0 ,0 ,1 ,0 ,1 ,1 ,1 ,0 ,1 ,0 ,0 ,0 ,0],
	[0, 1, 1, 1, 0, 1, 1, 1 ,1 ,1 ,0 ,1 ,0 ,0 ,0 ,0 ,1 ,1 ,1 ,0],
	[0, 0, 0, 1, 0, 0, 0, 0 ,1 ,0 ,0 ,1 ,0 ,1 ,1 ,0 ,0 ,0 ,1 ,0],
	[1, 1, 0, 1, 1, 1, 1, 1 ,1 ,0 ,1 ,1 ,0 ,0 ,1 ,1 ,1 ,0 ,1 ,1],
	[1, 0, 0, 0, 0, 0, 1, 1 ,0 ,0 ,0 ,0 ,1 ,0 ,1 ,1 ,0 ,0 ,1 ,0],
	[1, 0, 1, 1, 1, 0, 0, 0 ,1 ,0 ,1 ,0 ,0 ,0 ,0 ,0 ,1 ,1 ,0 ,0],
	[1, 0, 1, 0, 1, 1, 1, 0 ,1 ,0 ,1 ,1 ,0 ,1 ,1 ,0 ,0 ,0 ,0 ,1],
	[0, 0, 1, 0, 0, 1, 0, 0 ,1 ,0 ,0 ,1 ,0 ,1 ,0 ,1 ,1 ,1 ,0 ,0],
	[0, 1, 1, 1, 0, 1, 0, 1 ,0 ,0 ,1 ,1 ,0 ,1 ,0 ,1 ,1 ,0 ,1 ,0],
	[0, 0, 0, 1, 0, 1, 0, 0 ,1 ,0 ,1 ,1 ,0 ,1 ,0 ,0 ,0 ,0 ,0 ,0],
	[1, 1, 0, 1, 0, 1, 0, 1 ,1 ,0 ,0 ,1 ,0 ,1 ,1 ,0 ,1 ,1 ,1 ,0],
	[0, 0, 0, 1, 0, 1, 1, 1 ,1 ,1 ,0 ,1 ,0 ,1 ,1 ,0 ,0 ,0 ,1 ,0],
	[0, 1, 1, 1, 0, 1, 0, 0 ,0 ,0 ,0 ,1 ,0 ,0 ,0 ,1 ,1 ,0 ,1 ,1],
	[0, 1, 0, 0, 0, 1, 0, 1 ,1 ,1 ,0 ,0 ,1 ,1 ,0 ,1 ,0 ,0 ,0 ,0],
	[0, 0, 0, 1, 0, 0, 0, 1 ,1 ,1 ,1 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,1 ,0]
]

//メインループ
function main() {
	//塗(ぬ)りつぶす色を指定(してい)
	ctx.fillStyle = "rgb( 0, 0, 0 )";
	//塗(ぬ)りつぶす
	ctx.fillRect(0, 0, 640, 640);

	for (var y=0; y<map.length; y++) {
		for (var x=0; x<map[y].length; x++) {
			if ( map[y][x] === 0 ) ctx.drawImage( mapchip, 0, 0, 32, 32, 32*x, 32*y, 32, 32 );
			if ( map[y][x] === 1 ) ctx.drawImage( mapchip, 32, 0, 32, 32, 32*x, 32*y, 32, 32 );
		}
	}

	//画像を表示
	ctx.drawImage( rico.img, rico.x, rico.y );

	addEventListener("keydown", keydownfunc, false);
	addEventListener("keyup", keyupfunc, false);

	//方向キーが押されている場合(ばあい)は、りこちゃんが移動する
	if ( rico.move === 0 ) {
		if ( key.left === true ) {
			rico.move = 32;
			key.push = 'left';
		}
		if ( key.up === true ) {
			rico.move = 32;
			key.push = 'up';
		}
		if ( key.right === true ) {
			rico.move = 32;
			key.push = 'right';
		}
		if ( key.down === true ) {
			rico.move = 32;
			key.push = 'down';
		}
	}
	//rico.moveが0より大きい場合は、4pxずつ移動(いどう)を続ける
	if (rico.move > 0) {
		rico.move -= 4;
		if ( key.push === 'left' ) rico.x -= 4;
		if ( key.push === 'up' ) rico.y -= 4;
		if ( key.push === 'right' ) rico.x += 4;
		if ( key.push === 'down' ) rico.y += 4;
	}

	requestAnimationFrame( main );
}
//ページと依存(いぞん)している全てのデータが読み込まれたら、メインループ開始
addEventListener('load', main(), false);

//キーボードが押されたときに呼び出される関数(かんすう)
function keydownfunc( event ) {
	var key_code = event.keyCode;
	if( key_code === 37 ) key.left = true;
	if( key_code === 38 ) key.up = true;
	if( key_code === 39 ) key.right = true;
	if( key_code === 40 ) key.down = true;
	event.preventDefault();
}

//キーボードが放(はな)されたときに呼び出される関数
function keyupfunc( event ) {
	var key_code = event.keyCode;
	if( key_code === 37 ) key.left = false;
	if( key_code === 38 ) key.up = false;
	if( key_code === 39 ) key.right = false;
	if( key_code === 40 ) key.down = false;
}

しかしこのプログラムでは、りこちゃんがかべをすりけてしまいます。

下の枠内わくないをクリックして、方向ほうこうキーで操作そうさしてみてね!

りこ

きゃー、私がかべをすりけてる

アル

これじゃあ迷路めいろにならないね

山田

そこで今回は、かべにぶつからないように、プログラムをつくっていくべ!

壁の座標とりこちゃんの座標を比べて、壁との当たり判定をする

当たり判定はんていといえば、Scratch(スクラッチ)でシューティングゲームを作った時も敵とぶつかっているかとか、たまが当たっているかとか、やりましたね。

しかし今回の場合ばあい、「かべに当たっているか」ではなく、「壁に当たってしまうか」という判定になります。
どちらにしろ実際じっさいには当たっていないので、「この先、衝突しょうとつするかも」という予測よそく判定はんていです。

アル

予測よそくって……まほうでもつかうのかな?

りこ

そんなわけないでしょ! でも、どうやるんだろう

山田

座標ざひょうで考えればいんだべ

アル

りこちゃんの座標と、壁の座標を比べるってこと? でもどうやって……

さて、壁の座標はもうすでに取得しゅとくできています。
なぜなら、mapという配列はいれつにマップを作って、そこに合わせて壁を配置はいちしているからです。

さらに、りこちゃんの座標も取得できますね。
オブジェクトのrico.xとrico.yです。

じつはこの2つ、座標の表し方がちがいます。
りこちゃんは実際じっさいに動かすため、正確せいかくな座標をrico.xとrico.yが覚えてくれています。
しかしmapは正確な座標を覚えているわけではなく、マップチップの場所ばしょを覚えているだけなんです。

左から0番目、上から0番目の数字は0、
左から1番目、上から0番目の数字は0、
左から2番目、上から0番目の数字は1、
左から3番目、上から0番目の数字は0、

といったふうです。

だけど心配しんぱいする必要ひつようはありません。
りこちゃんの座標は、マップチップのサイズである32でわることで、mapと同じことになります。

りこ

なんで!? よく分からない!

アル

僕もさっぱり!

山田

じゃあ、れいを見るべよ!

では、れいで見ていきましょう。
りこちゃんのいる場所ばしょは、0から数えて、左から何番目の、上から何番目でしょうか?

答えは、左から3番目の、上から6番目です。

では、x座標ざひょうとy座標を正確せいかくにいうとどうなるでしょうか?
かんたんです。1マス32pxなので、32をかけ算すれば、答えがでますね。
つまりxは96px, yは192pxです。

りこ

逆にいえば、わり算すれば正確な座標から、何番目のところにいるかが出せるってことね!

山田

そうなんだべよ!

もしも右矢印が押されて、右方向にかべがある場合はすすむことができません。
壁があるということは、りこちゃんの右側が「1」になっているということです。

まず、変数xとyに、りこちゃんの座標ざひょうを32でわった数を代入だいにゅうします。
右矢印が押されたとき、x座標に1プラスしたいので、x++とすることでxに1足すことができます。

そのあと、map[y][x]とすることで、キャラが右方向へ1移動いどうしたあとのマップ上の座標をしめすことができます。
その座標が0の場合ばあいのときのみ移動できるようにします。

これをプログラムにしてみると、このようになります。

if ( key.right === true ) {
	var x = rico.x/32;
	var y = rico.y/32;
	x++;
	if ( map[y][x] === 0 ) {
		rico.move = 32;
		key.push = 'right';
	}
}

ほかのキーのときも同じです。

しかし、このプログラムでは、y座標の場合ばあい、上や下にマップからはみ出てしまうとエラーがおきてしまいます。
上下の矢印キーが押されたときのみ、移動いどう制限せいげんします。
例えば、上キーが押されたときは、このように4行目で移動を制限しています。

if ( key.up === true ) {
	var x = rico.x/32;
	var y = rico.y/32;
	if ( y > 0) {
		y--;
		if ( map[y][x] === 0 ) {
			rico.move = 32;
			key.push = 'up';
		}
	}
}

全体ぜんたいではこのようになります。

//canvasの設定(せってい)
var canvas = document.getElementById( 'canvas' );
canvas.width = 640;		//canvasの横幅(よこはば)
canvas.height = 640;	//canvasの縦幅(たてはば)
 
//コンテキストを取得(しゅとく)
var ctx = canvas.getContext( '2d' );
 
//りこちゃんのオブジェクトを作成
var rico = new Object();
rico.img = new Image();
rico.img.src = 'img/rico.png';
rico.x = 0;
rico.y = 0;
rico.move = 0;

//マップチップのImageオブジェクトを作る
var mapchip = new Image();
mapchip.src = 'img/map.png';

//キーボードのオブジェクトを作成
var key = new Object();
key.up = false;
key.down = false;
key.right = false;
key.left = false;
key.push = '';

//マップの作成(さくせい)
var map = [
	[0, 0, 1, 0, 1, 0, 0, 0 ,0 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,0],
	[0, 1, 0, 0, 0, 1, 1, 1 ,0 ,1 ,0 ,1 ,1 ,0 ,1 ,1 ,1 ,0 ,1 ,0],
	[0, 0, 1, 1, 0, 0, 0, 1 ,0 ,0 ,0 ,1 ,0 ,0 ,0 ,1 ,0 ,0 ,0 ,0],
	[1, 0, 1, 0, 1, 1, 0, 0 ,0 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,1 ,0 ,1 ,0],
	[0, 0, 0, 0, 0, 1, 1, 1 ,0 ,1 ,0 ,0 ,0 ,0 ,1 ,1 ,0 ,1 ,1 ,0],
	[0, 1, 1, 1, 0, 0, 0, 0 ,0 ,1 ,0 ,1 ,1 ,1 ,0 ,1 ,0 ,0 ,0 ,0],
	[0, 1, 1, 1, 0, 1, 1, 1 ,1 ,1 ,0 ,1 ,0 ,0 ,0 ,0 ,1 ,1 ,1 ,0],
	[0, 0, 0, 1, 0, 0, 0, 0 ,1 ,0 ,0 ,1 ,0 ,1 ,1 ,0 ,0 ,0 ,1 ,0],
	[1, 1, 0, 1, 1, 1, 1, 1 ,1 ,0 ,1 ,1 ,0 ,0 ,1 ,1 ,1 ,0 ,1 ,1],
	[1, 0, 0, 0, 0, 0, 1, 1 ,0 ,0 ,0 ,0 ,1 ,0 ,1 ,1 ,0 ,0 ,1 ,0],
	[1, 0, 1, 1, 1, 0, 0, 0 ,1 ,0 ,1 ,0 ,0 ,0 ,0 ,0 ,1 ,1 ,0 ,0],
	[1, 0, 1, 0, 1, 1, 1, 0 ,1 ,0 ,1 ,1 ,0 ,1 ,1 ,0 ,0 ,0 ,0 ,1],
	[0, 0, 1, 0, 0, 1, 0, 0 ,1 ,0 ,0 ,1 ,0 ,1 ,0 ,1 ,1 ,1 ,0 ,0],
	[0, 1, 1, 1, 0, 1, 0, 1 ,0 ,0 ,1 ,1 ,0 ,1 ,0 ,1 ,1 ,0 ,1 ,0],
	[0, 0, 0, 1, 0, 1, 0, 0 ,1 ,0 ,1 ,1 ,0 ,1 ,0 ,0 ,0 ,0 ,0 ,0],
	[1, 1, 0, 1, 0, 1, 0, 1 ,1 ,0 ,0 ,1 ,0 ,1 ,1 ,0 ,1 ,1 ,1 ,0],
	[0, 0, 0, 1, 0, 1, 1, 1 ,1 ,1 ,0 ,1 ,0 ,1 ,1 ,0 ,0 ,0 ,1 ,0],
	[0, 1, 1, 1, 0, 1, 0, 0 ,0 ,0 ,0 ,1 ,0 ,0 ,0 ,1 ,1 ,0 ,1 ,1],
	[0, 1, 0, 0, 0, 1, 0, 1 ,1 ,1 ,0 ,0 ,1 ,1 ,0 ,1 ,0 ,0 ,0 ,0],
	[0, 0, 0, 1, 0, 0, 0, 1 ,1 ,1 ,1 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,1 ,0]
]

//メインループ
function main() {
	//塗(ぬ)りつぶす色を指定(してい)
	ctx.fillStyle = "rgb( 0, 0, 0 )";
	//塗(ぬ)りつぶす
	ctx.fillRect(0, 0, 640, 640);

	for (var y=0; y<map.length; y++) {
		for (var x=0; x<map[y].length; x++) {
			if ( map[y][x] === 0 ) ctx.drawImage( mapchip, 0, 0, 32, 32, 32*x, 32*y, 32, 32 );
			if ( map[y][x] === 1 ) ctx.drawImage( mapchip, 32, 0, 32, 32, 32*x, 32*y, 32, 32 );
		}
	}

	//画像を表示
	ctx.drawImage( rico.img, rico.x, rico.y );

	addEventListener("keydown", keydownfunc, false);
	addEventListener("keyup", keyupfunc, false);

	//方向キーが押されている場合(ばあい)は、りこちゃんが移動する
	if ( rico.move === 0 ) {
		if ( key.left === true ) {
			var x = rico.x/32;
			var y = rico.y/32;
			x--;
			if ( map[y][x] === 0 ) {
				rico.move = 32;
				key.push = 'left';
			}
		}
		if ( key.up === true ) {
			var x = rico.x/32;
			var y = rico.y/32;
			if ( y > 0) {
				y--;
				if ( map[y][x] === 0 ) {
					rico.move = 32;
					key.push = 'up';
				}
			}
		}
		if ( key.right === true ) {
			var x = rico.x/32;
			var y = rico.y/32;
			x++;
			if ( map[y][x] === 0 ) {
				rico.move = 32;
				key.push = 'right';
			}
		}
		if ( key.down === true ) {
			var x = rico.x/32;
			var y = rico.y/32;
			if ( y < 19 ) {
				y++;
				if ( map[y][x] === 0 ) {
					rico.move = 32;
					key.push = 'down';
				}
			}
		}
	}

	//rico.moveが0より大きい場合は、4pxずつ移動(いどう)を続ける
	if (rico.move > 0) {
		rico.move -= 4;
		if ( key.push === 'left' ) rico.x -= 4;
		if ( key.push === 'up' ) rico.y -= 4;
		if ( key.push === 'right' ) rico.x += 4;
		if ( key.push === 'down' ) rico.y += 4;
	}

	requestAnimationFrame( main );
}
//ページと依存(いぞん)している全てのデータが読み込まれたら、メインループ開始
addEventListener('load', main(), false);

//キーボードが押されたときに呼び出される関数(かんすう)
function keydownfunc( event ) {
	var key_code = event.keyCode;
	if( key_code === 37 ) key.left = true;
	if( key_code === 38 ) key.up = true;
	if( key_code === 39 ) key.right = true;
	if( key_code === 40 ) key.down = true;
	event.preventDefault();		//方向キーでブラウザがスクロールしないようにする
}

//キーボードが放(はな)されたときに呼び出される関数
function keyupfunc( event ) {
	var key_code = event.keyCode;
	if( key_code === 37 ) key.left = false;
	if( key_code === 38 ) key.up = false;
	if( key_code === 39 ) key.right = false;
	if( key_code === 40 ) key.down = false;
}

下の枠内わくないをクリックして、方向ほうこうキーで操作そうさしてみてね!

りこ

きゃー、迷路、まよっちゃう!

アル

すごい! ちゃんと壁で止まってるよ!

まとめ

今回は、マップ上をキャラクターが移動いどうし、かべをすりけられないように作りました。
大切なのはりこちゃんの座標ざひょうと、マップチップの座標をらし合わせることです。そのためには、りこちゃんの座標を1つのマップチップのサイズで割るというのも1つの方法です。

山田

ここまででも、一応遊べるべよ

アル

まだスタートもゴールもないけどね

りこ

でもスタートとゴールだけなら簡単かんたんにつくれそうだね

山田

ふたりとも、よくここまで頑張ったべ

りこ

へへへっ

アル

もっと頑張ればポケモンみたいなゲームだってゆめじゃないなって思えてきたよ

山田

それは良かったべ。ところでわだず、昨日ザギンで、こんなのかってきたべ

アル

ザギン……?

りこ

銀座ぎんざのことじゃない? どうやって行ったんだろう

山田

サングラスと、ジーパンだべ。どうだべ、似合にあうべか? これで人間になりすますんだべ

アル

ゴーグルと海パンだよ

りこ

ははは、海でも行くのー?

山田

あ……

山田

て……てやんでぇ、べらぼうめだべさ! 海に行ってこそ、真の男だべさ!
次回はもっとむずかしいこと教えてやるべ! 覚悟かくごするべ!

りこ

ひぃ、う……うん。先生、こわい

アル

ゆるしてー

このシリーズの一覧はこちら

  1. 小学生からのプログラミング入門。プログラミングってなぁに?
  2. Scratchの使い方と、ゲーム作りの基礎知識を学ぼう! 小学生からのプログラミング入門
  3. Scratchでじゃんけんゲームを作ろう! 小学生からのプログラミング入門
  4. Scratchでシューティングゲームを作ろう! 小学生からのプログラミング入門
  5. Scratchでピアノ鍵盤を作って音を鳴らそう! 小学生からのプログラミング入門
  6. テキストエディタ(Visual Studio Code)をインストールしてみよう! 小学生からのプログラミング入門
  7. Visual Studio Codeを日本語化してみよう! 小学生からのプログラミング入門
  8. JavaScriptでおみくじを作ろう! 小学生からのプログラミング入門
  9. JavaScriptで今月の残り日数を計算してみよう! 小学生からのプログラミング入門
  10. JavaScriptで画像を表示してみよう! 小学生からのプログラミング入門
  11. JavaScriptで画像を移動してみよう! 小学生からのプログラミング入門
  12. 【JavaScript】キー入力でキャラを動かしてみよう! 小学生からのプログラミング入門
  13. 【JavaScript】ファイルを分けて管理してみよう! 小学生からのプログラミング入門
  14. 【JavaScript】オブジェクトを使ってみよう! 小学生からのプログラミング入門
  15. 【JavaScript】ゲームのメインループを作ってみよう! 小学生からのプログラミング入門
  16. 【JavaScript】キャラを決まった間隔ずつ動かす! 小学生からのプログラミング入門
  17. HTML5とCanvasを使ってみよう! 小学生からのプログラミング入門
  18. 【JavaScript】迷路やRPGで使えるマップを作ってみよう! 小学生からのプログラミング入門
  19. 【JavaScript】マップでキャラを動かせるようにしよう! 小学生からのプログラミング入門
  20. 【JavaScript】クラスの概念をしっかりと理解しよう! 小学生からのプログラミング入門
  21. 【JavaScript】プログラム全体をクラスを使って作ってみよう! 小学生からのプログラミング入門
  22. 【JavaScript】文字を表示するクラスを作ってみよう! 小学生からのプログラミング入門
  23. 【JavaScript】改行と一文字ずつ画面に表示する方法! 小学生からのプログラミング入門
  24. 【JavaScript】ノベルゲーム風にキー入力で文字を切り替える方法! 小学生からのプログラミング入門
  25. JavaScriptでRPGを作ろう!スマホにも対応したゲームの作り方
  26. webpackを使ってゲームエンジンを作ろう!(JSライブラリの作り方)
  27. WindowsにPythonをインストールしてみよう!小学生からのPython入門
  28. MacにPythonをインストールしてみよう!小学生からのPython入門
  29. Pythonでじゃんけんゲームを作ってみよう!小学生からのPython入門
  30. Pythonのtkinterを使って、ウィンドウを表示してみよう!
  31. Pythonのtkinterで、画像つきのおみくじゲームを作ろう!

8件のコメント 「【JavaScript】マップでキャラを動かせるようにしよう! 小学生からのプログラミング入門」

  1. はじめまして、突然失礼します。
    JavaScriptの勉強をはじめており、このサイトで大変学ばせていただいております。ありがとうございます。

    1点教えてください。
    後半に「しかし、このプログラムでは、y座標の場合ばあい、上や下にマップからはみ出てしまうとエラーがおきてしまいます」とあるのですが、

    具体的にどういうエラーなのかをご教示いただけますでしょうか?
    よろしくお願いします。

    1. >hiroさん
      はじめまして。コメントありがとうございます。

      例えば、上キーが押されたとき、りこちゃんは上に移動しようとします。
      そのとき、移動先の座標が床か壁か、をmap[y][x]の値で判断します。
      yやxはりこちゃんの座標です。

      しかし、りこちゃんが画面の外に出てしまうと、そこはマップではなくなってしまいます。

      左右にはみ出した場合、map[y][x]は値が設定されていない状態で(undefinedという値で)、そのまま動作してくれるのですが、上下にはみ出したときは、map[y]の値が設定されていないので、map[y][x]を呼び出せず、エラーになってしまいます。

      参考になれば幸いです。

      1. コメント失礼致します。
        分かりやすくて大変参考にさせて頂いております。ありがとうございます。
        最近javascript(プログラム)を知ったため考え方に間違いがあるかもしれませんが
        同じような疑問があったためこちらに返信させて頂きました。

        左右にはみ出した場合、map[y][x]は値が設定されていない状態で(undefinedという値で)、そのまま動作してくれるのですが、上下にはみ出したときは、map[y]の値が設定されていないので、map[y][x]を呼び出せず、エラーになってしまいます。

        とのことですが、左右の場合は
        map[y(y軸のどこか)][x(undefined)]
        という形で動作してくれるが、上下の場合は
        map[y(y(undefined)][x(undefined)]
        となりmapが呼び起こせずエラーとなるという事でしょうか?

        確かに上下にはif分を追記しないと
        Uncaught TypeError: Cannot read properties of undefined (reading ‘0’)
        のエラーがでたのですが、
        xはundefinedで動作してくれるのにyがundefinedだと動作してくれない(エラーになる)
        というのが腑に落とせません。

        map[y(y軸のどこか)][x(undefined)]
        が動作できるならエラーではなく
        map[undefined]
        として動作してくれても良くない?と感じました。

        この理屈?の部分を
        ご教授お願いできませんでしょうか。
        よろしくお願い致します。

  2. 最近このサイトを見て勉強を始めた者です。
    このページのプログラムをvisual studio codeに書き、実行したのですかが、動きませんでした。
    変数の名前などを自分の分かりやすいように変更しつつほぼ丸写しのような状態で実行したのですが、動かなかったため、失礼ながらこのページのソースコードを一度コピペして実行してみました。しかし、マップも表示されずキャラクターも動きませんでした。
    前々回まで(マップを表示させる前)までは行う事が出来たのですがマップの表示が出来ません。
    どうすれば表示させられるでしょうか。つたない文章ですがよろしくおねがします。

    1. >ゆりかさん
      はじめまして。コメントありがとうございます。

      まず、この記事でミスがあったことに、たった今、気がつきました。「マップにキャラクターを表示してみよう!」の項目、キャラが壁をすり抜けてしまう部分の、全体のソースコードです。
      どうやら、貼り付けのときの私のミスで、改行が削除されてしまっていたようです。
      この部分は修正させていただきました。
      もし、ここが原因で、プログラムが動かなかったのでしたら、大変申し訳ありません。

      ただ、前々回までは動かせた、とのことですので、原因は前回の記事での過程のどこかにあるのかな、と思います。

      原因を探るために、ひとつずつ確認させてください。

      このページのソースコードをコピペした、というのは、一番最後の全体のソースコードでしょうか?
      もしそうでしたら、私ももう一度試してみましたが、動かすことができましたので、コピペでも問題ないと思います。

      となると原因は、index.htmlファイルか、画像ファイルの場所かな、と思いますが、もしindex.htmlファイルにミスがあれば、前々回のプログラムも動かないはずなので、こちらは問題なさそうな気がします。

      画像ファイルの場所ですが、マップの画像ファイル(map.png)は、imgフォルダの中に入っているでしょうか? こちらの確認をお願いします。

      ちゃんとimgフォルダのなかにmap.pngファイルが入っているのでしたら、可能性は低い気もしますが、ブラウザのキャッシュが原因かもしれません。
      ブラウザのキャッシュの削除を試してみてください。

      今のところ、私に考えられる原因は以上ですが、もしそれでも動かない場合は、ブラウザのコンソールを確認してみてください。
      なにかエラーメッセージが表示されていれば、そこから原因を探ることができるかもしれません。

      お役に立てると幸いです。

      1. このコードをVisualStudioCodeで作成しているのですが、どうも壁の衝突判定がうまくいきません。
        毎回、同じ場所で「 Uncaught TypeError: Cannot read properties of undefined (reading ‘9.8125’)
        at main」とエラーが出てしまいます。
        どうすればいいですか?
        (ここのサイトだと、一番最後の、全体コードの79番目にエラーが出ているとなります。)

        1. > Yasuさん
          こんにちは。コメントありがとうございます。

          まず、この記事の最後の全体のコードを、わたしの環境でもう一度試してみましたが、ちゃんと動いているようです。

          毎回同じ場所でエラーになるということ、一番最後の全体のコードの79番目にエラーが出るということ、の2つを考えると、方向キーの左を押したとき(左に進みたいとき)にエラーになる、ということかもしれません。

          79番目の「if ( map[y][x] === 0 )」の部分で使っている、変数yが宣言されていないと、それに似たエラーが表示されます。(変数yの宣言は、全体のコードの77行目)

          もうひとつ、エラーの中の「reading ‘9.8125’」という部分が気になります。
          私も色々試してみたのですが、今回のプログラムでは、この数値が小数にはならないはずなのです。

          私の方では、このエラーの確実な原因を探ることはできないのですが、以下の点を確認してみてください。

          マップチップやキャラクターの画像は、縦横32pxの正方形のものを使っていますか?
          キャラクターは、方向キーが押されるごとに32pxずつ動くようになっていますか?
          キャラクターの移動量は、4などの、32を割ってもあまりが出ない数値になっていますか?
          キャラクターの初期位置は、32で割り切れる数になっていますか?

          もしこれらを確認しても問題ないのでしたら、一度この記事の最後の全体のコードをコピペして試してみてください。

          お役に立てれば幸いです。

コメントを残す(コメントは承認後に反映されます)

メールアドレスが公開されることはありません。 が付いている欄は必須項目です




オリジナルゲーム.com