1. HOME»
  2. プログラミング・Web»
  3. JavaScript»
  4. JavaScriptでRPGを作ろう!スマホにも対応したゲームの作り方

JavaScriptでRPGを作ろう!スマホにも対応したゲームの作り方

りこ

あ、先生。ちょうネクタイが曲がってるよ

山田

お、そうだべか? すまんがちょっとなおしてくれないべか?

りこ

うん! あれ……ねじっちゃってるな……いったん取り外そう

アル

ふぅ……教室にリコーダー忘れちゃったから持ってきた。あれ? りこちゃん。その猫、どうしたの?

りこ

……

アル

山田先生、いないのか……また校長先生に怒られてるんじゃないの?

りこ

(……と、とにかく蝶ネクタイをつけてあげなくちゃ)

アル

山田先生って、けっこうドジなところがあるからなぁ

山田

アルくん……

アル

ひぃっ……あぁっ、や……山田先生だったの!? ごごご、ごめんなさーい!

目次
  1. タイルマップを作ってみよう!
  2. キャラクターをタイルとしてあつかってみよう!
  3. タイルマップにタイルを追加できるようにしてみよう!
  4. 移動のとき、キャラクターではなく、マップがスクロールするようにしてみよう!
  5. キャラクターとマップの初期位置を調節しよう!

タイルマップを作ってみよう!

つづいて、タイルマップを作っていきます。

山田

つぎはタイルマップの機能きのうを作っていくべよ
タイルマップは、タイルをみ合わせるようにして作られるマップのことだべ

アル

ひとむかしまえの、ポケモンやドラクエのマップみないなのだね

山田

そうだべよ
では、engineフォルダにtilemap.jsファイル追加ついかするべ

rpg/
|-- img (省略)/
|-- index.html
|-- js/
|   |-- engine/
|   |   |-- game.js
|   |   |-- scene.js
|   |   |-- sprite.js
|   |   `-- tilemap.js (追加)
|   `-- main.js
`-- sound (省略)/
りこ

ファイルを追加したあとは、index.htmlから読み込むのね

index.html

<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>くろねこラビリンス</title>
	<style>
		* {
			padding: 0;
			margin: 0;
		}
		canvas {
			display: block;
		}
	</style>
</head>
<body>
	<script src="js/engine/scene.js"></script>
	<script src="js/engine/sprite.js"></script>
	<script src="js/engine/tilemap.js"></script>
	<script src="js/engine/game.js"></script>
	<script src="js/main.js"></script>
</body>
</html>
山田

そして、tilemap.jsはこのようにして欲しいんだべ

js/engine/tilemap.js

'use strict'

/**
 * タイルマップに関するクラス
 */
class Tilemap {

	/**
	 * 引数
	 * img : 画像ファイルまでのパス
	 * size : タイルひとつの大きさ(一辺の長さ)
	 *
	 * タイルひとつは正方形にする
	 */
	constructor( img, size ) {
		//Imageのインスタンスを作成
		this.img = new Image();
		//this.img.srcに画像ファイルまでのパスを代入
		this.img.src = img;
		//画像の初期位置
		this.x = this.y = 0;
		//引数sizeが指定されていない場合、this.sizeに32を代入
		this.size = size || 32;
		//二次元配列で数値を入力すると、マップをつくることができる
		this.data = [];
	} //constructor() 終了

	/**Gameクラスのメインループからずっと呼び出され続ける
	 *
	 * 引数
	 * canvas : 紙(キャンバス)
	 */
	update( canvas ) {
		//画像などを画面に表示するためのメソッドを呼び出す
		this.render( canvas );
		//常に呼び出される、オーバーライド用のメソッドを呼び出す
		this.onenterframe();
	} //update() 終了

	/**
	 * Gameクラスのメインループからずっと呼び出され続ける。画像を表示したりするためのメソッド
	 *
	 * 引数
	 * canvas : 紙(キャンバス)
	 */
	render( canvas ) {
		//マップの縦方向の数だけ繰り返す
		for (let y=0; y<this.data.length; y++) {
			//タイルの縦の位置
			const _tileY = this.size * y + this.y;
			//タイルが、画面から縦にはみ出しているとき、この下をスキップして、次から繰り返し
			if ( _tileY < -1 * this.size || _tileY > canvas.height ) continue;

			//マップの横方向の数だけ繰り返す
			for (let x=0; x<this.data[y].length; x++) {
				//タイルの横の位置
				const _tileX = this.size * x + this.x
				//タイルが、画面から横にはみ出しているとき、この下をスキップして、次から繰り返し
				if ( _tileX < -1 * this.size || _tileX > canvas.width ) continue;

				//X方向に、何番目の画像か
				const _frameX = this.data[y][x] % ( this.img.width / this.size );
				//Y方向に、何番目の画像か
				const _frameY = ~~( this.data[y][x] / ( this.img.width / this.size ) );

				//画家さん(コンテキスト)を呼ぶ
				const _ctx = canvas.getContext( '2d' );

				//タイルを表示
				_ctx.drawImage(
					this.img,
					this.size * _frameX,
					this.size * _frameY,
					this.size,
					this.size,
					_tileX,
					_tileY,
					this.size,
					this.size
				);
			}
		}
	} //render() 終了

	/**
	 * 常に呼び出されるメソッド。空なのはオーバーライド(上書き)して使うため
	 */
	onenterframe() {}

}
山田

内容ないようはだいたいsprite.jsと同じだべけど、タイルは正方形せいほうけいのものを使うべから、constructorの引数にwidthやheightじゃなくて、sizeを使っているべ
そして、46〜83行目のrenderメソッドは、Spriteクラスよりも複雑ふくざつになっているべよ

りこ

あ、48行目と55行目に、2度もforが使われてる!

山田

そうなんだべ。ふつうのスプライトとの違いとして、タイルマップはタイルを大量たいりょうに表示する必要ひつようがあるべ。そこで、たて方向とよこ方向に、forでかえしをしながら、タイルを配置はいちしているんだべ

さらに61〜64行目で、どのタイルを使うのかを計算けいさんしているんだべよ

アル

このthis.data[y][x]っていうのは?

山田

マップは数字でつくるから、その場所ばしょの数字を取得しゅとくするんだべよ

りこ

どういうこと?

山田

うーむ。まずは実際じっさいに使ってみるべ
では、このTilemapクラスを使って、タイルマップを表示してみるべよ!

js/main.js

'use strict'

//ブラウザがページを完全に読みこむまで待つ
addEventListener( 'load', () => {

	//変数gameに、あなたはゲームですよ、と教える
	const game = new Game();

	//マップの作成
	const map = [
		[11,11,11,11,11,11,11,11,11,11],
		[11,10,10,10,10,10,10,10,10,11],
		[11, 4, 4, 4, 4, 4, 4, 4, 4,11],
		[11, 4,11, 4, 4,11,11,11, 4,11],
		[11, 4,11,11,11,11,10,10, 4,11],
		[11, 4,11,10,10,11, 4, 4, 4,11],
		[11, 4,11, 4, 4,11,11,11, 4,11],
		[11, 4, 9, 4, 4, 9,10,11, 4,11],
		[11, 4, 4, 4, 4, 4, 4,11, 4,11],
		[11,11,11,11,11,11,11,11,11,11]
	];
	//歩く速さ
	const WALKING_SPEED = 4;

	//変数sceneに、あなたはシーンですよ、と教える
	const scene = new Scene();

	//変数tilemapに、あなたはタイルマップですよ、と教える
	const tilemap = new Tilemap( 'img/tile.png' );
	//tilemap.dataに、どんなマップなのか教える
	tilemap.data = map;
	//マップを登録する
	scene.add( tilemap );

	//変数yamadaに、あなたは山田先生のスプライト画像ですよ、と教える
	const yamada = new Sprite( 'img/yamada.png' );
	//sceneに、山田先生のスプライト画像を追加して、とお願いする
	scene.add( yamada );

	//ループから常に呼び出される
	scene.onenterframe = () => {
		//キーが押されたとき、山田先生が移動する
		if ( game.input.left ) yamada.x -= WALKING_SPEED;
		if ( game.input.right ) yamada.x += WALKING_SPEED;
		if ( game.input.up ) yamada.y -= WALKING_SPEED;
		if ( game.input.down ) yamada.y += WALKING_SPEED;
	} //scene.onenterframe 終了

	//gameに、シーンを追加して、とお願いする
	game.add( scene );

	//gameに、ゲームをスタートして、とお願いする
	game.start();

} );
山田

まず、9〜21行目で、マップを作成しているべよ

アル

数字がたくさんで、むずかしそう……

山田

むずかしくはないべ
この数字は、どのタイルを配置するかをあらわしているんだべ

こんなふうに、タイルには0〜11の数字が割り当てられていると考えればいいんだべ

山田

さきほどアルくんが悩んでいた、this.data[y][x]の部分は、この数字を取得しているんだべよ

アル

なるほど! これだったのか

山田

そして、28〜33行目で、タイルマップをシーンに作っているんだべよ

りこ

タイルマップって、こうやって作ればいいのね!

山田

ブラウザで確認すると、こうなるべ

タイルマップが表示される!
アル

やった。もうRPGっぽいね!

りこ

やったぁ!

キャラクターをタイルとしてあつかってみよう!

つづいて、キャラクターをタイルとしてあつかってみます。

山田

さて……これまでわたすのスプライトは、Spriteクラスを使ってきたべよ

りこ

うん、そうだね

アル

なにかダメなの?

山田

うむ……いまのところ、Spriteクラスはただスプライトを表示するだけの機能きのうしかないべ
じゃあ、タイルマップ上の壁にぶつかったときに移動できなくしたり、ゴールしたときに「ゴールだべ!」と表示するおねがいはどうするべ?

りこ

あ、スプライトを表示しただけじゃ、タイルマップ上のどこにいるのかも分からないんだ……
でもどうすればいいの?

山田

やっぱりタイルマップ上にあるものは、タイルでなくちゃいけないべ

アル

うん、たしかにそうだよね

りこ

じゃあ、山田先生のスプライトをタイルにしちゃえばいいのね

山田

そうだべ
そこで、これからTileクラスを作っていくべよ!

まずはengineフォルダに、tile.jsファイルを作るべ

rpg/
|-- img (省略)/
|-- index.html
|-- js/
|   |-- engine/
|   |   |-- game.js
|   |   |-- scene.js
|   |   |-- sprite.js
|   |   |-- tile.js (追加)
|   |   `-- tilemap.js
|   `-- main.js
`-- sound (省略)/
山田

もちろん、作ったファイルはindex.htmlから読み込むべよ

index.html

<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>くろねこラビリンス</title>
	<style>
		* {
			padding: 0;
			margin: 0;
		}
		canvas {
			display: block;
		}
	</style>
</head>
<body>
	<script src="js/engine/scene.js"></script>
	<script src="js/engine/sprite.js"></script>
	<script src="js/engine/tile.js"></script>
	<script src="js/engine/tilemap.js"></script>
	<script src="js/engine/game.js"></script>
	<script src="js/main.js"></script>
</body>
</html>
山田

さらに、tile.jsはこのようにして欲しいんだべ

js/engine/tile.js

'use strict'

/**
 * タイルに関してのクラス
 */
class Tile extends Sprite {

	/**
	 * 引数
	 * img : 画像ファイルまでのパス
	 * size : タイルの大きさ
	 */
	constructor( img, size ) {
		//親クラスのコンストラクタを呼び出す
		super( img, size, size );
		//引数sizeが指定されていない場合、this.sizeに32を代入
		this.size = size || 32;
	} //constructor() 終了

}
りこ

あれ? 6行目にextends Spriteって書いてある……
これはどういうこと?

山田

これは継承というものだべ

アル

そんなむずかしい字、読めないよ……

りこ

ええっ、なんで字が分かったの!?

山田

「けいしょう」と読むんだべ。継承けいしょう……とても便利べんり機能きのうだから、おぼえておいて欲しいんだべ

今回の場合、Tileクラスは、Spriteクラスを継承しているんだべ

りこ

えーっと、それで、継承ってどんな機能なの?

山田

パワーアップだべよ
ポケモンでいう、進化しんかみたいなものだべ

アル

進化!?

山田

うむ。つまりTileクラスは、Spriteクラスが進化したものだべよ
では、さきほどのtile.jsの、15行目を見て欲しいべ

りこ

superスーパーって?

山田

おやクラスのコンストラクタを呼び出しているんだべ
親クラスというのは、継承元の方のクラス……今回ならば、Spriteクラスが親クラスだべ

この親クラスのことを、スーパークラスともいうから、覚えておいて欲しいべ

アル

superには、括弧かっこのなかにimgと、sizeってのが2つ入ってるよ

山田

ふっふっふ。親クラスであるSpriteクラスのコンストラクタには、img、width、heightの、3つの引数ひきすうがあったべな?
それにたいして、Tileクラスのコンストラクタには、img、sizeの2つの引数しかないべ
これは、スプライトが長方形ちょうほうけいでも表示できるのにたいして、タイルは正方形せいほうけいのみを表示できるようにしているからだべ

りこ

正方形だけを表示できるように、進化してるのね!

アル

それって退化たいかじゃ……

山田

てやんでぇ、べらぼうめだべ!
タイルとして使いやすいように進化してるんだべ!

りこ

じゃあ、このTileクラス、さっそく使ってみましょ

js/main.js

'use strict'

//ブラウザがページを完全に読みこむまで待つ
addEventListener( 'load', () => {

	//変数gameに、あなたはゲームですよ、と教える
	const game = new Game();

	//マップの作成
	const map = [
		[11,11,11,11,11,11,11,11,11,11],
		[11,10,10,10,10,10,10,10,10,11],
		[11, 4, 4, 4, 4, 4, 4, 4, 4,11],
		[11, 4,11, 4, 4,11,11,11, 4,11],
		[11, 4,11,11,11,11,10,10, 4,11],
		[11, 4,11,10,10,11, 4, 4, 4,11],
		[11, 4,11, 4, 4,11,11,11, 4,11],
		[11, 4, 9, 4, 4, 9,10,11, 4,11],
		[11, 4, 4, 4, 4, 4, 4,11, 4,11],
		[11,11,11,11,11,11,11,11,11,11]
	];
	//歩く速さ
	const WALKING_SPEED = 4;

	//変数sceneに、あなたはシーンですよ、と教える
	const scene = new Scene();

	//変数tilemapに、あなたはタイルマップですよ、と教える
	const tilemap = new Tilemap( 'img/tile.png' );
	//tilemap.dataに、どんなマップなのか教える
	tilemap.data = map;
	//マップを登録する
	scene.add( tilemap );

	//変数yamadaに、あなたは山田先生のタイルですよ、と教える
	const yamada = new Tile( 'img/yamada.png' );
	//sceneに、山田先生のタイルを追加して、とお願いする
	scene.add( yamada );

	//ループから常に呼び出される
	scene.onenterframe = () => {
		//キーが押されたとき、山田先生が移動する
		if ( game.input.left ) yamada.x -= WALKING_SPEED;
		if ( game.input.right ) yamada.x += WALKING_SPEED;
		if ( game.input.up ) yamada.y -= WALKING_SPEED;
		if ( game.input.down ) yamada.y += WALKING_SPEED;
	} //scene.onenterframe 終了

	//gameに、シーンを追加して、とお願いする
	game.add( scene );

	//gameに、ゲームをスタートして、とお願いする
	game.start();

} );

実行結果じっこうけっかはさきほどと変わりませんが、ブラウザ確認かくにんすると、このようになります。

Tileクラスを使って山田先生をタイルとして表示できる
山田

36行目のSpriteをTileに変更へんこうしたべ

りこ

あれ、Tileクラスには、updateメソッドも、renderメソッドもなかったような……
どうして山田先生のタイルが表示されるの?

山田

ふっふっふ。TileクラスはSpriteクラスが進化したものだからだべ

アル

もしかして、Spriteクラスのupdateメソッドやrenderメソッドが、Tileクラスでも使えるってこと!?

山田

そうだべ。TileクラスにはSpriteクラスの能力がそのまま引き継がれているんだべよ

りこ

そうなんだ、継承ってすごい!

山田

しかし……main.jsの38行目を見て欲しいべ

アル

タイルをシーンに追加してるところだ
でも、なにかダメなの?

山田

タイルはタイルマップ上に存在そんざいするものだべ
タイルと一緒いっしょに移動したり、タイルマップ上の何番目のタイルに、そのタイルがあるのかを取得したり……

タイルとタイルマップを、別々にシーンに追加していては、いろいろと不便ふべんだべよ

りこ

そっかぁ。でもどうすればいいの?

山田

これから教えるべよ!

タイルマップにタイルを追加できるようにしてみよう!

つづいて、タイルをシーンではなく、タイルマップに追加ついかできるようにしてみましょう!

山田

さて、さきほどはわたすのスプライトを、タイルとしてあつかってみたべ

りこ

継承けいしょうのやり方も覚えたね

山田

しかしだべ……タイルはシーンよりも、タイルマップに追加したほうが便利になるべな

りこ

そっか、タイルマップに追加すればよかったのね!

山田

では、Tilemapクラスに、タイルを追加するためのメソッドを作っていくべ!

js/engine/tilemap.js

'use strict'

/**
 * タイルマップに関するクラス
 */
class Tilemap {

	/**
	 * 引数
	 * img : 画像ファイルまでのパス
	 * size : タイルひとつの大きさ(一辺の長さ)
	 *
	 * タイルひとつは正方形にする
	 */
	constructor( img, size ) {
		//Imageのインスタンスを作成
		this.img = new Image();
		//this.img.srcに画像ファイルまでのパスを代入
		this.img.src = img;
		//画像の初期位置
		this.x = this.y = 0;
		//引数sizeが指定されていない場合、this.sizeに32を代入
		this.size = size || 32;
		//二次元配列で数値を入力すると、マップをつくることができる
		this.data = [];
		//タイルマップに重ねるように置きたいタイルを追加できる
		this.tiles = [];
	} //constructor() 終了

	/**
	 * タイルマップの上にタイルを重ねるように追加できるメソッド
	 *
	 * 引数
	 * tile : 追加したいタイル
	 */
	add( tile ) {
		//引数がTileのとき、this.tilesの末尾にtileを追加
		if ( tile instanceof Tile ) this.tiles.push( tile );
		//引数がTileでなければ、コンソールにエラーを表示
		else console.error( 'Tilemapに追加できるのはTileだけだよ!' );
	} //add() 終了

	/**Gameクラスのメインループからずっと呼び出され続ける
	 *
	 * 引数
	 * canvas : 紙(キャンバス)
	 */
	update( canvas ) {
		//画像などを画面に表示するためのメソッドを呼び出す
		this.render( canvas );
		//常に呼び出される、オーバーライド用のメソッドを呼び出す
		this.onenterframe();

		//タイルの数だけ繰り返す
		for ( let i=0; i<this.tiles.length; i++ ) {
			//それぞれのタイルのupdateメソッドを呼び出す
			this.tiles[i].update( canvas );
		}
	} //update() 終了

	/**
	 * Gameクラスのメインループからずっと呼び出され続ける。画像を表示したりするためのメソッド
	 *
	 * 引数
	 * canvas : 紙(キャンバス)
	 */
	render( canvas ) {
		//マップの縦方向の数だけ繰り返す
		for (let y=0; y<this.data.length; y++) {
			//タイルの縦の位置
			const _tileY = this.size * y + this.y;
			//タイルが、画面から縦にはみ出しているとき、この下をスキップして、次から繰り返し
			if ( _tileY < -1 * this.size || _tileY > canvas.height ) continue;

			//マップの横方向の数だけ繰り返す
			for (let x=0; x<this.data[y].length; x++) {
				//タイルの横の位置
				const _tileX = this.size * x + this.x
				//タイルが、画面から横にはみ出しているとき、この下をスキップして、次から繰り返し
				if ( _tileX < -1 * this.size || _tileX > canvas.width ) continue;

				//X方向に、何番目の画像か
				const _frameX = this.data[y][x] % ( this.img.width / this.size );
				//Y方向に、何番目の画像か
				const _frameY = ~~( this.data[y][x] / ( this.img.width / this.size ) );

				//画家さん(コンテキスト)を呼ぶ
				const _ctx = canvas.getContext( '2d' );

				//タイルを表示
				_ctx.drawImage(
					this.img,
					this.size * _frameX,
					this.size * _frameY,
					this.size,
					this.size,
					_tileX,
					_tileY,
					this.size,
					this.size
				);
			}
		}
	} //render() 終了

	/**
	 * 常に呼び出されるメソッド。空なのはオーバーライド(上書き)して使うため
	 */
	onenterframe() {}

}
山田

まず27行目で、タイルマップにかさねて配置はいちしたいタイルを入れておくためのものを、準備じゅんびしているんだべ

アル

ふむふむ

山田

そして36〜41行目が、そのタイルを追加するためのメソッドだべ

りこ

addメソッドを呼び出すことで、タイルを追加できるのね!
あ、追加できるのはTileだけに制限せいげんされてる!

山田

そうだべよ
そして追加したタイルのそれぞれのupdateメソッドを、55〜58行目で呼び出しているんだべ

りこ

そっか。このおかげで、追加したタイルを重ねて表示できるんだ

山田

じゃあ、実際じっさいにタイルマップにタイルを追加してみるべよ

js/main.js

'use strict'

//ブラウザがページを完全に読みこむまで待つ
addEventListener( 'load', () => {

	//変数gameに、あなたはゲームですよ、と教える
	const game = new Game();

	//マップの作成
	const map = [
		[11,11,11,11,11,11,11,11,11,11],
		[11,10,10,10,10,10,10,10,10,11],
		[11, 4, 4, 4, 4, 4, 4, 4, 4,11],
		[11, 4,11, 4, 4,11,11,11, 4,11],
		[11, 4,11,11,11,11,10,10, 4,11],
		[11, 4,11,10,10,11, 4, 4, 4,11],
		[11, 4,11, 4, 4,11,11,11, 4,11],
		[11, 4, 9, 4, 4, 9,10,11, 4,11],
		[11, 4, 4, 4, 4, 4, 4,11, 4,11],
		[11,11,11,11,11,11,11,11,11,11]
	];
	//歩く速さ
	const WALKING_SPEED = 4;

	//変数sceneに、あなたはシーンですよ、と教える
	const scene = new Scene();

	//変数tilemapに、あなたはタイルマップですよ、と教える
	const tilemap = new Tilemap( 'img/tile.png' );
	//tilemap.dataに、どんなマップなのか教える
	tilemap.data = map;
	//マップを登録する
	scene.add( tilemap );

	//変数yamadaに、あなたは山田先生のタイルですよ、と教える
	const yamada = new Tile( 'img/yamada.png' );
	//tilemapに、山田先生のタイルを追加して、とお願いする
	tilemap.add( yamada );

	//ループから常に呼び出される
	scene.onenterframe = () => {
		//キーが押されたとき、山田先生が移動する
		if ( game.input.left ) yamada.x -= WALKING_SPEED;
		if ( game.input.right ) yamada.x += WALKING_SPEED;
		if ( game.input.up ) yamada.y -= WALKING_SPEED;
		if ( game.input.down ) yamada.y += WALKING_SPEED;
	} //scene.onenterframe 終了

	//gameに、シーンを追加して、とお願いする
	game.add( scene );

	//gameに、ゲームをスタートして、とお願いする
	game.start();

} );
山田

38行目の、タイルの追加部分ついかぶぶんを、タイルマップに追加するように変更してみたべ

実行結果じっこうけっかに変わりはないだべけど、ブラウザ確認かくにんするとこうなるべ

タイルマップに山田先生をタイルとして追加できるようになる

移動のとき、キャラクターではなく、マップがスクロールするようにしてみよう!

つづいて、移動いどうのとき、キャラクターではなくマップが移動するようにしてみたいと思います。

山田

さてアルくん……

アル

は、はいっ

山田

もしもっと大きいマップにしたいとき、どうすればいいと思うべ?

アル

うーん、キャンバスからはみ出ちゃうよね……そうだ、キャンバスを大きくする!

山田

てやんでぇ、べらぼうめだべ!
こんどはキャンバスが画面からはみ出るべ

アル

はっ……たしかに

りこ

ふふっ、アルったらまだまだね

アル

むむ……じゃあ、りこちゃんならどうするのさ

りこ

マップの方を動かせばいいんじゃない?

アル

あ、よく考えたら、ポケモンもドラクエも、マップの方が動いてる!

山田

りこちゃん、正解だべ
では、方向キーが押されたときは、キャラクターじゃなくてマップの方が動くように、プログラムを作りかえていくべ

js/main.js

'use strict'

//ブラウザがページを完全に読みこむまで待つ
addEventListener( 'load', () => {

	//変数gameに、あなたはゲームですよ、と教える
	const game = new Game();

	//マップの作成
	const map = [
		[11,11,11,11,11,11,11,11,11,11],
		[11,10,10,10,10,10,10,10,10,11],
		[11, 4, 4, 4, 4, 4, 4, 4, 4,11],
		[11, 4,11, 4, 4,11,11,11, 4,11],
		[11, 4,11,11,11,11,10,10, 4,11],
		[11, 4,11,10,10,11, 4, 4, 4,11],
		[11, 4,11, 4, 4,11,11,11, 4,11],
		[11, 4, 9, 4, 4, 9,10,11, 4,11],
		[11, 4, 4, 4, 4, 4, 4,11, 4,11],
		[11,11,11,11,11,11,11,11,11,11]
	];
	//歩く速さ
	const WALKING_SPEED = 4;

	//変数sceneに、あなたはシーンですよ、と教える
	const scene = new Scene();

	//変数tilemapに、あなたはタイルマップですよ、と教える
	const tilemap = new Tilemap( 'img/tile.png' );
	//tilemap.dataに、どんなマップなのか教える
	tilemap.data = map;
	//マップを登録する
	scene.add( tilemap );

	//変数yamadaに、あなたは山田先生のタイルですよ、と教える
	const yamada = new Tile( 'img/yamada.png' );
	//tilemapに、山田先生のタイルを追加して、とお願いする
	tilemap.add( yamada );

	//ループから常に呼び出される
	scene.onenterframe = () => {
		//キーが押されたとき、山田先生(マップ)が移動する
		if ( game.input.left ) tilemap.x += WALKING_SPEED;
		if ( game.input.right ) tilemap.x -= WALKING_SPEED;
		if ( game.input.up ) tilemap.y += WALKING_SPEED;
		if ( game.input.down ) tilemap.y -= WALKING_SPEED;
	} //scene.onenterframe 終了

	//gameに、シーンを追加して、とお願いする
	game.add( scene );

	//gameに、ゲームをスタートして、とお願いする
	game.start();

} );
山田

42〜46行目で、わたすのタイルではなく、タイルマップが動くようにを変更したべよ

ブラウザ確認かくにんすると、こうなるべ

キャラクターではなくマップがスクロールするようになる!
りこ

わぁ、山田先生が動いてるように見える!

アル

うーん、でも先生の位置いちがすみっこだから、見た目がよくないね

山田

そうだべな
では、その位置をなおしていくべよ!

キャラクターとマップの初期位置を調節しよう!

つづいて、キャラクターとマップの最初さいしょ位置いちを、調節ちょうせつしてみましょう!

山田

今の状態じょうたいでは、わたすのタイルが左上にあり、しかもゲームがはじまったときはかべの上にってしまってる状態だべ

アル

いきなり壁の上からはじまったら、どうすればいいのか分からないよね

山田

そこで、キャラクターとマップの初期位置しょきいちをなおしていくべ!

js/main.js

'use strict'

//ブラウザがページを完全に読みこむまで待つ
addEventListener( 'load', () => {

	//変数gameに、あなたはゲームですよ、と教える
	const game = new Game();

	//マップの作成
	const map = [
		[11,11,11,11,11,11,11,11,11,11],
		[11,10,10,10,10,10,10,10,10,11],
		[11, 4, 4, 4, 4, 4, 4, 4, 4,11],
		[11, 4,11, 4, 4,11,11,11, 4,11],
		[11, 4,11,11,11,11,10,10, 4,11],
		[11, 4,11,10,10,11, 4, 4, 4,11],
		[11, 4,11, 4, 4,11,11,11, 4,11],
		[11, 4, 9, 4, 4, 9,10,11, 4,11],
		[11, 4, 4, 4, 4, 4, 4,11, 4,11],
		[11,11,11,11,11,11,11,11,11,11]
	];
	//タイルのサイズ
	const TILE_SIZE = 32;
	//歩く速さ
	const WALKING_SPEED = 4;

	//変数sceneに、あなたはシーンですよ、と教える
	const scene = new Scene();

	//変数tilemapに、あなたはタイルマップですよ、と教える
	const tilemap = new Tilemap( 'img/tile.png' );
	//tilemap.dataに、どんなマップなのか教える
	tilemap.data = map;
	//マップ全体の位置をずらす
	tilemap.x = TILE_SIZE*4 - TILE_SIZE/2;
	tilemap.y = TILE_SIZE*3 - TILE_SIZE/2;
	//マップを登録する
	scene.add( tilemap );

	//変数yamadaに、あなたは山田先生のタイルですよ、と教える
	const yamada = new Tile( 'img/yamada.png' );
	//山田先生を画面の中央に配置
	yamada.x = yamada.y = TILE_SIZE*5 - TILE_SIZE/2;
	//tilemapに、山田先生のタイルを追加して、とお願いする
	tilemap.add( yamada );

	//ループから常に呼び出される
	scene.onenterframe = () => {
		//キーが押されたとき、山田先生(マップ)が移動する
		if ( game.input.left ) tilemap.x += WALKING_SPEED;
		if ( game.input.right ) tilemap.x -= WALKING_SPEED;
		if ( game.input.up ) tilemap.y += WALKING_SPEED;
		if ( game.input.down ) tilemap.y -= WALKING_SPEED;
	} //scene.onenterframe 終了

	//gameに、シーンを追加して、とお願いする
	game.add( scene );

	//gameに、ゲームをスタートして、とお願いする
	game.start();

} );
りこ

まず23行目で、TILE_SIZEに32を代入してるのね

山田

うむ。タイルのサイズは一辺32pxに固定こていだべから、定数ていすうに代入しておくと、プログラムが分かりやすくなるべな

さらに35行目では、タイル4つ分から半分を引いた分、マップを右にずらしてるんだべ
そして36行目では、タイル3つ分から半分を引いた分、マップを下にずらしてるんだべよ

りこ

これでタイルマップの位置を調節してるんだ

山田

そして43行目で、わたすのタイルを、キャンバスの中心になるように設定せっていしてるんだべ
キャンバス全体に表示できるタイルの数は、縦横10まいだべから、タイル5つ分から半分を引いた位置が、ちょうどキャンバスの中心になるべよ

ブラウザで確認すると、こうなるべ!

山田先生が中央に表示されマップの位置もずらすことができる!

つぎのページでは、キャラクターを移動させるための機能をつくったり、タイルの間隔かんかくずつ移動させられるようにしていきます。

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

  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で、画像つきのおみくじゲームを作ろう!
オリジナルゲーム.com