canvas+js запускает блок тетриса с 0

JavaScript
canvas+js запускает блок тетриса с 0

Недавно в свободное время смотрел видео-урок по реализации мини-игры тетрис, считаю алгоритм этой мини-игры очень интересным, планирую разобраться и изучить для ознакомления.

Хотя название помечено как canvas, на самом деле эта небольшая игра использует очень мало знаний о canvas, а ее ядром является проблема с алгоритмом js. Учебное пособие будет относительно скучным и, возможно, не очень полезным в реальной работе (но вы можете использовать тетрис для небольшой маркетинговой деятельности). проблемы. .

предисловие

Эта статья будет разделена на несколько модулей для пошагового объяснения:

  1. Введение в игровой процесс
  2. Алгоритмическое мышление
  3. Создать игровую карту
  4. создание блоков
  5. Создавайте блоки разной формы
  6. падение блока
  7. столкновение блоков
  8. Двигайте блок влево и вправо
  9. Деформация блока
  10. Блоки ускоряются вниз
  11. удалить всю строку
  12. игра окончена

Несмотря на то, что игра небольшая, функции достаточно сложные, и все, что в ней нужно, — это мышление и терпение. Идите прямо к точке ниже.

Введение в игровой процесс

Все, должно быть, играли в тетрис. Общий игровой процесс прост. Случайным образом бросайте блоки различной формы на карту сетки. Используйте клавиши со стрелками ←→ для управления движением блока и клавишу ↑ для изменения формы блока, и клавишу ↓, чтобы ускорить его падение. , когда блок падает и ряд заполнен, ряд исключается.

Давайте взглянем на рендеринг, которого мы наконец добились:

tetris

Алгоритмическое мышление

Мы можем разделить тетрис на две части, карта + квадрат. Мы рассматриваем карту как двумерный массив:

[
    [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-образного квадрата:

arr

Зная его основную идею, вы можете кодировать разработку кода.

Создать игровую карту

Сначала мы готовим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

нужно сделать тетрис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*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坐标亦然

После выполнения кода эффект следующий:

map

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 для регулярной отправки шаблонных сообщений (версия узла)