var ary = [
[ 1, 2, 3, 4, 5 ],
[ 10, 20, 30, 40, 50 ],
[ 100,200,300,400,500 ]
];
| X→ Y↓ | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| 0 | 1 | 2 | 3 | 4 | 5 |
| 1 | 10 | 20 | 30 | 40 | 50 |
| 2 | 100 | 200 | 300 | 400 | 500 |
| 01 | <!DOCTYPE html> |
|---|---|
| 02 | <html lang="ja"> |
| 03 | <head> |
| 04 | <meta charset="utf-8"> |
| 05 | <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0 user-scalable=no"> |
| 06 | <title>JavaScriptのテストプログラム</title> |
| 07 | </head> |
| 08 | <body> |
| 09 | <canvas style="position:absolute; top:0; bottom:0; left:0; right:0; margin:auto;" id="bg"></canvas> |
| 10 | <script> |
| 11 | var winW = window.innerWidth; |
| 12 | var winH = window.innerHeight; |
| 13 | var canvas = document.getElementById("bg"); |
| 14 | canvas.width = winW; |
| 15 | canvas.height = winH; |
| 16 | var cnt = canvas.getContext("2d"); |
| 17 | var SCALE = winW / 384; |
| 18 | cnt.scale( SCALE, SCALE ); |
| 19 | cnt.font = "24px monospace"; |
| 20 | cnt.textAlign = "center"; |
| 21 | cnt.textBaseline = "middle"; |
| 22 | |
| 23 | //画像ファイル用の配列 |
| 24 | var img; |
| 25 | var imgPre; |
| 26 | |
| 27 | function loadImg() {//画像を読み込む |
| 28 | imgPre = false; |
| 29 | img = new Image(); |
| 30 | img.src = "example241.png"; |
| 31 | img.onload = function() { |
| 32 | imgPre = true; |
| 33 | drawMap(); |
| 34 | } |
| 35 | } |
| 36 | |
| 37 | function drawChip( no, dx, dy ) {//画像を切り出し表示 |
| 38 | if( imgPre != true ) return; |
| 39 | var sx = 32*no; |
| 40 | var sy = 0; |
| 41 | cnt.drawImage( img, sx, sy, 32, 32, dx, dy, 32, 32 ); |
| 42 | } |
| 43 | |
| 44 | //変数の宣言 |
| 45 | var mapNo = 0; |
| 46 | var MAP_DATA = [ |
| 47 | |
| 48 | [ 9, 5, 5, 9, 7, 7, 7, 7, 9, 5, 5, 9 ], |
| 49 | [ 9, 6, 6, 9, 8, 8, 8, 8, 9, 6, 6, 9 ], |
| 50 | [ 9, 0, 0, 9, 3, 3, 3, 3, 9, 0, 0, 9 ], |
| 51 | [ 9, 0, 0, 9, 3, 3, 3, 3, 9, 0, 0, 9 ], |
| 52 | [ 9, 0, 0, 5, 3, 3, 3, 3, 5, 0, 0, 9 ], |
| 53 | [ 9, 0, 0, 6, 1, 1, 1, 1, 6, 0, 0, 9 ], |
| 54 | [ 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5 ], |
| 55 | [ 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6 ], |
| 56 | |
| 57 | [ 5, 7, 7, 5, 5, 7, 7, 5, 5, 7, 7, 5 ], |
| 58 | [ 6, 8, 8, 6, 6, 8, 8, 6, 6, 8, 8, 6 ], |
| 59 | [ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ], |
| 60 | [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], |
| 61 | [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], |
| 62 | [ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ], |
| 63 | [ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 ], |
| 64 | [ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 ], |
| 65 | |
| 66 | [ 4, 4, 4, 4, 5, 5, 5, 5, 4, 4, 4, 4 ], |
| 67 | [ 4, 4, 4, 5, 6, 6, 6, 6, 5, 4, 4, 4 ], |
| 68 | [ 4, 4, 4, 6, 0, 0, 0, 0, 6, 4, 4, 4 ], |
| 69 | [ 4, 4, 4, 5, 0, 3, 3, 0, 5, 4, 4, 4 ], |
| 70 | [ 4, 4, 4, 6, 0, 3, 3, 0, 6, 4, 4, 4 ], |
| 71 | [ 4, 4, 4, 5, 0, 0, 0, 0, 5, 4, 4, 4 ], |
| 72 | [ 4, 4, 4, 6, 1, 1, 1, 1, 6, 4, 4, 4 ], |
| 73 | [ 4, 4, 4, 5, 1, 1, 1, 1, 5, 4, 4, 4 ], |
| 74 | |
| 75 | ]; |
| 76 | |
| 77 | function drawMap() { |
| 78 | var x, y; |
| 79 | for( y = 0; y < 8; y ++ ) { |
| 80 | for( x = 0; x < 12; x ++ ) drawChip( MAP_DATA[mapNo*8+y][x], x*32, y*32 ); |
| 81 | } |
| 82 | |
| 83 | cnt.fillStyle = "#000"; cnt.fillText( "マップの番号 " + mapNo, 197, 24 ); |
| 84 | cnt.fillStyle = "#fff"; cnt.fillText( "マップの番号 " + mapNo, 197, 23 ); |
| 85 | } |
| 86 | |
| 87 | //画面をタップ(クリック)した判定 |
| 88 | canvas.addEventListener( "click", onClick ); |
| 89 | function onClick(event) { |
| 90 | event.preventDefault(); |
| 91 | mapNo ++; |
| 92 | if( mapNo == 3 ) mapNo = 0; |
| 93 | drawMap(); |
| 94 | } |
| 95 | |
| 96 | window.onload = loadImg(); |
| 97 | </script> |
| 98 | </body> |
| 99 | </html> |

画像は一枚にまとめよう

マップチップは1枚の画像に並べており、読み込んでいるファイルはこの1枚です。
マップデータの値からマップチップを切り出し表示しています。
1-8 (2) で触れましたが、JavaScriptでは画像枚数が増えるほど読み込みに失敗する可能性が高くなります。
HTML5+JavaScriptで開発するゲームでは、私達の経験から、JavaScript起動時に読み込むのは数枚程度にしておくのが無難です。
コンピューターの性能や通信速度、ソフトウェアの技術は日々進歩していますので、
いずれネット上の多くの画像を瞬時に扱えるようになるでしょうが、
今のところ画像はできるだけ一つのファイルにまとめましょう。
| 01 | <!DOCTYPE html> |
|---|---|
| 02 | <html lang="ja"> |
| 03 | <head> |
| 04 | <meta charset="utf-8"> |
| 05 | <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0 user-scalable=no"> |
| 06 | <title>JavaScriptのテストプログラム</title> |
| 07 | </head> |
| 08 | <body> |
| 09 | <canvas style="position:absolute; top:0; bottom:0; left:0; right:0; margin:auto;" id="bg"></canvas> |
| 10 | <script> |
| 11 | var winW = window.innerWidth; |
| 12 | var winH = window.innerHeight; |
| 13 | var canvas = document.getElementById("bg"); |
| 14 | canvas.width = winW; |
| 15 | canvas.height = winH; |
| 16 | var cnt = canvas.getContext("2d"); |
| 17 | var SCALE = winW / 384; |
| 18 | cnt.scale( SCALE, SCALE ); |
| 19 | cnt.font = "32px monospace"; |
| 20 | cnt.textAlign = "center"; |
| 21 | cnt.textBaseline = "middle"; |
| 22 | |
| 23 | //マウスとタップの判定 |
| 24 | var tapX = 0, tapY = 0, tapC = 0; |
| 25 | |
| 26 | canvas.addEventListener( "touchstart", touchStart ); |
| 27 | canvas.addEventListener( "touchmove", touchMove ); |
| 28 | canvas.addEventListener( "touchend", touchEnd ); |
| 29 | function touchStart(event) { |
| 30 | event.preventDefault(); |
| 31 | var rect = event.target.getBoundingClientRect(); |
| 32 | tapX = event.touches[0].clientX-rect.left; |
| 33 | tapY = event.touches[0].clientY-rect.top; |
| 34 | transformXY(); |
| 35 | tapC = 1; |
| 36 | } |
| 37 | function touchMove(event) { |
| 38 | event.preventDefault(); |
| 39 | var rect = event.target.getBoundingClientRect(); |
| 40 | tapX = event.touches[0].clientX-rect.left; |
| 41 | tapY = event.touches[0].clientY-rect.top; |
| 42 | transformXY(); |
| 43 | } |
| 44 | function touchEnd(event) { tapC = 0; } |
| 45 | |
| 46 | canvas.addEventListener( "mousedown", mouseDown ); |
| 47 | canvas.addEventListener( "mousemove", mouseMove ); |
| 48 | canvas.addEventListener( "mouseup", mouseUp ); |
| 49 | function mouseDown(event) { |
| 50 | var rect = event.target.getBoundingClientRect(); |
| 51 | tapX = event.clientX-rect.left; |
| 52 | tapY = event.clientY-rect.top; |
| 53 | transformXY(); |
| 54 | tapC = 1; |
| 55 | } |
| 56 | function mouseMove(event) { |
| 57 | var rect = event.target.getBoundingClientRect(); |
| 58 | tapX = event.clientX-rect.left; |
| 59 | tapY = event.clientY-rect.top; |
| 60 | transformXY(); |
| 61 | } |
| 62 | function mouseUp(event) { tapC = 0; } |
| 63 | |
| 64 | function transformXY() {//実座標→仮想座標への変換 |
| 65 | tapX = toInt(tapX/SCALE); |
| 66 | tapY = toInt(tapY/SCALE); |
| 67 | } |
| 68 | |
| 69 | //キー入力 |
| 70 | var key = 0; |
| 71 | window.onkeydown = function(event) { key = event.keyCode; } |
| 72 | window.onkeyup = function(event) { key = 0; } |
| 73 | |
| 74 | function toInt( val ) {//整数を返す関数 |
| 75 | return parseInt(val); |
| 76 | } |
| 77 | |
| 78 | //画像ファイル用の配列 |
| 79 | var img = []; |
| 80 | var imgPre = []; |
| 81 | |
| 82 | function loadImg( n ) {//画像を読み込む |
| 83 | imgPre[n] = false; |
| 84 | img[n] = new Image(); |
| 85 | img[n].src = "example242_" + n + ".png"; |
| 86 | img[n].onload = function() { imgPre[n] = true; } |
| 87 | } |
| 88 | |
| 89 | function drawImg( chip, dx, dy ) {//マップチップを切り出し表示 |
| 90 | if( imgPre[0] != true ) return; |
| 91 | var sx = 1+33*chip; |
| 92 | var sy = 1; |
| 93 | cnt.drawImage( img[0], sx, sy, 32, 32, dx, dy, 32+1, 32+1 ); |
| 94 | } |
| 95 | |
| 96 | function drawImg2( x, y ) {//木の上の部分を表示 |
| 97 | if( imgPre[0] != true ) return; |
| 98 | cnt.drawImage( img[0], 0, 34, 82, 88, x*32-26, y*32-80, 82, 88 ) |
| 99 | } |
| 100 | |
| 101 | function drawCharacter( dx, dy, dir, ptn ) {//キャラクターを表示 |
| 102 | if( imgPre[1] != true ) return; |
| 103 | var sx = 32*ptn; |
| 104 | var sy = 54*dir; |
| 105 | cnt.drawImage( img[1], sx, sy, 32, 54, dx, dy, 32, 54 ); |
| 106 | } |
| 107 | |
| 108 | function fText( str, x, y, col ) {//文字表示 |
| 109 | cnt.fillStyle = col; |
| 110 | cnt.fillText( str, x, y ); |
| 111 | } |
| 112 | |
| 113 | function fRect( x, y, w, h, col ) {//矩形 |
| 114 | cnt.fillStyle = col; |
| 115 | cnt.fillRect( x, y, w, h ); |
| 116 | } |
| 117 | |
| 118 | function setAlp( per ) {//透明度 |
| 119 | cnt.globalAlpha = per/100; |
| 120 | } |
| 121 | |
| 122 | function drawBtn( str, x, y, w, h ) {//ボタン |
| 123 | fRect( x, y, w, h, "#fff" ); |
| 124 | fRect( x+1, y+1, w-1, h-1, "#abb" ); |
| 125 | fRect( x+1, y+1, w-2, h-2, "#cdd" ); |
| 126 | fText( str, x+w/2, y+h/2, "#000" ); |
| 127 | if( x < tapX && tapX < x+w && y < tapY && tapY < y+h ) { |
| 128 | if( tapC == 1 ) |
| 129 | setAlp(60); |
| 130 | else |
| 131 | setAlp(20); |
| 132 | fRect( x, y, w, h, "#fff" ); |
| 133 | setAlp(100); |
| 134 | return tapC; |
| 135 | } |
| 136 | return 0; |
| 137 | } |
| 138 | |
| 139 | //変数の宣言 |
| 140 | var idx = 0; |
| 141 | var tmr = 0; |
| 142 | |
| 143 | var playerX, playerY, playerD, playerStep; |
| 144 | var WALK_PTN = [ 0, 1, 0, 2 ]; |
| 145 | |
| 146 | var MAP_DATA = [ |
| 147 | [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ], |
| 148 | [ 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 2, 1 ], |
| 149 | [ 1, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 1 ], |
| 150 | [ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 ], |
| 151 | [ 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1 ], |
| 152 | [ 1, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, 1 ], |
| 153 | [ 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1 ], |
| 154 | [ 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ], |
| 155 | ]; |
| 156 | |
| 157 | function hitChkBG( x, y ) { |
| 158 | var cx = toInt(x/32); |
| 159 | var cy = toInt(y/32); |
| 160 | return MAP_DATA[cy][cx]; |
| 161 | } |
| 162 | |
| 163 | window.onload = moveChr(); |
| 164 | function moveChr() { |
| 165 | var x, y; |
| 166 | tmr ++; |
| 167 | |
| 168 | switch( idx ) { |
| 169 | case 0://初期化処理 |
| 170 | loadImg(0);//背景画像の読み込み |
| 171 | loadImg(1);//キャラクター画像の読み込み |
| 172 | playerX = 5*32; |
| 173 | playerY = 4*32; |
| 174 | playerD = 1; |
| 175 | playerStep = 0; |
| 176 | idx = 1; |
| 177 | break; |
| 178 | |
| 179 | case 1://移動処理 |
| 180 | if( playerStep == 0 ) {//停止中 |
| 181 | if( key == 38 || drawBtn( "↑", 96, 256, 192, 48 ) == 1 ) { |
| 182 | playerD = 0; |
| 183 | if( hitChkBG( playerX, playerY-32 ) == 0 ) playerStep = 4; |
| 184 | } |
| 185 | if( key == 40 || drawBtn( "↓", 96, 304, 192, 48 ) == 1 ) { |
| 186 | playerD = 1; |
| 187 | if( hitChkBG( playerX, playerY+32 ) == 0 ) playerStep = 4; |
| 188 | } |
| 189 | if( key == 37 || drawBtn( "←", 0, 256, 96, 96 ) == 1 ) { |
| 190 | playerD = 2; |
| 191 | if( hitChkBG( playerX-32, playerY ) == 0 ) playerStep = 4; |
| 192 | } |
| 193 | if( key == 39 || drawBtn( "→", 288, 256, 96, 96 ) == 1 ) { |
| 194 | playerD = 3; |
| 195 | if( hitChkBG( playerX+32, playerY ) == 0 ) playerStep = 4; |
| 196 | } |
| 197 | } |
| 198 | if( playerStep != 0 ) {//歩行中 |
| 199 | if( playerD == 0 ) playerY -= 8; |
| 200 | if( playerD == 1 ) playerY += 8; |
| 201 | if( playerD == 2 ) playerX -= 8; |
| 202 | if( playerD == 3 ) playerX += 8; |
| 203 | playerStep --; |
| 204 | } |
| 205 | |
| 206 | for( y = 0; y < 8; y ++ ) { |
| 207 | for( x = 0; x < 12; x ++ ) drawImg( MAP_DATA[y][x], x*32, y*32 ); |
| 208 | } |
| 209 | drawCharacter( playerX, playerY-28, playerD, WALK_PTN[tmr%4] ); |
| 210 | for( y = 0; y < 8; y ++ ) { |
| 211 | for( x = 0; x < 12; x ++ ) if( MAP_DATA[y][x] == 2 ) drawImg2( x, y ); |
| 212 | } |
| 213 | break; |
| 214 | } |
| 215 | |
| 216 | setTimeout( moveChr, 100 ); |
| 217 | } |
| 218 | </script> |
| 219 | </body> |
| 220 | </html> |
![]() |
![]() |
function hitChkBG( x, y ) {
var cx = toInt(x/32);
var cy = toInt(y/32);
return MAP_DATA[cy][cx];
}
| 変数名 | 用途 | 値の説明 |
|---|---|---|
| playerX, playerY | XY座標 | マップチップは32ドット角なので、playerX,playerYいずれかが32変化すれば隣のチップ上に移る |
| playerD | 向き | 0上向き、1下向き、2左向き、3右向きとしている |
| playerStep | 移動するためのフラグ | 0の時は停止しており、1~4の時に移動する |
①定義したマップデータから背景を描く
↓
②キャラクターを描く
↓
③再度マップデータを調べ、上に表示するものがあればそれを描く
マップチップを切り出し表示した時の筋を消すテクニック
(1)の例ではブラウザやお使いの端末によっては画面に筋が表示されます。これは
①画像ファイルに並んでいる隣り合ったチップの一部が表示される
②キャンバスに表示する際にチップ間に隙間が生じる
という原因で起こります。
(2)の例では
①を回避するため画像ファイルに並べるマップチップを1ドットずつ離す(離した隙間は透明色にする)
②を回避するためチップを表示する際に縦横に1ドット伸ばして表示する
という方法で筋が入らないようにしています。
