Das erste Mal, dass ich Tetris schrieb, war vor mehr als einem Jahr, und ich hatte js erst vor nicht allzu langer Zeit gelernt.
Um mein Verständnis von JS zu stärken und meine Liebe zu Spielen zu fördern, habe ich Tetris auf Leinwandbasis geschrieben und dabei den einfachsten JS-Code verwendet, ohne auf die Ideen und Codes anderer Leute zu verweisen.
Während der Sommerferien meines Juniorjahres habe ich die Syntax von es6 verwendet, um sie zu verbessern, einschließlich des syntaktischen Zuckers der Klasse, der Pfeilfunktionen usw., um mein Verständnis von es6 weiter zu verbessern. Der Code umfasst mehr als 400 Zeilen.
Wenn Sie dieses kleine Spiel erstellen möchten, müssen Sie zunächst mit H5-Canvas, der JS-Array-Verarbeitung, der Überwachung und Verarbeitung von Tastaturereignissen, der Verwendung von Timern usw. vertraut sein. Der Rest ist die grundlegende Logikverarbeitung.
Die Spielregeln sind der Kern und die oberste Priorität unseres Codes
Die Kernlogik besteht hier darin, zu bestimmen, ob der Block kollidiert (der aktuell bewegte Block kollidiert mit dem bereits positionierten Block, sodass sich der aktuell bewegte Block nicht nach unten bewegen kann, da sich unser Block standardmäßig nach unten bewegt. Wenn er sich nicht nach unten bewegen kann, wird das Heruntergehen in Betracht gezogen als Block, der positioniert wurde, und dann wird ein neuer Block generiert und bewegt sich von der Anfangsposition weiter nach unten).
Und diese Kollision muss auch angewendet werden, wenn der Block während des Verformungsprozesses mit anderen positionierten Blöcken kollidiert, wir sollten verhindern, dass sich der Block erfolgreich verformt.
Der Code ist beigefügt, Diskussionen und Korrekturen sind willkommen.
<!DOCTYPE html><html lang=en><head> <meta charset=UTF-8> <title>es6-Rekonstruktion von Tetris (basierend auf Canvas)</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;'>Aktuelle Punktzahl: 0</div> <script type=text/javascript> /** * [Ein vollständiges Tetris-Klassendesign von magic_xiang] * @param {Zahl} Seite [Länge jeder Quadratseite (px), Standard 35] * @param {Zahl} Breite [Anzahl der Quadrate in einer Reihe (Zahl), Standard 20] * @param {Zahl} Höhe [Anzahl der in einer Spalte enthaltenen Blöcke (Standard 15)] * @param {number} speed [Geschwindigkeit der fallenden Blockbewegung (ms), Standard 400] */ class tetris{ constructionor(side=35, width=20, height=15 , speed=400){ this.side = side // Die Seitenlänge jedes Blocks this.width = width // Die Anzahl der in einer Zeile enthaltenen Blöcke this.height = height // Die Anzahl der in einer Spalte enthaltenen Blöcke this .speed = Geschwindigkeit // Die fallende Bewegungsgeschwindigkeit des Blocks this.num_blcok // Die numerische Variable des aktuellen Blocktyps this.type_color // Die String-Variable des aktuellen Farbtyps this.ident // Der Bezeichner von setInterval this.direction = 1 // Die Richtung des Blocks, initialisiert auf 1, Standardstatus this.grade = 0 // Wird zur Berechnung der Punkte verwendet this.over = false // Ob das Spiel vorbei ist this.arr_bX = [] // Speichern Sie die X-Koordinate des aktuellen Blocks this.arr_bY = [] // Speichere die Y-Koordinate des aktuellen Blocks this.arr_store_X = [] // Speichere die X-Koordinate aller Blöcke, die den Boden erreicht haben this.arr_store_Y = [] // Speichere die Y-Koordinate aller Blöcke, die den Boden erreicht haben this. arr_store_color = [] // Alle Blöcke speichern, die den Boden erreicht haben. Die Farbe des Quadrats this.paints = document.getElementById('tetris').getContext('2d') //Den Pinsel abrufen self = this } // Kapseln Sie die Paints-Methode, um den Code prägnanter zu gestalten. paintfr(x, y, scale=1){ this.paints.fillRect(x*this.side, y*this.side, scale*this.side, scale*this.side ) } // Das Spiel startet gameStart(){ this.init() this.run() } // Initialisierungsarbeit init(){ this.initBackground() this.initBlock() } // Der Block fällt automatisch run(){ this.ident = setInterval(self.down_speed_up(), this.speed) } // Initialisiere die Karte initBackground(){ this.paints.beginPath() this.paints.fillStyle='#000000 ' / /Die Füllfarbe der Karte ist Schwarz for(let i = 0; i < this.height; i++){ for(let j = 0; j < this.width; j++){ this.paintfr(j, i) } } this.paints.closePath() } // Position und Farbe des Blocks initialisieren initBlock(){ this.paints.beginPath() this.createRandom('rColor') // Einen Farbstring generieren, this.paints.fillStyle = this.type_color this.createRandom('rBlock') // Blocktypnummern generieren this.arr_bX.forEach((item, index) => { this.paintfr(item, this.arr_bY[index], 0.9) }) this.paints.closePath() } // Array verwenden, um Block zeichnen 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() } // Den bereits positionierten Block zeichnen drawStaticBlock( ){ this.arr_store_X.forEach((item, index) => { this.paints.beginPath() this.paints.fillStyle = this.arr_store_color[index] this.paintfr(item, this.arr_store_Y[index], 0.9) this.paints.closePath() }) } // Eine Zufallszahl generieren und den Blocktyp oder die Farbe zurückgeben type createRandom( type){ let temp = this.width/2-1 if (type == 'rBlock'){ //Wenn es sich um einen Block handelt, geben Sie 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) Bruchfall 2: this.arr_bX.push(temp,temp-1,temp-1,temp+1) this.arr_bY.push(1,0,1,1) unterbricht Fall 3: this.arr_bX.push(temp,temp-1,temp+1,temp+2) this.arr_bY.push(0,0,0, 0) Fall 4 unterbrechen: this.arr_bX.push(temp,temp-1,temp,temp+1) this.arr_bY.push(0,0,1,1) Fall unterbrechen 5: this.arr_bX.push(temp,temp+1,temp,temp+1) this.arr_bY.push(0,0,1,1) break } } if (type == 'rColor'){ //if Es ist der Farbtyp 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' Break-Fall 3: this.type_color='#2FE0BF' Break-Fall 4: this.type_color='red' Break-Fall 5: this.type_color='gray' Break-Fall 6: this.type_color ='#C932C6' unterbricht Fall 7: this.type_color= '#FC751B' unterbricht Fall 8: this.type_color= '#6E6EDD' break case 9: this.type_color= '#F4E9E1' break } } } // Bestimmen Sie, ob die Blöcke kollidieren (unten) und ob sie die untere Grenze während der Verformung überschreiten JudgeCollision_down(){ for(let i = 0 ; i < this.arr_bX.length; i++){ if (this.arr_bY[i] + 1 == this.height){ //Ob die untere Grenze während der Verformung überschritten wird return false } if (this.arr_store_X.length != 0) { //Bestimmen Sie, ob die Blöcke kollidieren (unten) 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 } //Beurteilen Sie, ob die Blöcke kollidieren (links und rechts) und ob sie während der Verformung die linken und rechten Grenzen überschreiten JudgeCollision_other(num){ for(let i = 0; i < this.arr_bX.length; i++){ if (num == 1) { //Ob die rechte Grenze während der Verformung überschritten wird if (this.arr_bX[i] == this.width - 1) return false } if (num == -1) { //Ob die linke Grenze während der Verformung überschritten wird if (this.arr_bX[i] == 0) return false } if (this.arr_store_X.length != 0) { / /Bestimmen Sie, ob die Blöcke kollidieren (links und rechts) 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; } //Die Beschleunigungsfunktion für 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. Länge) this.initBlock() } this.clearUnderBlock() this.drawBlock(this.type_color) this.drawStaticBlock() this.gameover() } //Die Richtungstaste ist die Linksbewegungsfunktion move(dir_temp){ this.initBackground() if (dir_temp == 1) { //Rechts 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() } //Der Richtungsschlüssel ist eine Raumtransformationsrichtungsfunktion up_change_direction(num_blcok){ if (num_blcok == 5) { return } let arr_tempX = [] let arr_tempY = [] //Da ich nicht weiß, ob die Transformation erfolgreich sein kann, speichere ich sie zuerst 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++ //Extrahiere die Mittelpunktskoordinaten und verforme sie durch The aktuelles Zentrum soll Vorrang haben let ax_temp = this.arr_bX[0] let ay_temp = this.arr_bY[0] this.arr_bX.splice(0, this.arr_bX.length) //Das Array löschen this.arr_bY.splice(0, this.arr_bY.length) if (num_blcok == 1) { switch(this.direction%4){ Fall 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) Break-Fall 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) Break-Fall 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) Fall 0 unterbrechen: 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) unterbricht Fall 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) Break-Fall 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) Fall 0 unterbrechen: 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) break } } 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) unterbricht Fall 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 ) Bruchfall 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) Fall 0 unterbrechen: 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) break } } 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) unterbricht Fall 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) Break-Fall 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) Fall 0 unterbrechen: 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) )) { // Wenn die Transformation nicht erfolgreich ist, führen Sie den folgenden Code aus: 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() } //Wenn eine Zeile voll ist, löschen Sie die Blöcke und die Y-Koordinate des oberen Blocks beträgt +1 clearUnderBlock(){ //Low-Level-Blöcke löschen 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 ) { //Note berechnen grade this.grade++ document.getElementById('text').innerHTML = 'Current grade:'+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) } //Lass den oberen Block ein Leerzeichen nach unten fallen 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 } } } //Beurteile das Ende des Spiels 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() //Richtungstastenfunktion document.onkeydown = (e) => { if (tetrisObj.over) return switch(e.keyCode){ case 40: // Die Richtung ist nach unten tetrisObj.down_speed_up() break case 32: // Leerzeichen zum Ändern der Richtung tetrisObj.initBackground() // Karte neu zeichnen tetrisObj.up_change_direction(tetrisObj.num_blcok) tetrisObj.drawBlock(tetrisObj.type_color) break case 37: // Die Richtung ist links tetrisObj.initBackground() tetrisObj .move (-1) tetrisObj.drawBlock(tetrisObj.type_color) break case 39: // Die Richtung ist richtig tetrisObj.initBackground() tetrisObj.move(1) tetrisObj.drawBlock(tetrisObj.type_color) break } } </script></body></ html >
Das Obige ist der gesamte Inhalt dieses Artikels. Ich hoffe, dass er für das Studium aller hilfreich ist. Ich hoffe auch, dass jeder das VeVb Wulin Network unterstützt.