第一次寫俄羅斯方塊的時(shí)候已經(jīng)是1年多前了,也是我剛剛學(xué)js不久。
為了加強(qiáng)對(duì)js的理解又加上對(duì)游戲的愛(ài)好,于是在沒(méi)有參考他人的思路和代碼下,自己用最基本的js代碼寫出了基于canvas的俄羅斯方塊。
在大三的暑假,我又用了es6的語(yǔ)法進(jìn)行了改進(jìn),包含了class的語(yǔ)法糖、箭頭函數(shù)等,進(jìn)一步增加自己對(duì)es6的理解,代碼有400+行
想要做這個(gè)小游戲,必須先熟悉H5的canvas,js對(duì)數(shù)組的處理,鍵盤事件監(jiān)聽(tīng)和處理,定時(shí)器的使用等,其他的就是基本的邏輯處理了。
游戲的規(guī)則就是核心,也是我們代碼的重中之重
這里的邏輯核心是需要判斷方塊是否碰撞(當(dāng)前運(yùn)動(dòng)的方塊和已經(jīng)定位好的方塊有碰撞以致于當(dāng)前的運(yùn)動(dòng)的方塊不能在向下走,因?yàn)槲覀兊姆綁K默認(rèn)是向下走的,如果不能向下走,是視為已經(jīng)定位好的方塊,然后在生成一個(gè)新的方塊從初始位置繼續(xù)往下走)。
而且這個(gè)碰撞還需要應(yīng)用在方塊變形的時(shí)候,同樣地,如果方塊在變形的過(guò)程中和其他定位好的方塊進(jìn)行碰撞,則我們應(yīng)該阻止這個(gè)方塊進(jìn)行變形成功,
附上代碼,歡迎討論和指正
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>es6-重構(gòu)俄羅斯方塊(基于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;'>當(dāng)前分?jǐn)?shù):0</div> <script type="text/javascript"> /** * [一個(gè)完整的俄羅斯方塊類 design by magic_xiang] * @param {number} side [每個(gè)方塊邊長(zhǎng)(px),默認(rèn)35] * @param {number} width [一行包含的方塊數(shù)(個(gè)),默認(rèn)20] * @param {number} height [一列包含的方塊數(shù)(個(gè)),默認(rèn)15] * @param {number} speed [方塊下落移動(dòng)速度(ms),默認(rèn)400] */ class tetris{ constructor(side=35, width=20, height=15, speed=400){ this.side = side // 每個(gè)方塊邊長(zhǎng) this.width = width // 一行包含的方塊數(shù) this.height = height // 一列包含的方塊數(shù) this.speed = speed // 方塊下落移動(dòng)速度 this.num_blcok // 當(dāng)前方塊類型的數(shù)字變量 this.type_color // 當(dāng)前顏色類型的字符串變量 this.ident // setInterval的標(biāo)識(shí) this.direction = 1 // 方塊方向,初始化為1,默認(rèn)狀態(tài) this.grade = 0 // 用來(lái)計(jì)算分?jǐn)?shù) this.over = false // 游戲是否結(jié)束 this.arr_bX = [] // 存放當(dāng)前方塊的X坐標(biāo) this.arr_bY = [] // 存放當(dāng)前方塊的Y坐標(biāo) this.arr_store_X = [] // 存放到達(dá)底部所有方塊的X坐標(biāo) this.arr_store_Y = [] // 存放到達(dá)底部所有方塊的Y坐標(biāo) this.arr_store_color = [] // 存放到達(dá)底部所有方塊的顏色 this.paints = document.getElementById('tetris').getContext('2d') //獲取畫筆 self = this } // 封裝paints方法,讓代碼更簡(jiǎn)潔 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() } // 方塊自動(dòng)下落 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') //生成方塊類型數(shù)字 this.arr_bX.forEach((item, index) => { this.paintfr(item, this.arr_bY[index], 0.9) }) this.paints.closePath() } // 利用數(shù)組畫方塊 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() } // 畫已經(jīng)在定位好的方塊 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() }) } // 生成隨機(jī)數(shù)返回方塊類型或顏色類型 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) break case 2: this.arr_bX.push(temp,temp-1,temp-1,temp+1) this.arr_bY.push(1,0,1,1) break case 3: this.arr_bX.push(temp,temp-1,temp+1,temp+2) this.arr_bY.push(0,0,0,0) break case 4: this.arr_bX.push(temp,temp-1,temp,temp+1) this.arr_bY.push(0,0,1,1) break case 5: this.arr_bX.push(temp,temp+1,temp,temp+1) this.arr_bY.push(0,0,1,1) break } } if (type == 'rColor'){ //如果是顏色類型 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 case 3: this.type_color='#2FE0BF' break case 4: this.type_color='red' break case 5: this.type_color='gray' break case 6: this.type_color='#C932C6' break case 7: this.type_color= '#FC751B' break case 8: this.type_color= '#6E6EDD' break case 9: this.type_color= '#F4E9E1' break } } } // 判斷方塊之間是否碰撞(下),以及變形時(shí)是否越過(guò)下邊界 judgeCollision_down(){ for(let i = 0; i < this.arr_bX.length; i++){ if (this.arr_bY[i] + 1 == this.height){ //變形時(shí)是否越過(guò)下邊界 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 } //判斷方塊之間是否碰撞(左右),以及變形時(shí)是否越過(guò)左右邊界 judgeCollision_other(num){ for(let i = 0; i < this.arr_bX.length; i++){ if (num == 1) { //變形時(shí)是否越過(guò)右邊界 if (this.arr_bX[i] == this.width - 1) return false } if (num == -1) { //變形時(shí)是否越過(guò)左邊界 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; } //方向鍵為下的加速函數(shù) 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.length) this.initBlock() } this.clearUnderBlock() this.drawBlock(this.type_color) this.drawStaticBlock() this.gameover() } //方向鍵為左右的左移動(dòng)函數(shù) 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() } //方向鍵為空格的變換方向函數(shù) up_change_direction(num_blcok){ if (num_blcok == 5) { return } let arr_tempX = [] let arr_tempY = [] //因?yàn)椴恢朗欠衲軌蜃冃纬晒Γ韵却鎯?chǔ)起來(lái) 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++ //將中心坐標(biāo)提取出來(lái),變形都以當(dāng)前中心為準(zhǔn) let ax_temp = this.arr_bX[0] let ay_temp = this.arr_bY[0] this.arr_bX.splice(0, this.arr_bX.length) //將數(shù)組清空 this.arr_bY.splice(0, this.arr_bY.length) if (num_blcok == 1) { 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+1,ay_temp+1,ay_temp+1) break case 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 case 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) break case 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) break case 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 case 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) break case 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){ 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) break case 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) break case 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) break case 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) 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) break case 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 case 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) break case 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) )) { //如果變形不成功則執(zhí)行下面代碼 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坐標(biāo)+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) { //計(jì)算成績(jī)grade this.grade++ document.getElementById('text').innerHTML = '當(dāng)前成績(jī):'+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) } //讓上面的方塊往下掉一格 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 } } } } //判斷游戲結(jié)束 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() //方向鍵功能函數(shù) document.onkeydown = (e) => { if (tetrisObj.over) return switch(e.keyCode){ case 40: // 方向?yàn)橄? tetrisObj.down_speed_up() break case 32: // 空格換方向 tetrisObj.initBackground() //重畫地圖 tetrisObj.up_change_direction(tetrisObj.num_blcok) tetrisObj.drawBlock(tetrisObj.type_color) break case 37: // 方向?yàn)樽? tetrisObj.initBackground() tetrisObj.move(-1) tetrisObj.drawBlock(tetrisObj.type_color) break case 39: // 方向?yàn)橛? tetrisObj.initBackground() tetrisObj.move(1) tetrisObj.drawBlock(tetrisObj.type_color) break } } </script> </body> </html>
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
標(biāo)簽:盤錦 聊城 赤峰 綏化 萍鄉(xiāng) 金昌 中山 阿壩
巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《Html5原創(chuàng)俄羅斯方塊(基于canvas)》,本文關(guān)鍵詞 Html5,原創(chuàng),俄羅斯,方塊,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問(wèn)題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。