Недавно в свободное время смотрел видео-урок по реализации мини-игры тетрис, считаю алгоритм этой мини-игры очень интересным, планирую разобраться и изучить для ознакомления.
Хотя название помечено как canvas, на самом деле эта небольшая игра использует очень мало знаний о canvas, а ее ядром является проблема с алгоритмом js. Учебное пособие будет относительно скучным и, возможно, не очень полезным в реальной работе (но вы можете использовать тетрис для небольшой маркетинговой деятельности). проблемы. .
предисловие
Эта статья будет разделена на несколько модулей для пошагового объяснения:
- Введение в игровой процесс
- Алгоритмическое мышление
- Создать игровую карту
- создание блоков
- Создавайте блоки разной формы
- падение блока
- столкновение блоков
- Двигайте блок влево и вправо
- Деформация блока
- Блоки ускоряются вниз
- удалить всю строку
- игра окончена
Несмотря на то, что игра небольшая, функции достаточно сложные, и все, что в ней нужно, — это мышление и терпение. Идите прямо к точке ниже.
Введение в игровой процесс
Все, должно быть, играли в тетрис. Общий игровой процесс прост. Случайным образом бросайте блоки различной формы на карту сетки. Используйте клавиши со стрелками ←→ для управления движением блока и клавишу ↑ для изменения формы блока, и клавишу ↓, чтобы ускорить его падение. , когда блок падает и ряд заполнен, ряд исключается.
Давайте взглянем на рендеринг, которого мы наконец добились:
Алгоритмическое мышление
Мы можем разделить тетрис на две части, карта + квадрат. Мы рассматриваем карту как двумерный массив:
[
[0,0,0,0,0,0...],
[0,0,0,0,0,0...],
[0,0,0,0,0,0...],
[0,0,0,0,0,0...],
...
]
Квадрат также рассматривается как двумерный массив.
Квадрат в форме буквы Z можно рассматривать как:
[
[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],
]
Сверху — зигзагообразный квадрат в первом ряду.
На первый взгляд из приведенного выше массива видно, что когда мы визуализируем холст, мы визуализируем элемент со значением массива 0 как цвет карты, а элемент со значением 1 как цвет квадрата. Затем, как переместить блок, очень просто изменить элементы слева и справа от блока с 0 на 1, а затем сбросить исходное положение на 0, что заставляет блок двигаться визуально.
На следующем рисунке показано движение Z-образного квадрата:
Зная его основную идею, вы можете кодировать разработку кода.
Создать игровую карту
Сначала мы готовимcanvas
холст:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<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>
</head>
<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>
Сначала нам нужно понятьcanvas
Простая операция, нарисуйте на холсте небольшой квадрат:
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();
Как показано на рисунке:
нужно сделать тетрисcanvas
Вам просто нужно знатьfillStyle
,fillRect
Вот и все, не так уж и сложно.
Далее нам нужно нарисовать карту сетки N*N.
Сначала сгенерируйте двумерный массив N*N со всеми значениями 0:
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));
В соответствии с комбинацией вышеуказанного метода блочного рендеринга и генерации массива карт рендерится карта 20*20:
Полный код выглядит следующим образом:
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();
Анализ вышеизложенного немного труден для понимания, это расчет значения оси x, y каждого квадрата.
Подробный анализ выглядит следующим образом:
画布宽高500 * 500 要平均分配20*20个方块,
那么方块的最大宽度为 500/20 = 25
但是这样的话方块会挤在一块 因此我们再给每个方块之间增加4个单位的间距
于是 单个方块的 w/h = 500/20 - 4 = 21;
坐标的算法:
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
所以第j列的x坐标可以归纳为 (w+4)*j + 2
y坐标亦然
После выполнения кода эффект следующий:
The Long March успешно сделал первый шаг, и следующий шаг — создание куба.
создание блоков
Существует много типов блоков, таких как однословные, Г-образные, Z-образные, перевернутые Т-образные, полевые и т. д. Согласно приведенному выше алгоритму, каждый тип может быть описан двумерным массивом:
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);
}
Создавайте блоки разной формы
Чтобы случайным образом сгенерировать блок другой формы, вызовитеMath.random()
Вот и все.
Вставьте полный код:
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();
Обновите страницу, и появится случайный блок, как показано ниже:
падение блока
Падение ящика на самом деле означает изменение положения ящика по оси Y на холсте.Как продолжать падение?Очевидно, нужно запустить таймер.
[Продолжение следует. . . ]
столкновение блоков
[Продолжение следует. . . ]
Двигайте блок влево и вправо
[Продолжение следует. . . ]
Деформация блока
[Продолжение следует. . . ]
Блоки ускоряются вниз
[Продолжение следует. . . ]
удалить всю строку
[Продолжение следует. . . ]
игра окончена
[Продолжение следует. . . ]
послесловие
Времени в последнее время относительно мало, а остальные модули потихоньку обновляются. После того, как общая идея будет понята, другие проблемы могут быть легко решены, просто нужно терпение и постоянная отладка.
Если вам это нравится, ставьте палец вверх и дайте ему немного мотивации для обновления 😆
использованная литература
Блог-центр Renrendai Big Front-end Technology
Наконец-то прорекламировали. Добро пожаловать в гостиБлог-центр Renrendai Big Front-end Technology
внутри оnodejs
react
reactNative
Небольшие программы, фронтенд-инжиниринг и другие сопутствующие технические статьи обновляются одна за другой Добро пожаловать в гости и жалуйтесь~
Предыдущий:Мини-программа Wechat для регулярной отправки шаблонных сообщений (версия узла)