canvas實現俄羅斯方塊

原文地址:canvas實現俄羅斯方塊 很久沒使用canvas了,因而經過寫小遊戲「俄羅斯方塊」再次熟悉下canvas,若是有必定的canvas基礎,要實現仍是不難的。實際完成的Demo請看:canvas俄羅斯方塊javascript

原理詳解

看遊戲最終界面,可知須要實現如下關鍵功能:html

  1. 遊戲面板,也就是12 * 20的方格,以及是否填充了方塊信息;
  2. 運動方塊,方塊須要實現移動,變形的功能。 遊戲界面

    <!-- more -->java

界面的實現

整個面板就是以左上角(0,0)爲原點的座標系,右上角(12,0)左下角(0,20)右下角(12,20),每一個點的座標位置均可以肯定。是否已經填充方塊,咱們能夠將每一個方格當作一個數組元素,0表示沒有,1表示已經填充。12 * 20 的面板使用兩層數組,即用20個長度爲12的數組實現。git

var maps = [[0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,1,0,1,0], ...];

畫出面板的代碼,用最基礎的canvas的api就能實現github

//格子
	for(var i=0;i<12;i++){
		for(var j=0;j<20;j++){
			ctx.fillRect(i*40,j*40,40,40);
			ctx.strokeRect(i*40,j*40,40,40);
			if(this.maps[j][i]==1){//方格已經有填充內容
				ctx.save();
				ctx.lineWidth=4;
				ctx.fillStyle='hsla(200,100%,50%,.5)';
				ctx.strokeStyle='hsla(200,100%,50%,.9)';
				ctx.fillRect(i*40,j*40,40,40);
				ctx.strokeRect(i*40+2,j*40+2,38,38);
				ctx.restore();
			}
		}
	}

	//邊框
	ctx.lineWidth=4;
	ctx.strokeStyle='hsla(0,100%,0%,.3)';
	ctx.moveTo(0,0);
	ctx.lineTo(0,20*40);
	ctx.lineTo(12*40,20*40);
	ctx.lineTo(12*40,0);
	ctx.stroke();
	ctx.restore();

方塊的實現

遊戲中用到如下 7 種圖形 方塊canvas

結合上面介紹的座標系,數組 [x1, y1, x2, y2, x3, y3, x4, y4] 就是上面圖形中4個點座標的數據表現形式,7 種圖形的座標分別以下:api

var Arr = [[4,0,4,1,5,1,6,1],[4,1,5,1,6,1,6,0],[4,0,5,0,5,1,6,1],[4,1,5,0,5,1,6,0],
[5,0,4,1,5,1,6,1],[4,0,5,0,6,0,7,0],[5,0,6,0,5,1,6,1]];

方塊的移動,遍歷整個數組,加上位移向量就行,很是簡單數組

class Shape {
	constructor(m){
		this.m = Object.assign([],m);
	}
	move(x,y){ // 位移
		var m = this.m,
			l = m.length;
		y = y||0;

		for (var i=0;i<l;i=i+2){
			m[i]+=x;
			m[i+1]+=y;
		}
		return this;
	}

方塊的旋轉,俄羅斯方塊裏面方塊除了左右和上下運動,還會旋轉,不是嗎?稍微思考下就知道,這不過就是矩陣變換而已,也就是每次圖形繞中心點旋轉90度。我這裏用數組第三個點做爲圖形變換的中心點,固然這樣處理不夠完善。app

class Shape {
	transform(){//二維矩陣變換
		var m =this.m,
			l = m.length,
			c = Math.ceil(l/2),
			x = m[c],
			y = m[c+1],
			cos = Math.cos(Math.PI/2),
			sin = Math.sin(Math.PI/2);

		for (var i=0;i<l;i=i+2){
			if(i == c) continue;
			var mx = m[i]- x,
				my = m[i+1] - y,
				nx = mx*cos - my*sin,
				ny = my*cos + mx*sin;
			m[i]=x+nx;
			m[i+1]=y+ny;
		}
		return this;	
	}

邊界條件

主要包括以下三個方面this

  1. 方塊位置不能超出界面的判斷;
  2. 方塊到達底部或放置完成的判斷;
  3. 遊戲結束的判斷。

遍歷數組 (1)任意一個點y座標爲19時表示到達了底部;(2)獲取該座標的y+1位置在maps的信息,若是爲1表示已經填充。這兩種狀況下,運動方塊的週期結束,將該方塊的座標填充到maps對應的數組裏面便可。

若是座標的y+1已經有填充,同時當前座標小於1,即已經在界面的頂部了,那麼表示遊戲結束。

var isEnd = false,isOver=false,x,y;
for(var i=0,sl=that.shape.m.length;i<sl;i=i+2){
	x=that.shape.m[i];
	y=that.shape.m[i+1];
	if(y >= 19){ // 到了底部
		isEnd = true;break;
	}
	if(that.maps[y+1][x]==1){ // y+1位置已經填充
		isEnd = true;
		if(y <= 1){isOver=true;} // 遊戲結束
		break;
	}
}

方塊運動週期結束時檢測每一層是否滿格,以及滿格後的處理。某項數組所有元素都爲1則表示已經滿格,那麼刪除該項數組,同時列表頭再壓入一項每一個元素都爲0的數組便可。

checkPoint(){
	var that = this,
		maps = that.maps;

	for(var i=0,l=maps.length;i<l;i++){
		if(Math.min.apply(null,maps[i]) == 1){// 表示該層已經滿格
			that.maps.splice(i,1);
			that.score+=10; // 增長分數
			that.maps.unshift([0,0,0,0,0,0,0,0,0,0,0,0]);
		}
	}
	return this;
}

綁定事件

主要就是綁定keydown事件,要注意的是左移和右移事件包括了邊界判斷

bindEvent(){
	var that = this;
	document.addEventListener('keydown',function(e){
		switch(e.keyCode){
        	case 13:        //enter
        		cancelAnimationFrame(that.timer);
	        	that.init().update();
        	break;
        	case 80:        //p
        		that.pause = !that.pause;
				break;	
            case 40:        //down
            	that.d = 0.5;
				break;
            case 37:        //left
            	var over = false,
            		maps = that.maps,
            		shape = that.shape,
            		m = shape.m;
            	for(var i=0,l=m.length;i<l;i=i+2){
            		if(m[i]<=0 || maps[m[i+1]][m[i]-1] == 1){
            			over = true;break;
            		}
            	}
            	if(!over) shape.move(-1,0);
              	break;
            case 39:        //right
            	var over = false,
            		shape = that.shape,
            		maps = that.maps,
            		m = shape.m;
              	for(var i=0,l=m.length;i<l;i=i+2){
            		if(m[i]>=11 || maps[m[i+1]][m[i]+1] == 1){
            			over = true;break;
            		}
            	}
            	if(!over) shape.move(1,0);
              	break;
            case 32:        //space
              	that.shape.transform();
              	break;
        }
    },false);
}

總結

這裏面實現了俄羅斯方塊的最基本功能,還有關卡等功能點並無實現,同時該demo仍然有不完善的地方須要修正。

原文出處:https://www.cnblogs.com/edwardloveyou/p/10111213.html

相關文章
相關標籤/搜索