# canvas+js從0開始擼一個俄羅斯方塊

1. 遊戲玩法介紹
2. 算法思想
3. 創建遊戲地圖
4. 方塊的創建
5. 生成不同形狀方塊
6. 方塊下落
7. 方塊碰撞
8. 方塊左右移動
9. 方塊變形
10. 方塊向下加速
11. 消除整行
12. 遊戲結束

## 算法思想

``````[
[0,0,0,0,0,0...],
[0,0,0,0,0,0...],
[0,0,0,0,0,0...],
[0,0,0,0,0,0...],
...
]
``````

``````[
[1,1,0],
[0,1,1]
]
``````

``````[
[0,1,0],
[1,1,1]
]
``````

``````[
[0,0,1,1,0,0,0,0],
[0,0,0,1,1,0,0,0],
[0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0],
]
``````

## 創建遊戲地圖

index.html

``````<!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>canvas</title>
<style>
#myCanvas {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
margin: auto;
background: #000;
}
</style>
<body>
<canvas id="myCanvas" height="500" width="500"></canvas>
<button id="stop">停止</button>
<button id="score">得分: 0</button>
<script src="/js/tetris/index.js"></script>
</body>
</html>
``````

index.js

``````let Tetris = {
init(){
//初始化
const canvas = document.getElementById('myCanvas');
this.gc = canvas.getContext('2d');
this.render();
},
//渲染畫布
render(){
//在座標為100，100的位置畫一個40*40的紅色方塊
this.gc.fillStyle = 'red';
this.gc.fillRect(100,100,40,40);
}
};
Tetris.init();
``````

``````function map(r,c){
let data = [];
for(let i = 0; i < c; i++){
data.push([]);
//每行長度為 r
data[i].length = r;
//所有元素默認值 0
data[i].fill(0);
}
return data;
}
console.table(map(20,10));
``````

index.js

``````let Tetris = {
init(){
//初始化
const canvas = document.getElementById('myCanvas');
this.gc = canvas.getContext('2d');
//20*20的格子
let data = this.map(20,20);
this.render(data);
},
map(r,c){
let data = [];
for(let i = 0; i < c; i++){
data.push([]);
//每行長度為 r
data[i].length = r;
//所有元素默認值 0
data[i].fill(0);
}
return data;
},
render(data){
//計算每個方塊的寬高 方塊之間間隔為4
let w = 500/20 - 4;
let h = 500/20 - 4;
//計算方塊的行列
let r = data.length;
let c = data[0].length;
for(let i = 0; i < r; i ++){
for (let j = 0; j < c; j++){
//判斷數組裡的值 若為1 則渲染為紅色 0 則渲染為白色
this.gc.fillStyle = data[i][j] === 0 ? 'white' : 'red';
this.gc.fillRect(
(w+4)*j+2,
(h+4)*i+2,
w,
h
);
}
}
}
};
Tetris.init();
``````

``````畫布寬高500 * 500 要平均分配20*20個方塊，

20個方塊之間有19個間隙 剩下一個4單位的距離 分配到小方塊距離左右兩側的間隙

n列   x座標
0     w*0 + 2
1     w*1 + 2 + 4*1
2     w*2 + 2 + 4*2
3     w*3 + 2 + 4*3
...
n     w*n + 2 + 4*n

y座標亦然
``````

## 方塊的創建

``````blockType: [
[[1,1,1,1]],
[[1,1],[1,1]],
[[1,1,0],[0,1,1]],
[[0,1,1],[1,1,0]],
[[0,1,0],[1,1,1]],
[[1,0,0],[1,1,1]],
[[0,0,1],[1,1,1]]
]
``````

``````//更新data數組
draw(block){
/*
* 假如block為Z字型的方塊 [[1,1,0],[0,1,1]]
*/
for (let i = 0; i < block.length; i++){
for (let j = 0; j < block[0].length; j++){
this.data[i][j + this.y] = block[i][j];
}
}
console.table(this.data);
//再次調用render方法更新畫布
this.render(this.data);
}
``````

## 生成不同形狀方塊

index.js

``````let Tetris = {
//初始化
init(){
const canvas = document.getElementById('myCanvas');
this.gc = canvas.getContext('2d');
//20*20的格子
this.data = this.map(20,20);
//X軸的偏移量 之所以保存為變量 是以後我們做左右移動的是需要通過改變這個值來實現
this.x = 7;
//隨機生成一個方塊
this._block = this.block();
this.draw(this._block);
},
//地圖數據
map(r,c){
let data = [];
for(let i = 0; i < c; i++){
data.push([]);
//每行長度為 r
data[i].length = r;
//所有元素默認值 0
data[i].fill(0);
}
return data;
},
//隨機生成一個類型的方塊
block(){
let index = Math.floor(Math.random()*7);
return this.blockType[index];
},
//方塊的類型
blockType: [
[[1,1,1,1]],
[[1,1],[1,1]],
[[1,1,0],[0,1,1]],
[[0,1,1],[1,1,0]],
[[0,1,0],[1,1,1]],
[[1,0,0],[1,1,1]],
[[0,0,1],[1,1,1]]
],
//重繪畫布
draw(block){
for (let i = 0; i < block.length; i++){
for (let j = 0; j < block[0].length; j++){
//要向x軸偏移 需要為j加一個偏移量即可
this.data[i][j + this.x] = block[i][j];
}
}
console.table(this.data);
this.render(this.data);
},
//渲染
render(data){
//計算每個方塊的寬高 方塊之間間隔為4
let w = 500/20 - 4;
let h = 500/20 - 4;
//計算方塊的行列
let r = data.length;
let c = data[0].length;
for(let i = 0; i < r; i ++){
for (let j = 0; j < c; j++){
//判斷數組裡的值 若為1 則渲染為紅色 0 則渲染為白色
this.gc.fillStyle = data[i][j] === 0 ? 'white' : 'red';
/*
* 座標算法
* 畫布寬度500 小方格寬度21 個數20 則 留下的空隙寬度為 500 - 21*20 = 80 其中 20個小方塊可分4單位的間隙
* 20個方塊之間有19個間隙 剩下一個4單位的距離 分配到小方塊距離左右兩側的間隙
* 總結一下規律
* n行     x座標
* 0       w*0 + 2
* 1       w*1 + 2 + 4
* 2       w*2 + 2 + 4*2
* 3       w*3 + 2 + 4*3
* ...
* n       w*n + 2 + 4*n
* 所以第j列的x座標可以歸納為 (w+4)*j + 2
* y座標亦然
*/
this.gc.fillRect(
(w+4)*j+2,
(h+4)*i+2,
w,
h
);
}
}
}
};
Tetris.init();
``````

[未完待續。。。]

[未完待續。。。]

[未完待續。。。]

[未完待續。。。]

[未完待續。。。]

[未完待續。。。]

[未完待續。。。]

## 人人貸大前端技術博客中心

### 相關文章

Gin(九):生成restful接口

JSI小試牛刀——Native同步調用JS代碼