La première fois que j'ai écrit Tetris, c'était il y a plus d'un an, et je venais d'apprendre js il n'y a pas longtemps.
Afin de renforcer ma compréhension de js et d'ajouter mon amour pour les jeux, j'ai écrit Tetris basé sur Canvas en utilisant le code js le plus basique sans faire référence aux idées et aux codes des autres.
Pendant les vacances d'été de ma première année, j'ai utilisé la syntaxe d'es6 pour l'améliorer, y compris le sucre syntaxique de la classe, les fonctions fléchées, etc., pour approfondir davantage ma compréhension d'es6. Le code contient plus de 400 lignes.
Si vous souhaitez réaliser ce petit jeu, vous devez d'abord être familier avec le canevas H5, le traitement des tableaux js, la surveillance et le traitement des événements de clavier, l'utilisation de la minuterie, etc.
Les règles du jeu sont au cœur et la priorité absolue de notre code
La logique de base ici est de déterminer si le bloc entre en collision (le bloc actuellement en mouvement entre en collision avec le bloc déjà positionné de sorte que le bloc en cours de mouvement ne peut pas descendre, car notre bloc se déplace vers le bas par défaut. S'il ne peut pas descendre, la descente est considérée comme un bloc qui a été positionné, puis un nouveau bloc est généré et continue de descendre depuis la position initiale).
Et cette collision doit également être appliquée lorsque le bloc est déformé. De même, si le bloc entre en collision avec d'autres blocs positionnés pendant le processus de déformation, nous devons empêcher le bloc de se déformer correctement.
Le code est joint, les discussions et corrections sont les bienvenues.
<!DOCTYPE html><html lang=fr><head> <meta charset=UTF-8> <title>es6-Reconstruction de Tetris (basé sur canevas)</title> <style type=text/css> #tetris{ marge : 10px 250px;} </style></head><body> <canvas width=700 height=525 id=tetris></canvas> <div id=text style='color: red;font-size: 30px;'>Score actuel : 0</div> <script type=text/javascript> /** * [Une conception complète de classe Tetris par magic_xiang] * @param {numéro} côté [longueur de chaque côté du carré (px), par défaut 35] * @param {numéro} largeur [nombre de carrés dans une rangée (nombre), par défaut 20] * @param {numéro} hauteur [Nombre de blocs inclus dans une colonne (par défaut 15)] * @param {number} speed [Vitesse de mouvement de chute du bloc (ms), par défaut 400] */ class tetris{ constructor(side=35, width=20, height=15 , speed=400){ this.side = side // La longueur du côté de chaque bloc this.width = width // Le nombre de blocs contenus dans une ligne this.height = height // Le nombre de blocs contenus dans une colonne this .speed = vitesse // La vitesse de déplacement décroissante du bloc this.num_blcok // La variable numérique du type de bloc actuel this.type_color // La variable chaîne du type de couleur actuel this.ident // L'identifiant de setInterval this.direction = 1 // Le direction du bloc, initialisé à 1, par défaut Status this.grade = 0 // Utilisé pour calculer les scores this.over = false // Si le jeu est terminé this.arr_bX = [] // Stocke la coordonnée X du bloc actuel this.arr_bY = [] // Stocke la coordonnée Y du bloc actuel this.arr_store_X = [] // Stocke la coordonnée X de tous les blocs qui ont atteint le bas this.arr_store_Y = [] // Stocke la coordonnée Y de tous les blocs qui ont atteint le bas this. arr_store_color = [] // Stocke tous les blocs qui ont atteint le bas La couleur du carré this.paints = document.getElementById('tetris').getContext('2d') //Récupère le pinceau self = this } // Encapsulez la méthode paints pour rendre le code plus concis paintfr(x, y, scale=1){ this.paints.fillRect(x*this.side, y*this.side, scale*this.side, scale*this.side ) } // Le jeu démarre gameStart(){ this.init() this.run() } // Travail d'initialisation init(){ this.initBackground() this.initBlock() } // Le bloc tombe automatiquement run(){ this.ident = setInterval(self.down_speed_up(), this.speed) } // Initialise la carte initBackground(){ this.paints.beginPath() this.paints.fillStyle='#000000 ' / /La couleur de remplissage de la carte est noire for(let i = 0; i < this.height; i++){ for(let j = 0; j < this.width; j++){ this.paintfr(j, i) } } this.paints.closePath() } // Initialise la position et la couleur du bloc initBlock(){ this.paints.beginPath() this.createRandom('rColor') // Génère une chaîne de couleur, this.paints.fillStyle = this.type_color this.createRandom('rBlock') // Générer des numéros de type de bloc this.arr_bX.forEach((item, index) => { this.paintfr(item, this.arr_bY[index], 0.9) }) this.paints.closePath() } // Utiliser un tableau pour draw Block drawBlock(color){ this.paints.beginPath() this.paints.fillStyle = couleur this.arr_bX.forEach((item, index) => { this.paintfr(item, this.arr_bY[index], 0.9) }) this.paints.closePath() } // Dessine le bloc déjà positionné 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() }) } // Génère un nombre aléatoire et renvoie le type ou la couleur du bloc tapez createRandom( type){ let temp = this.width/2-1 if (type == 'rBlock'){ //S'il s'agit d'un bloc, tapez this.num_blcok = Math.round(Math.random()*4+1) switch(this.num_blcok){ cas 1 : this.arr_bX.push(temp,temp-1,temp, temp+ 1) this.arr_bY.push(0,1,1,1) cas de rupture 2 : this.arr_bX.push(temp,temp-1,temp-1,temp+1) this.arr_bY.push(1,0,1,1) casse le cas 3 : this.arr_bX.push(temp,temp-1,temp+1,temp+2) this.arr_bY.push(0,0,0, 0) cas d'arrêt 4 : this.arr_bX.push(temp,temp-1,temp,temp+1) this.arr_bY.push(0,0,1,1) cas d'arrêt 5 : this.arr_bX.push(temp,temp+1,temp,temp+1) this.arr_bY.push(0,0,1,1) break } } if (type == 'rColor'){ //if C'est le type de couleur let num_color = Math.round(Math.random()*8+1) switch(num_color){ case 1 : this.type_color='#3EF72A' break case 2 : this.type_color='jaune' cas de rupture 3 : this.type_color='#2FE0BF' cas de rupture 4 : this.type_color='red' cas de rupture 5 : this.type_color='gray' cas de rupture 6 : this.type_color ='#C932C6' cas de rupture 7 : this.type_color= '#FC751B' cas de rupture 8 : this.type_color= '#6E6EDD' break case 9 : this.type_color= '#F4E9E1' break } } } // Déterminer si les blocs entrent en collision (inférieur) et s'ils traversent la limite inférieure pendant la déformation JudgeCollision_down(){ for(let i = 0 ; je < this.arr_bX.length; i++){ if (this.arr_bY[i] + 1 == this.height){ //Si la limite inférieure est franchie pendant la déformation, return false } if (this.arr_store_X.length != 0) { //Déterminez si les blocs entrent en collision (inférieur) 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 } //Jugez si les blocs entrent en collision (gauche et droite) et s'ils traversent les limites gauche et droite pendant la déformation JudgeCollision_other(num){ for(let i = 0; i < this.arr_bX.length; i++){ if (num == 1) { //Si la limite droite est franchie lors de la déformation if (this.arr_bX[i] == this.width - 1) return false } if (num == -1) { //Si la limite gauche est franchie lors de la déformation if (this.arr_bX[i] == 0) return false } if (this.arr_store_X.length != 0) { / /Déterminer si les blocs entrent en collision (gauche et droite) 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 } //La fonction d'accélération 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. longueur) this.initBlock() } this.clearUnderBlock() this.drawBlock(this.type_color) this.drawStaticBlock() this.gameover() } //La touche de direction est la fonction de mouvement vers la gauche move(dir_temp){ this.initBackground() if (dir_temp == 1) { //Right 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 je=0; je < this.arr_bY.length; i++){ this.arr_bX[i] = this.arr_bX[i]-1 } } } this.drawBlock(this.type_color) this.drawStaticBlock() } //La clé de direction est une fonction de direction de transformation spatiale up_change_direction(num_blcok){ if (num_blcok == 5) { return } let arr_tempX = [] let arr_tempY = [] //Parce que je ne sais pas si la transformation peut réussir, je la stocke d'abord pour (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++ //Extraire les coordonnées du centre et les déformer par The le centre actuel prévaudra let ax_temp = this.arr_bX[0] let ay_temp = this.arr_bY[0] this.arr_bX.splice(0, this.arr_bX.length) //Effacer le tableau this.arr_bY.splice(0, this.arr_bY.length) if (num_blcok == 1) { switch(this.direction%4){ cas 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) cas de rupture 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) cas de rupture 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) cas de rupture 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){ cas 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) cas de rupture 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) cas de rupture 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) cas de rupture 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) break } } if (num_blcok == 3) { switch(this.direction%4){ cas 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) cas de rupture 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 ) cas de rupture 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) cas de rupture 0 : this.arr_bX.push(ax_temp,ax_temp,ax_temp, axe_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){ cas 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) cas de rupture 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) cas de rupture 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) cas de rupture 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) )) { // Si la transformation échoue, exécutez le code suivant 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() } //Lorsqu'une ligne est pleine, effacez les blocs et la coordonnée Y du bloc supérieur est +1 clearUnderBlock(){ //Supprimer les blocs de bas niveau 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 ) { //Calculer la note this.grade++ document.getElementById('text').innerHTML = 'Note actuelle :'+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) } //Laissez le bloc supérieur descendre d'un espace pour(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 } } } //Juger la fin du 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() //Fonction de touche de direction document.onkeydown = (e) => { if (tetrisObj.over) return switch(e.keyCode){ case 40 : // La direction est vers le bas tetrisObj.down_speed_up() break case 32 : // Espace pour changer de direction tetrisObj.initBackground() // Redessine la carte tetrisObj.up_change_direction(tetrisObj.num_blcok) tetrisObj.drawBlock(tetrisObj.type_color) break case 37 : // La direction est à gauche tetrisObj.initBackground() tetrisObj .déplacer (-1) tetrisObj.drawBlock(tetrisObj.type_color) break cas 39 : // La direction est droite tetrisObj.initBackground() tetrisObj.move(1) tetrisObj.drawBlock(tetrisObj.type_color) break } } </script></body></ HTML >
Ce qui précède représente l’intégralité du contenu de cet article. J’espère qu’il sera utile à l’étude de chacun. J’espère également que tout le monde soutiendra le réseau VeVb Wulin.