初めてテトリスを書いたのは 1 年以上前で、つい最近まで JS を学んだばかりでした。
js への理解を深め、ゲームへの愛情をさらに高めるために、他の人のアイデアやコードを参照せずに、最も基本的な js コードを使用してキャンバスに基づいてテトリスを作成しました。
3 年生の夏休みに、es6 の構文を使用してクラスの糖衣構文やアロー関数などを改善しました。コードは 400 行以上ありました。
この小さなゲームを作成したい場合は、まず H5 キャンバス、js 配列処理、キーボード イベントの監視と処理、タイマーの使用などに精通する必要があります。残りは基本的なロジック処理です。
ゲームのルールはコードの中核であり、最優先事項です
ここでの中心となるロジックは、ブロックが衝突するかどうかを判断することです (現在移動しているブロックは、既に配置されているブロックと衝突するため、現在移動しているブロックは下に移動できません。これは、ブロックがデフォルトで下に移動するためです。下に移動できない場合は、下に歩くことが考慮されます)配置されたブロックとして、その後新しいブロックが生成され、初期位置から下に移動し続けます)。
また、この衝突は、ブロックが変形するときにも適用する必要があります。同様に、変形プロセス中にブロックが他の配置されたブロックと衝突する場合、ブロックが正常に変形しないようにする必要があります。
コードは添付されています。議論や修正は歓迎です。
<!DOCTYPE html><html lang=ja><head> <meta charset=UTF-8> <title>es6-テトリスの再構築 (キャンバスベース)</title> <style type=text/css> #tetris{ margin: 10px 250px;} </style></head><body> <canvas width=700 height=525 id=tetris></canvas> <div id=text style='color: red;font-size: 30px;'>現在のスコア: 0</div> <script type=text/javascript> /** * [magic_xiang による完全なテトリス クラス設計] * @param {数値} 辺 [各正方形の辺の長さ (ピクセル)、デフォルト 35] * @param {数値} 幅 [行内の正方形の数 (数値)、デフォルト 20] * @param {数値} 高さ[1列に含まれるブロック数(デフォルト15)] * @param {number}speed [ブロック落下移動速度(ms)、デフォルト400] */ class tetris{constructor(side=35, width=20, height=15) ,speed=400){ this.side = side // 各ブロックの辺の長さ this.width = width // 行に含まれるブロックの数 this.height = height // 列に含まれるブロックの数 this .speed = 速度 //ブロックの落下移動速度 this.num_blcok // 現在のブロックタイプの数値変数 this.type_color // 現在のカラータイプの文字列変数 this.ident // setInterval の識別子 this.direction = 1 //ブロックの方向、1 に初期化、デフォルトのステータス this.grade = 0 // スコアの計算に使用します this.over = false // ゲームが終了したかどうか this.arr_bX = [] // 現在のブロックの X 座標を保存しますthis.arr_bY = [] //現在のブロックの Y 座標を格納します this.arr_store_X = [] // 最下位に到達したすべてのブロックの X 座標を格納します this.arr_store_Y = [] // 最下位に到達したすべてのブロックの Y 座標を格納します。 arr_store_color = [] // 最下位に到達したすべてのブロックを格納 正方形の色 this.paints = document.getElementById('tetris').getContext('2d') // ブラシを取得 self = this } //コードをより簡潔にするためにペイント メソッドをカプセル化します。paintfr(x, y,scale=1){ this.paints.fillRect(x*this.side, y*this.side,scale*this.side,scale*this.side ) } // ゲーム開始 gameStart(){ this.init() this.run() } // 初期化作業 init(){ this.initBackground() this.initBlock() } //ブロックは自動的に落下します run(){ this.ident = setInterval(self.down_speed_up(), this.speed) } // マップを初期化します initBackground(){ this.paints.beginPath() this.paints.fillStyle='#000000 ' / /マップの塗りつぶしの色は黒です for(let i = 0; i < this.height; i++){ for(let j = 0; j < this.width; j++){ this.paintfr(j, i) } } this.paints.closePath() } // ブロックの位置と色を初期化します initBlock(){ this.paints.beginPath() this.createRandom('rColor') // カラー文字列を生成します。 this.paints.fillStyle = this.type_color this.createRandom('rBlock') // ブロック タイプ番号を生成します this.arr_bX.forEach((item,index) => { this.paintfr(item, this.arr_bY[index], 0.9) }) this.paints.closePath() } // 配列を使用して描画ブロックdrawBlock(color){ this.paints.beginPath() this.paints.fillStyle = color this.arr_bX.forEach((item,index) => { this.paintfr(item, this.arr_bY[index], 0.9) }) this.paints.closePath() } // すでに配置されているブロックを描画しますdrawStaticBlock( ){ this.arr_store_X.forEach((アイテム, インデックス) => { this.paints.beginPath() this.paints.fillStyle = this.arr_store_color[index] this.paintfr(item, this.arr_store_Y[index], 0.9) this.paints.closePath() }) } // 乱数を生成し、ブロックのタイプまたは色を返しますtype createRandom( type){ let temp = this.width/2-1 if (type == 'rBlock'){ //ブロック型の場合 this.num_blcok = Math.round(Math.random()*4+1) switch(this.num_blcok){ case 1: this.arr_bX.push(temp,temp-1,temp, temp+ 1) this.arr_bY.push(0,1,1,1) ブレークケース 2: this.arr_bX.push(temp,temp-1,temp-1,temp+1) this.arr_bY.push(1,0,1,1) ブレーク ケース 3: this.arr_bX.push(temp,temp-1,temp+1,temp+2) this.arr_bY.push(0,0,0, 0) ブレークケース 4: this.arr_bX.push(temp,temp-1,temp,temp+1) this.arr_bY.push(0,0,1,1) ブレークケース5: this.arr_bX.push(temp,temp+1,temp,temp+1) this.arr_bY.push(0,0,1,1) Break } } if (type == 'rColor'){ //ifこれは色のタイプです let num_color = Math.round(Math.random()*8+1) switch(num_color){ case 1: this.type_color='#3EF72A' Break case 2: this.type_color=' yellow' ブレーク ケース 3: this.type_color='#2FE0BF' ブレーク ケース 4: this.type_color=' red' ブレーク ケース 5: this.type_color='gray' ブレーク ケース 6: this.type_color ='#C932C6' ブレークケース 7: this.type_color= '#FC751B' ブレークケース 8: this.type_color= '#6E6EDD' ブレーク ケース 9: this.type_color= '#F4E9E1' ブレーク } } } // ブロックが衝突するかどうか (下側)、および変形中に下側の境界を横切るかどうかを判断します judgeCollision_down(){ for(let i = 0) ; i < this.arr_bX.length; i++){ if (this.arr_bY[i] + 1 == this.height){ //変形中に下限を越えるかどうか return false } if (this.arr_store_X.length != 0) { //ブロックが衝突するかどうかを判断する (下限) for(let j = 0; j < this.arr_store_X.length; j++ ){ if (this.arr_bX[i] == this.arr_store_X[j]) { if (this.arr_bY[i] + 1 ==) this.arr_store_Y[j]) { return false } } } } } return true } //ブロックが衝突(左右)しているか、変形中に左右の境界線をまたいでいるかを判定しますjudgeCollision_other(num){ for(let i = 0; i < this.arr_bX.length; i++){ if (num == 1) { //変形中に右境界を越えたかどうか if (this.arr_bX[i] == this.width - 1) return false } if (num == -1) { //変形中に左の境界を越えるかどうか if (this.arr_bX[i] == 0) return false } if (this.arr_store_X.length != 0) { / /ブロックが(左右で)衝突するかどうかを判断します for(let j = 0; j < this.arr_store_X.length; j++){ if (this.arr_bY[i] == this.arr_store_Y[j]) { if (this.arr_bX[i] + num == this.arr_store_X[j]) { return false } } } } return true } //加速関数 down_speed_up( ){ let flag_all_down = true flag_all_down = this.judgeCollision_down() if (flag_all_down) { this.initBackground() for(let i = 0; i < this.arr_bY.length; i++){ this.arr_bY[i] = this.arr_bY[i] + 1 } } else{ for(let i=0; i < this.arr_bX.length; i++){ this.arr_store_X.push(this.arr_bX[i]) this.arr_store_Y.push(this.arr_bY[i]) this.arr_store_color.push(this.type_color) } this.arr_bX.splice(0,this.arr_bX.length) this.arr_bY.splice(0,this.arr_bY.長さ) this.initBlock() } this.clearUnderBlock() this.drawBlock(this.type_color) this.drawStaticBlock() this.gameover() } //方向キーは左移動関数 move(dir_temp){ this.initBackground() if (dir_temp == 1) { //右let flag_all_right = true flag_all_right = this.judgeCollision_other(1) if (flag_all_right) { for(let i = 0; i < this.arr_bY.length; i++){ this.arr_bX[i] = this.arr_bX[i]+1 } } } else{ let flag_all_left = true flag_all_left = this.judgeCollision_other(-1) if (flag_all_left) { for(let i=0; i < this.arr_bY.length; i++){ this.arr_bX[i] = this.arr_bX[i]-1 } } } this.drawBlock(this.type_color) this.drawStaticBlock() } //方向キーは空間変換方向関数 up_change_direction(num_blcok){ if (num_blcok == 5) { return } let arr_tempX = [] let arr_tempY = [] //変換が成功するかどうかわからないので、最初に保存します for(let i = 0;i < this.arr_bX.length; i++){ arr_tempX.push(this.arr_bX[i]) arr_tempY.push(this.arr_bY[i]) } this.direction++ //中心座標を抽出し、次のように変形します。現在の中心が優先されます let ax_temp = this.arr_bX[0] let ay_temp = this.arr_bY[0] this.arr_bX.splice(0, this.arr_bX.length) //配列をクリア this.arr_bY.splice(0, this.arr_bY.length) if (num_blcok == 1) { switch(this.direction%4){ケース 1: this.arr_bX.push(ax_temp,ax_temp-1,ax_temp,ax_temp+1) this.arr_bY.push(ay_temp,ay_temp+1,ay_temp+1,ay_temp+1) ブレーク ケース 2: this.arr_bX.push(ax_temp,ax_temp-1,ax_temp,ax_temp) this.arr_bY.push(ay_temp,ay_temp, ay_temp-1,ay_temp+1) ブレークケース 3: this.arr_bX.push(ax_temp,ax_temp-1,ax_temp,ax_temp+1) this.arr_bY.push(ay_temp,ay_temp,ay_temp+1,ay_temp) ブレークケース 0: this.arr_bX.push(ax_temp,ax_temp,ax_temp, ax_temp+1) this.arr_bY.push(ay_temp,ay_temp-1,ay_temp+1,ay_temp) Break } } if (num_blcok == 2) { switch(this.direction%4){ case 1: this.arr_bX.push(ax_temp,ax_temp) -1、ax_temp-1、ax_temp+1) this.arr_bY.push(ay_temp,ay_temp,ay_temp-1,ay_temp) ブレーク ケース 2: this.arr_bX.push(ax_temp,ax_temp,ax_temp,ax_temp-1) this.arr_bY.push(ay_temp,ay_temp-1,ay_temp+ 1) ,ay_temp+1) ブレークケース 3: this.arr_bX.push(ax_temp,ax_temp-1,ax_temp+1,ax_temp+1) this.arr_bY.push(ay_temp,ay_temp,ay_temp,ay_temp+1) ブレークケース 0: this.arr_bX.push(ax_temp,ax_temp, ax_temp,ax_temp+1) this.arr_bY.push(ay_temp,ay_temp-1,ay_temp+1,ay_temp-1) ブレーク } } if (num_blcok == 3) { switch(this.direction%4){ case 1: this.arr_bX.push(ax_temp ,ax_temp-1,ax_temp+1,ax_temp+2) this.arr_bY.push(ay_temp,ay_temp,ay_temp,ay_temp) ブレーク ケース 2: this.arr_bX.push(ax_temp,ax_temp,ax_temp,ax_temp) this.arr_bY.push(ay_temp,ay_temp-1,ay_temp+1,ay_temp+ 2) ) ブレークケース 3: this.arr_bX.push(ax_temp,ax_temp-1,ax_temp+1,ax_temp+2) this.arr_bY.push(ay_temp,ay_temp,ay_temp,ay_temp) ブレークケース 0: this.arr_bX.push(ax_temp,ax_temp,ax_temp, ax_temp) this.arr_bY.push(ay_temp,ay_temp-1,ay_temp+1,ay_temp+2) ブレーク } } if (num_blcok == 4) { switch(this.direction%4){ case 1: this.arr_bX.push(ax_temp ,ax_temp-1,ax_temp,ax_temp+1) this.arr_bY.push(ay_temp,ay_temp,ay_temp+1,ay_temp+1) ブレークケース 2: this.arr_bX.push(ax_temp,ax_temp,ax_temp+1,ax_temp+1) this.arr_bY.push(ay_temp,ay_temp+ 1) ,ay_temp,ay_temp-1) ブレークケース 3: this.arr_bX.push(ax_temp,ax_temp,ax_temp-1,ax_temp+1) this.arr_bY.push(ay_temp,ay_temp-1,ay_temp,ay_temp-1) ブレークケース 0: this.arr_bX.push(ax_temp,ax_temp, ax_temp+1,ax_temp+1) this.arr_bY.push(ay_temp,ay_temp-1,ay_temp,ay_temp+1) Break } } if (! (this.judgeCollision_other(-1) && this.judgeCollision_down() && this.judgeCollision_other(1) )) { //変換が失敗した場合は、次のコード this.arr_bX.splice(0, this.arr_bX.length) を実行します。 this.arr_bY.splice(0, this.arr_bY.length) for(let i=0; i< arr_tempX.length; i++){ this.arr_bX.push(arr_tempX[i]) this.arr_bY.push(arr_tempY[i] ]) } } this.drawStaticBlock() } //行がいっぱいの場合、ブロックをクリアし、上のブロックの Y 座標は +1 になりますclearUnderBlock(){ //低レベルのブロックを削除 let arr_row=[] let line_num if (this.arr_store_X.length != 0) { for(let j = this.height-1; j >= 0; j--) { for (let i = 0; i < this.arr_store_color.length; i++){ if (this.arr_store_Y[i] == j) { arr_row.push(i) } } if (arr_row.length == this.width) { line_num = j Break }else{ arr_row.splice(0, arr_row.length) } } } if (arr_row.length == this.width ) { //成績を計算します this.grade++ document.getElementById('text').innerHTML = '現在の成績:'+this.grade for(let i = 0; i < arr_row.length; i++){ this.arr_store_X.splice(arr_row[i]-i, 1) this.arr_store_Y.splice(arr_row[i]-i, 1) this.arr_store_color。 splice(arr_row[i]-i, 1) } //上のブロックを 1 つ下のスペースにドロップします。 for(let i = 0; i < this.arr_store_color.length; i++){ if (this.arr_store_Y[i] < line_num) { this.arr_store_Y[i] = this.arr_store_Y[i]+1 } } } //ゲーム終了を判定 gameover() { for (let i=0; i < this.arr_store_X.length; i++){ if (this.arr_store_Y[i] == 0) { clearInterval(this.ident) this.over = true } } } } let tetrisObj = new tetris() tetrisObj.gameStart() //方向キー関数 document.onkeydown = (e) => { if (tetrisObj.over) return switch(e.keyCode){ case 40: // 方向は下です tetrisObj.down_speed_up() Break case 32: // 方向を変更するスペース tetrisObj.initBackground() // マップを再描画 tetrisObj.up_change_direction(tetrisObj.num_blcok) tetrisObj.drawBlock(tetrisObj.type_color) Break case 37: // 方向は左 tetrisObj.initBackground() tetrisObj .move (-1) tetrisObj.drawBlock(tetrisObj.type_color) Break case 39: // 方向は右です tetrisObj.initBackground() tetrisObj.move(1) tetrisObj.drawBlock(tetrisObj.type_color) Break } } </script></body></ html >
以上がこの記事の全内容です。皆様の学習のお役に立てれば幸いです。また、VeVb Wulin Network をご支援いただければ幸いです。