情報学部 菅沼ホーム JavaScript 目次 基礎技術目次 索引

背景(マップ)

  1. マップの使用

      アニメーションやゲームにおいて,背景画像上で様々なターゲットを移動させたいような場合が存在します.その際,ターゲットと共に背景画像自体を移動するような場合も少なくありません.それを実現する一つの方法は,大きな背景画像を準備し,それを背景として使用することです.しかし,そのようにすると,ターゲットと背景画像の位置関係を明確にするのが面倒になります.

      そこでよく使用されるのが,全体を格子状に区切り,区切られた各ブロックに適切な画像を配置したり,画像を描画することによって背景を構成する方法です.このようにすれば,ターゲットが格子内のどこに存在するかによって,背景画像との位置関係を知ることができ,障害物等に対する処理も容易になります.

      背景の例は,12 行 × 26 列の格子内に 2 種類の画像は配置し,横方向に移動した例です.以下にそのプログラムを示します.このプログラムは,「跳ね返りと乱数」の章におけるもう一つの例と同様,新しいオブジェクトを生成し,変数や関数をそのオブジェクト内で定義し,それらをグローバル変数のような形では直接参照できないようにしています.
    01	<!DOCTYPE HTML>
    02	<HTML>
    03	<HEAD>
    04		<TITLE>背景(マップ)</TITLE>
    05		<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
    06		<META NAME=viewport CONTENT="width=device-width, initial-scale=1">
    07		<LINK REL="stylesheet" TYPE="text/css" HREF="../../master.css">
    08		<SCRIPT TYPE="text/javascript">
    09			canvas  = null;
    10			ctx     = null;
    11			mp      = null;
    12			timerID = -1;
    13	
    14			function start()
    15			{
    16								// キャンバス情報
    17				canvas = document.getElementById('canvas_e');   // キャンバス要素の取得
    18				ctx    = canvas.getContext('2d');   // キャンバスからコンテキストを取得
    19								// Map オブジェクト
    20				mp = new Map();
    21								// タイマーのスタート
    22				timerID = setInterval('draw()', 30);
    23			}
    24						// 描画
    25			function draw()
    26			{
    27								// キャンバスのクリア
    28				ctx.clearRect(0, 0, canvas.width, canvas.height);
    29								// 移動
    30				mp.x += mp.v_x;
    31								// 背景の描画
    32				for (let i1 = 0; i1 < mp.col; i1++) {
    33					let x = mp.x + mp.width * i1;
    34					if (x + mp.width >= 0 && x <= canvas.width) {
    35						for (let i2 = 0; i2 < mp.row; i2++) {
    36							if (mp.map[i2][i1] > 0) {
    37								let y = i2 * mp.height;
    38								ctx.drawImage(mp.image[mp.map[i2][i1]-1], x, y);
    39							}
    40						}
    41					}
    42				}
    43			}
    44						// Map オブジェクト(プロパティ)
    45			function Map()
    46			{
    47								// 画像の配置
    48				this.map = new Array();
    49				this.map[0] = new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
    50				this.map[1] = new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
    51				this.map[2] = new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
    52				this.map[3] = new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
    53				this.map[4] = new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
    54				this.map[5] = new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
    55				this.map[6] = new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
    56				this.map[7] = new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
    57				this.map[8] = new Array(1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2);
    58				this.map[9] = new Array(1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2);
    59				this.map[10] = new Array(1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2);
    60				this.map[11] = new Array(1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2);
    61				this.image = new Array();   // ブロック画像
    62				this.row = 12;   // ブロックの行数
    63				this.col = 26;   // ブロックの列数
    64				this.x = 0;   // 背景の位置
    65				this.v_x = -2;   // 背景の水平方向移動速度
    66				this.width = 25;   // ブロックの幅
    67				this.height = 25;   // ブロックの高さ
    68						// ブロック画像の読み込み
    69				this.image[0] = new Image();
    70				this.image[0].src = "obj1.jpg";
    71				this.image[1] = new Image();
    72				this.image[1].src = "obj2.jpg";
    73			}
    74		</SCRIPT>
    75	</HEAD>
    76	<BODY CLASS="white" STYLE="text-align: center" onLoad="start()">
    77		<H1>背景(マップ)</H1>
    78		<CANVAS ID="canvas_e" STYLE="background-color: #eeffee;" WIDTH="400" HEIGHT="300"></CANVAS>
    79	</BODY>
    80	</HTML>
    			
    20 行目

      Map オブジェクトを生成し,変数 mp に代入しています.

    30 行目

      マップを左に 2 ピクセル移動しています.このように,Map オブジェクトのプロパティは,mp.x,mp.v_x などのように,「 mp. 」を付加した形だけで参照可能です.

    32 行目

      この for 文によって,マップの各列に対して 33 行目~ 41 行目の処理が繰り返されます.

    33 行目

      i1 列にあるブロックの左端の x 座標です.

    34 行目

      この if 文で,i1 列にあるブロックがキャンバス内に存在するか否かをチェックしています.存在する場合は,以下の処理が実行されます.

    35 行目

      この for 文によって,マップの各行に対して 35 行目~ 40 行目の処理が繰り返されます.

    36 行目~ 39 行目

      マップのブロックの値が正である箇所に,指定された画像を描画しています.

    48 行目~ 60 行目

      12 行 26 列のマップを 2 次元配列を使用して定義しています.このように,配列を定義し( 48 行目),その各要素を再び配列として定義する(この例の場合は,初期設定も行っている)ことによって,2 次元配列を定義することができます.値が 0 の箇所には何も表示されず,1 または 2 の箇所には,画像が表示されます.

      「ビットマップ,フィルタ,外部画像」の章におけるビットマップの節においては,1 次元配列を 2 次元配列のような形で扱いました.逆に,2 次元配列が連続した領域に確保されていれば,2 次元配列を 1 次元配列のように取り扱うことが基本的に可能です.実際,C/C++ の場合は可能ですが,JavaScript の場合は,多次元配列が連続した領域に確保されるという保証もなく,そのような処理を行うことはできません.しかし,例えば,
    	map1 = map[8];				
    のような記述によって,2 次元配列 map の 9 行目だけを 1 次元配列として扱うことが可能です(任意の行において可能).ただし,map1 と map[8] は,同じ場所(行に対するデータが記憶されている場所の先頭)を指しているポインタとしてみなすことができるため,例えば,map1[4] の値を変更すれば,map[8][4] も変化します(逆も同様).

    62 行目~ 63 行目

      マップの行数と列数です.

    65 行目

      マップの移動速度です.

    66 行目~ 67 行目

      マップを構成するブロックの大きさです.

    69 行目~ 72 行目

      マップに必要な画像を読み込んでいます.

  2. もう一つの例

      この例では,マップを,繰り返し,切れ目なく表示しています.基本的には,同じマップを 2 つ用意して,それらの表示・非表示,及び,その位置を制御することによって実現しています.
    01	<!DOCTYPE HTML>
    02	<HTML>
    03	<HEAD>
    04		<TITLE>背景(マップ)</TITLE>
    05		<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
    06		<META NAME=viewport CONTENT="width=device-width, initial-scale=1">
    07		<LINK REL="stylesheet" TYPE="text/css" HREF="../../master.css">
    08		<SCRIPT TYPE="text/javascript">
    09			canvas  = null;
    10			ctx     = null;
    11			mp      = new Array();
    12			timerID = -1;
    13			dsp     = new Array(1, 0);
    14	
    15			function start()
    16			{
    17								// キャンバス情報
    18				canvas = document.getElementById('canvas_e');   // キャンバス要素の取得
    19				ctx    = canvas.getContext('2d');   // キャンバスからコンテキストを取得
    20								// Map オブジェクト
    21				mp[0]   = new Map();
    22				mp[1]   = new Map();
    23				mp[1].x = canvas.width;
    24								// タイマーのスタート
    25				timerID = setInterval('draw()', 30);
    26			}
    27						// 描画
    28			function draw()
    29			{
    30								// キャンバスのクリア
    31				ctx.clearRect(0, 0, canvas.width, canvas.height);
    32								// 移動
    33				for (let i1 = 0; i1 < 2; i1++) {
    34					let k = (i1 + 1) % 2;
    35					if (dsp[i1] > 0) {
    36						mp[i1].x += mp[i1].v_x;
    37						if (mp[i1].x + mp[i1].width * mp[i1].col <= canvas.width) {
    38							mp[k].x = mp[i1].x + mp[i1].width * mp[i1].col;
    39							dsp[k]  = 1;
    40						}
    41						else if (mp[i1].x + mp[i1].width * mp[i1].col <= 0)
    42							dsp[i1] = 0;
    43					}
    44				}
    45								// 背景の描画
    46				for (let i0 = 0; i0 < 2; i0 ++) {
    47					if (dsp[i0] > 0) {
    48						for (let i1 = 0; i1 < mp[i0].col; i1++) {
    49							let x = mp[i0].x + mp[i0].width * i1;
    50							if (x + mp[i0].width >= 0 && x <= canvas.width) {
    51								for (let i2 = 0; i2 < mp[i0].row; i2++) {
    52									if (mp[i0].map[i2][i1] > 0) {
    53										let y = i2 * mp[i0].height;
    54										ctx.drawImage(mp[i0].image[mp[i0].map[i2][i1]-1], x, y);
    55									}
    56								}
    57							}
    58						}
    59					}
    60				}
    61			}
    62						// Map オブジェクト(プロパティ)
    63			function Map()
    64			{
    65								// 画像の配置
    66				this.map = new Array();
    67				this.map[0] = new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
    68				this.map[1] = new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
    69				this.map[2] = new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
    70				this.map[3] = new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
    71				this.map[4] = new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
    72				this.map[5] = new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
    73				this.map[6] = new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
    74				this.map[7] = new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
    75				this.map[8] = new Array(1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2);
    76				this.map[9] = new Array(1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2);
    77				this.map[10] = new Array(1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2);
    78				this.map[11] = new Array(1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2);
    79				this.image  = new Array();   // ブロック画像
    80				this.row    = 12;   // ブロックの行数
    81				this.col    = 26;   // ブロックの列数
    82				this.x      = 0;   // 背景の位置
    83				this.v_x    = -2;   // 背景の水平方向移動速度
    84				this.width  = 25;   // ブロックの幅
    85				this.height = 25;   // ブロックの高さ
    86						// ブロック画像の読み込み
    87				this.image[0]     = new Image();
    88				this.image[0].src = "obj1.jpg";
    89				this.image[1]     = new Image();
    90				this.image[1].src = "obj2.jpg";
    91			}
    92		</SCRIPT>
    93	</HEAD>
    94	<BODY CLASS="white" STYLE="text-align: center" onLoad="start()">
    95		<H1>背景(マップ)</H1>
    96		<CANVAS ID="canvas_e" STYLE="background-color: #eeffee;" WIDTH="400" HEIGHT="300"></CANVAS>
    97	</BODY>
    98	</HTML>
    			
    11 行目

      2 つのマップを記憶するための配列です.

    13 行目

      各マップが表示されているか否かを表すための配列です(各要素を,1 と 0 で初期設定).1 が表示,0 が非表示状態であることを表します.初期状態では,ここに示すように,1 番目のマップだけが表示状態となっています.

    21 行目~ 23 行目

      同じ内容の 2 つの Map オブジェクトを生成し,配列 mp の各要素に代入し,2 番目のマップの位置をキャンバスの右外に設定しています.同じ内容のマップであるからと言って,

      mp[0] = new Map();
      mp[1] = mp[0];

    のようには記述しないでください.mp[0] に代入されるのは,生成されたオブジェクトのアドレスです.従って,上のように記述すると,mp[0] と mp[1] は,全く同じマップを示すことになってしまいます.ここで記憶したいのは,内容は同じであっても,異なるマップ(データ)である必要があります.さもないと,各マップの表示・非表示や位置を独立に制御できません.

    33 行目

      この for 文によって,各マップの表示・非表示や位置を制御しています.

    34 行目

      変数 k の値は,i1 が 0 の時は 1,1 の時は 0 になります.

    35 行目

      この if 文によって,マップ i1 が表示されているとき,36 行目~ 42 行目の処理が実行されます.

    37 行目~ 40 行目

      表示されているマップの右端がキャンバス内に入ったとき,あと一つのマップの位置をキャンバスの右端とし,表示状態にします.

    41 行目~ 42 行目

      表示されているマップの右端がキャンバスの左外に出たとき,マップを非表示状態にします.

    46 行目~ 60 行目

      表示されているマップに対してだけ,先ほどの例と同様,各ブロックに指定された画像を表示します.

情報学部 菅沼ホーム JavaScript 目次 基礎技術目次 索引