在互聯(lián)網(wǎng)的興起下,隨著而來(lái)的各種游戲和其他的各種軟件更是窮出不起,那么今天我們就來(lái)說(shuō)說(shuō)有關(guān)于:“html5怎么實(shí)現(xiàn)俄羅斯方塊游戲?游戲?qū)崿F(xiàn)方法分享! ”這個(gè)問(wèn)題的相關(guān)內(nèi)容。
第一次寫(xiě)俄羅斯方塊的時(shí)候已經(jīng)是1年多前了,也是我剛剛學(xué)js不久。
為了加強(qiáng)對(duì)js的理解又加上對(duì)游戲的愛(ài)好,于是在沒(méi)有參考他人的思路和代碼下,自己用最基本的js代碼寫(xiě)出了基于canvas的俄羅斯方塊。
在大三的暑假,我又用了es6的語(yǔ)法進(jìn)行了改進(jìn),包含了class的語(yǔ)法糖、箭頭函數(shù)等,進(jìn)一步增加自己對(duì)es6的理解,代碼有400+行
想要做這個(gè)小游戲,必須先熟悉H5的canvas,js對(duì)數(shù)組的處理,鍵盤(pán)事件監(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è)完整的俄羅斯方塊類(lèi) 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)前方塊類(lèi)型的數(shù)字變量
this.type_color // 當(dāng)前顏色類(lèi)型的字符串變量
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')
//獲取畫(huà)筆
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)
}
// 游戲開(kāi)始
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') //生成方塊類(lèi)型數(shù)字
this.arr_bX.forEach((item, index) => {
this.paintfr(item, this.arr_bY[index], 0.9)
})
this.paints.closePath()
}
// 利用數(shù)組畫(huà)方塊
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()
}
// 畫(huà)已經(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ù)返回方塊類(lèi)型或顏色類(lèi)型
createRandom(type){
let temp = this.width/2-1
if (type == 'rBlock'){ //如果是方塊類(lèi)型
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'){ //如果是顏色類(lèi)型
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() //重畫(huà)地圖
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>
以上就是有關(guān)于:“html5怎么實(shí)現(xiàn)俄羅斯方塊游戲?游戲?qū)崿F(xiàn)方法分享! ”這個(gè)問(wèn)題的相關(guān)內(nèi)容,當(dāng)然了在html5中不止這個(gè)功能,更多有關(guān)于html5這個(gè)方面的相關(guān)內(nèi)容我們都可以在W3Cschool中進(jìn)行學(xué)習(xí)和了解!