テトリス風ゲーム【javascript】

画像1

こんな感じのテトリス風ゲームができます

html

<!DOCTYPE html>
<html>
 <head>
   <meta charset="UTF-8">
   <title>テトリス風ゲーム</title>
   <meta name="viewport" content="width=device-width,initial-scale=1">
   <link rel="stylesheet" type="text/css" href="style.css">
   <script type="text/javascript" src="main.js"></script>
 </head>

 <body>
   <h1>テトリス風ゲーム</h1>
   <div class="wrapper-container">
       <span class="tetris-container">
           <canvas id="stage" width="250px" height="500px" style="background-color:black;">
           </canvas>
           <span class="tetris-panel-container">
             <p>Next:</p>
               <canvas id="next" width="150px" height="150px" style="background-color:black;">
               </canvas>
             <p>LINES:<span id="lines">0</span></p>
             <p><span id="message"></span></p>
             <div class="tetris-panel-container-padding"></div>
             <table class="tetris-button-panel">
               <tr>
                 <td></td>
                 <td id="tetris-rotate-button" class="tetris-button">↻</td>
                 <td></td>
               </tr>
               <tr>
                 <td id="tetris-move-left-button"class="tetris-button">←</td>
                 <td id="tetris-fall-button"class="tetris-button">↓</td>
                 <td id="tetris-move-right-button"class="tetris-button">→</td>
               </tr>
             </table>
           </span>
       </span>
   </div>
   <script>
     var tetris = new Tetris();
     tetris.startGame();
   </script>
 </body>
</html>

<!--
   参考WEBサイト
   http://kmaebashi.com/index.html
-->

js

class Tetris {
   constructor() {
       this.stageWidth = 10;
       this.stageHeight = 20;
       this.stageCanvas = document.getElementById("stage");
       this.nextCanvas = document.getElementById("next");
       let cellWidth = this.stageCanvas.width / this.stageWidth;
       let cellHeight = this.stageCanvas.height / this.stageHeight;
       this.cellSize = cellWidth < cellHeight ? cellWidth : cellHeight;
       this.stageLeftPadding = (this.stageCanvas.width - this.cellSize * this.stageWidth) / 2;
       this.stageTopPadding = (this.stageCanvas.height - this.cellSize * this.stageHeight) / 2;
       this.blocks = this.createBlocks();
       this.deletedLines = 0;

       window.onkeydown = (e) => {
           if (e.keyCode === 37) {
               this.moveLeft();
           } else if (e.keyCode === 38) {
               this.rotate();
           } else if (e.keyCode === 39) {
               this.moveRight();
           } else if (e.keyCode === 40) {
               this.fall();
           }
       }

       document.getElementById("tetris-move-left-button").onmousedown = (e) => {
           this.moveLeft();
       }
       document.getElementById("tetris-rotate-button").onmousedown = (e) => {
           this.rotate();
       }
       document.getElementById("tetris-move-right-button").onmousedown = (e) => {
           this.moveRight();
       }
       document.getElementById("tetris-fall-button").onmousedown = (e) => {
           this.fall();
       }
   }

   createBlocks() {
       let blocks = [
           {
               shape: [[[-1, 0], [0, 0], [1, 0], [2, 0]],
                       [[0, -1], [0, 0], [0, 1], [0, 2]],
                       [[-1, 0], [0, 0], [1, 0], [2, 0]],
                       [[0, -1], [0, 0], [0, 1], [0, 2]]],
               color: "rgb(0, 255, 255)"
           },
           {
               shape: [[[0, 0], [1, 0], [0, 1], [1, 1]],
                       [[0, 0], [1, 0], [0, 1], [1, 1]],
                       [[0, 0], [1, 0], [0, 1], [1, 1]],
                       [[0, 0], [1, 0], [0, 1], [1, 1]]],
               color: "rgb(255, 255, 0)"
           },
           {
               shape: [[[0, 0], [1, 0], [-1, 1], [0, 1]],
                       [[-1, -1], [-1, 0], [0, 0], [0, 1]],
                       [[0, 0], [1, 0], [-1, 1], [0, 1]],
                       [[-1, -1], [-1, 0], [0, 0], [0, 1]]],
               color: "rgb(0, 255, 0)"
           },
           {
               shape: [[[-1, 0], [0, 0], [0, 1], [1, 1]],
                       [[0, -1], [-1, 0], [0, 0], [-1, 1]],
                       [[-1, 0], [0, 0], [0, 1], [1, 1]],
                       [[0, -1], [-1, 0], [0, 0], [-1, 1]]],
               color: "rgb(255, 0, 0)",
           },
           {
               shape: [[[-1, -1], [-1, 0], [0, 0], [1, 0]],
                       [[0, -1], [1, -1], [0, 0], [0, 1]],
                       [[-1, 0], [0, 0], [1, 0], [1, 1]],
                       [[0, -1], [0, 0], [-1, 1], [0, 1]]],
               color: "rgb(0, 0, 255)",
           },
           {
               shape: [[[1, -1], [-1, 0], [0, 0], [1, 0]],
                       [[0, -1], [0, 0], [0, 1], [1, 1]],
                       [[-1, 0], [0, 0], [1, 0], [-1, 1]],
                       [[-1, -1], [0, -1], [0, 0], [0, 1]]],
               color: "rgb(255, 165, 0)",
           },
           {
               shape: [[[0, -1], [-1, 0], [0, 0], [1, 0]],
                       [[0, -1], [0, 0], [1, 0], [0, 1]],
                       [[-1, 0], [0, 0], [1, 0], [0, 1]],
                       [[0, -1], [-1, 0], [0, 0], [0, 1]]],
               color: "rgb(255, 0, 255)",
           }
       ];
       return blocks;
   }

   drawBlock(x, y, type, angle, canvas) {
       let context = canvas.getContext("2d");
       let block = this.blocks[type];

       for (let i = 0; i < block.shape[angle].length; i++) {
           this.drawCell(context,
                    x + (block.shape[angle][i][0] * this.cellSize),
                    y + (block.shape[angle][i][1] * this.cellSize),
                    this.cellSize,
                    type);
       }
   }

   drawCell(context, cellX, cellY, cellSize, type) {
       let block = this.blocks[type];
       let adjustedX = cellX + 0.5;
       let adjustedY = cellY + 0.5;
       let adjustedSize = cellSize - 1;
       context.fillStyle = block.color;
       context.fillRect(adjustedX, adjustedY, adjustedSize, adjustedSize);
      
   }

   startGame() {
       let virtualStage = new Array(this.stageWidth);
       for (let i = 0; i < this.stageWidth; i++) {
           virtualStage[i] = new Array(this.stageHeight).fill(null);
       }
       this.virtualStage = virtualStage;
       this.currentBlock = null;
       this.nextBlock = this.getRandomBlock();
       this.mainLoop();
   }

   mainLoop() {
       if (this.currentBlock == null) {
           if (!this.createNewBlock()) {
               return;
           }
       } else {
           this.fallBlock();
       }
       this.drawStage();
       if (this.currentBlock != null) {
           this.drawBlock(this.stageLeftPadding + this.blockX * this.cellSize,
               this.stageTopPadding + this.blockY * this.cellSize,
               this.currentBlock, this.blockAngle, this.stageCanvas);
       }
       setTimeout(this.mainLoop.bind(this), 500);
   }

   createNewBlock() {
       this.currentBlock = this.nextBlock;
       this.nextBlock = this.getRandomBlock();
       this.blockX = Math.floor(this.stageWidth / 2 - 2);
       this.blockY = 0;
       this.blockAngle = 0;
       this.drawNextBlock();
       if (!this.checkBlockMove(this.blockX, this.blockY, this.currentBlock, this.blockAngle)) {
           let messageElem = document.getElementById("message");
           messageElem.innerText = "GAME OVER";
           return false;
       }
       return true;
   }

   drawNextBlock() {
       this.clear(this.nextCanvas);
       this.drawBlock(this.cellSize * 2, this.cellSize, this.nextBlock,
           0, this.nextCanvas);
   }

   getRandomBlock() {
       return  Math.floor(Math.random() * 7);
   }

   fallBlock() {
       if (this.checkBlockMove(this.blockX, this.blockY + 1, this.currentBlock, this.blockAngle)) {
           this.blockY++;
       } else {
           this.fixBlock(this.blockX, this.blockY, this.currentBlock, this.blockAngle);
           this.currentBlock = null;
       }
   }

   checkBlockMove(x, y, type, angle) {
       for (let i = 0; i < this.blocks[type].shape[angle].length; i++) {
           let cellX = x + this.blocks[type].shape[angle][i][0];
           let cellY = y + this.blocks[type].shape[angle][i][1];
           if (cellX < 0 || cellX > this.stageWidth - 1) {
               return false;
           }
           if (cellY > this.stageHeight - 1) {
               return false;
           }
           if (this.virtualStage[cellX][cellY] != null) {
               return false;
           }
       }
       return true;
   }

   fixBlock(x, y, type, angle) {
       for (let i = 0; i < this.blocks[type].shape[angle].length; i++) {
           let cellX = x + this.blocks[type].shape[angle][i][0];
           let cellY = y + this.blocks[type].shape[angle][i][1];
           if (cellY >= 0) {
               this.virtualStage[cellX][cellY] = type;
           }
       }
       for (let y = this.stageHeight - 1; y >= 0; ) {
           let filled = true;
           for (let x = 0; x < this.stageWidth; x++) {
               if (this.virtualStage[x][y] == null) {
                   filled = false;
                   break;
               }
           }
           if (filled) {
               for (let y2 = y; y2 > 0; y2--) {
                   for (let x = 0; x < this.stageWidth; x++) {
                       this.virtualStage[x][y2] = this.virtualStage[x][y2 - 1];
                   }
               }
               for (let x = 0; x < this.stageWidth; x++) {
                   this.virtualStage[x][0] = null;
               }
           let linesElem = document.getElementById("lines");
               this.deletedLines++;
               linesElem.innerText = "" + this.deletedLines;
           } else {
               y--;
           }
       }
   }

   drawStage() {
       this.clear(this.stageCanvas);

       let context = this.stageCanvas.getContext("2d");
       for (let x = 0; x < this.virtualStage.length; x++) {
           for (let y = 0; y < this.virtualStage[x].length; y++) {
               if (this.virtualStage[x][y] != null) {
                   this.drawCell(context,
                       this.stageLeftPadding + (x * this.cellSize),
                       this.stageTopPadding + (y * this.cellSize),
                       this.cellSize,
                       this.virtualStage[x][y]);
               }
           }
       }
   }

   moveLeft() {
       if (this.checkBlockMove(this.blockX - 1, this.blockY, this.currentBlock, this.blockAngle)) {
           this.blockX--;
           this.refreshStage();
       }
   }

   moveRight() {
       if (this.checkBlockMove(this.blockX + 1, this.blockY, this.currentBlock, this.blockAngle)) {
           this.blockX++;
           this.refreshStage();
       }
   }

   rotate() {
       let newAngle;
       if (this.blockAngle < 3) {
           newAngle = this.blockAngle + 1;
       } else {
           newAngle = 0;
       }
       if (this.checkBlockMove(this.blockX, this.blockY, this.currentBlock, newAngle)) {
           this.blockAngle = newAngle;
           this.refreshStage();
       }
   }

   fall() {
       while (this.checkBlockMove(this.blockX, this.blockY + 1, this.currentBlock, this.blockAngle)) {
           this.blockY++;
           this.refreshStage();
       }
   }

   refreshStage() {
     this.clear(this.stageCanvas);
     this.drawStage();
     this.drawBlock(this.stageLeftPadding + this.blockX * this.cellSize,
               this.stageTopPadding + this.blockY * this.cellSize,
               this.currentBlock, this.blockAngle, this.stageCanvas);
   }

   clear(canvas) {
       let context = canvas.getContext("2d");
       context.fillStyle = "rgb(0, 0, 0)";
       context.fillRect(0, 0, canvas.width, canvas.height);
   }
}

css

html {
   touch-action: manipulation;
}

.wrapper-container {
   display: inline-block;
}

.tetris-container {
   display: flex;
   flex-direction: row;
   margin: 10px;
   background-color: #333333;
}

.tetris-panel-container {
   display: flex;
   padding-left: 10px;
   padding-right: 10px;
   flex-direction: column;
   color: white;
   background-color: #333333;
}

.tetris-panel-container-padding {
   flex-grow: 1;
}

.tetris-panel-container p {
  margin: 0;
  padding: 0;
}

.tetris-button-panel {
   border-style: none;
   width: 100%;
}

.tetris-button {
   padding-top: 10px;
   padding-bottom: 10px;
   text-align: center;
   background: #444444;
}

この記事が気に入ったらサポートをしてみませんか?