前提・実現したいこと
html、jsを用いてブラウザ上で動作するテトリスゲームを作成しています。
キー入力による左右移動時について、障害物があるときのブロック移動を制限したいです。
発生している問題・エラーメッセージ
左右のセルに何もない状態においてブロックは問題なく左右移動します。
発生する問題は以下の二点です。
1.落下中のブロックが左右両端の列にあり左右移動を試みると、ブロックが変形します。
2.落下中のブロックが左右移動を試みると、固定されたブロックを貫通します。
main.js:208 Uncaught TypeError: Cannot set property 'className' of undefined at moveLeft (main.js:208) at HTMLDocument.onKeyDown (main.js:181) main.js:193 Uncaught TypeError: Cannot set property 'className' of undefined at moveRight (main.js:193) at HTMLDocument.onKeyDown (main.js:183)
該当のソースコード
lang
1document.getElementById("hello_text").textContent = "はじめてのJavaScript"; 2 3// キーボードイベントを監視する 4document.addEventListener("keydown", onKeyDown); 5 6var count = 0; 7 8var cells; 9 10// ブロックのパターン 11var blocks = { 12 i: { 13 class: "i", 14 pattern: [ 15 [1, 1, 1, 1] 16 ] 17 }, 18 o: { 19 class: "o", 20 pattern: [ 21 [1, 1], 22 [1, 1] 23 ] 24 }, 25 t: { 26 class: "t", 27 pattern: [ 28 [0, 1, 0], 29 [1, 1, 1] 30 ] 31 }, 32 s: { 33 class: "s", 34 pattern: [ 35 [0, 1, 1], 36 [1, 1, 0] 37 ] 38 }, 39 z: { 40 class: "z", 41 pattern: [ 42 [1, 1, 0], 43 [0, 1, 1] 44 ] 45 }, 46 j: { 47 class: "j", 48 pattern: [ 49 [1, 0, 0], 50 [1, 1, 1] 51 ] 52 }, 53 l: { 54 class: "l", 55 pattern: [ 56 [0, 0, 1], 57 [1, 1, 1] 58 ] 59 } 60}; 61 62loadTable(); 63setInterval(function () { 64 count++; 65 document.getElementById("hello_text").textContent = "はじめてのJavaScript(" + count + ")"; 66 if (hasFallingBlock()) { // 落下中のブロックがあるか確認する 67 fallBlocks();// あればブロックを落とす 68 } else { // なければ 69 deleteRow();// そろっている行を消す 70 generateBlock();// ランダムにブロックを作成する 71 } 72}, 200); 73 74/* ------ ここから下は関数の宣言部分 ------ */ 75 76function loadTable() { 77 cells = []; 78 var td_array = document.getElementsByTagName("td"); 79 var index = 0; 80 for (var row = 0; row < 20; row++) { 81 cells[row] = []; 82 for (var col = 0; col < 10; col++) { 83 cells[row][col] = td_array[index]; 84 index++; 85 } 86 } 87 88} 89 90function fallBlocks() { 91 // 1. 底についていないか? 92 for (var col = 0; col < 10; col++) { 93 if (cells[19][col].blockNum === fallingBlockNum) { 94 isFalling = false; 95 return; // 一番下の行にブロックがいるので落とさない 96 } 97 } 98 // 2. 1マス下に別のブロックがないか? 99 for (var row = 18; row >= 0; row--) { 100 for (var col = 0; col < 10; col++) { 101 if (cells[row][col].blockNum === fallingBlockNum) { 102 if (cells[row + 1][col].className !== "" && cells[row + 1][col].blockNum !== fallingBlockNum){ 103 isFalling = false; 104 return; // 一つ下のマスにブロックがいるので落とさない 105 } 106 } 107 } 108 } 109 // 下から二番目の行から繰り返しクラスを下げていく 110 for (var row = 18; row >= 0; row--) { 111 for (var col = 0; col < 10; col++) { 112 if (cells[row][col].blockNum === fallingBlockNum) { 113 cells[row + 1][col].className = cells[row][col].className; 114 cells[row + 1][col].blockNum = cells[row][col].blockNum; 115 cells[row][col].className = ""; 116 cells[row][col].blockNum = null; 117 } 118 } 119 } 120} 121 122var isFalling = false; 123function hasFallingBlock() { 124 // 落下中のブロックがあるか確認する 125 return isFalling; 126} 127 128function deleteRow() { 129 // そろっている行を消す 130 for (var row = 19; row >= 0; row--) { 131 var canDelete = true; 132 for (var col = 0; col < 10; col++) { 133 if (cells[row][col].className === "") { 134 canDelete = false; 135 } 136 } 137 if (canDelete) { 138 // 1行消す 139 for (var col = 0; col < 10; col++) { 140 cells[row][col].className = ""; 141 } 142 // 上の行のブロックをすべて1マス落とす 143 for (var downRow = row - 1; row >= 0; row--) { 144 for (var col = 0; col < 10; col++) { 145 cells[downRow + 1][col].className = cells[downRow][col].className; 146 cells[downRow + 1][col].blockNum = cells[downRow][col].blockNum; 147 cells[downRow][col].className = ""; 148 cells[downRow][col].blockNum = null; 149 } 150 } 151 } 152 } 153} 154 155var fallingBlockNum = 0; 156function generateBlock() { 157 // ランダムにブロックを生成する 158 // 1. ブロックパターンからランダムに一つパターンを選ぶ 159 var keys = Object.keys(blocks); 160 var nextBlockKey = keys[Math.floor(Math.random() * keys.length)]; 161 var nextBlock = blocks[nextBlockKey]; 162 var nextFallingBlockNum = fallingBlockNum + 1; 163 // 2. 選んだパターンをもとにブロックを配置する 164 var pattern = nextBlock.pattern; 165 for (var row = 0; row < pattern.length; row++) { 166 for (var col = 0; col < pattern[row].length; col++) { 167 if (pattern[row][col]) { 168 cells[row][col + 3].className = nextBlock.class; 169 cells[row][col + 3].blockNum = nextFallingBlockNum; 170 } 171 } 172 } 173 // 3. 落下中のブロックがあるとする 174 isFalling = true; 175 fallingBlockNum = nextFallingBlockNum; 176} 177 178// キー入力によってそれぞれの関数を呼び出す 179function onKeyDown(event) { 180 if (event.keyCode === 37) { 181 moveLeft(); 182 } else if (event.keyCode === 39) { 183 moveRight(); 184 } 185} 186 187function moveRight() { 188 denyMv(); 189 // ブロックを右に移動させる 190 for (var row = 0; row < 20; row++) { 191 for (var col = 9; col >= 0; col--) { 192 if (cells[row][col].blockNum === fallingBlockNum) { 193 cells[row][col + 1].className = cells[row][col].className; 194 cells[row][col + 1].blockNum = cells[row][col].blockNum; 195 cells[row][col].className = ""; 196 cells[row][col].blockNum = null; 197 } 198 } 199 } 200} 201 202function moveLeft() { 203 denyMv() 204 // ブロックを左に移動させる 205 for (var row = 0; row < 20; row++) { 206 for (var col = 0; col < 10; col++) { 207 if (cells[row][col].blockNum === fallingBlockNum) { 208 cells[row][col - 1].className = cells[row][col].className; 209 cells[row][col - 1].blockNum = cells[row][col].blockNum; 210 cells[row][col].className = ""; 211 cells[row][col].blockNum = null; 212 } 213 } 214 } 215} 216 217var isRlDenied = false 218function denyMv() { 219 //障害があるとき左右に移動させない 220 //1. 左右に障害がないか? 221 for(var row = 0; row < 20; row++){ 222 if(cells[row][9].blockNum === fallingBlockNum) { 223 isRlDenied = true; 224 return; //左右に障害があるので動かさない 225 } 226 } 227} 228
lang
1<!DOCTYPE html> 2<html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 <link rel="stylesheet" href="./css/style.css"> 7 </head> 8 <body> 9 <h1 id="hello_text">Hello World</h1> 10 <table> 11 <tr> <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td> </tr> 12 <tr> <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td> </tr> 13 <tr> <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td> </tr> 14 <tr> <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td> </tr> 15 <tr> <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td> </tr> 16 <tr> <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td> </tr> 17 <tr> <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td> </tr> 18 <tr> <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td> </tr> 19 <tr> <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td> </tr> 20 <tr> <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td> </tr> 21 <tr> <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td> </tr> 22 <tr> <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td> </tr> 23 <tr> <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td> </tr> 24 <tr> <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td> </tr> 25 <tr> <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td> </tr> 26 <tr> <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td> </tr> 27 <tr> <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td> </tr> 28 <tr> <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td> </tr> 29 <tr> <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td> </tr> 30 <tr> <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td> </tr> 31 </table> 32 <script src="./js/main.js"></script> 33 </body> 34</html>
lang
1body { 2 background-color: #263238; 3} 4 5h1 { 6 color: #FFFFFF; 7} 8 9table { 10 width: 400px; /* 表の幅 */ 11 height: 800px; /* 表の高さ */ 12 /* 中央揃えにする */ 13 margin-left: auto; 14 margin-right: auto; 15} 16 17td { 18 width: 10%; /* セルの幅 */ 19 height: 5%; /* セルの高さ */ 20 background-color: #455A64; /* 背景色 */ 21} 22 23.i { 24 background-color: #00BCD4; /* cyan */ 25} 26 27.o { 28 background-color: #FFEB3B; /* yellow */ 29} 30 31.t { 32 background-color: #9C27B0; /* purple */ 33} 34 35.s { 36 background-color: #4CAF50; /* green */ 37} 38 39.z { 40 background-color: #F44336; /* red */ 41} 42 43.j { 44 background-color: #2196F3; /* blue */ 45} 46 47.l { 48 background-color: #FF9800; /* orange */ 49}
試したこと
ある企業のエンジニアブログを参考にしているのですがそのサイトにブロック左右移動制限についてはコードが書かれていませんでした。しかし定義したfallBlocksをヒントに作成せよとのことなのでソースコード最下部にisRlDeniedを追加し、moveLeft、moveRightで呼び出していますが状況は変わりません。
###詳細情報
以下、ファイルを置いたアドレスです。
https://s3-ap-northeast-1.amazonaws.com/ttls/index.html
以下、参考にしたブログサイトです。
http://tech.mti.co.jp/entry/2017/07/21/608/
###追記
上記2つのバグは解決しました。
ご回答ありがとうございました。
AWS上のファイルはバグ修正済みです
回答2件
あなたの回答
tips
プレビュー