Изучите объектно-ориентированное программирование на JavaScript, создав тетрис (3)
В это время цель состоит в том, чтобы вы могли создать программу для перемещения мины, используя принципы ООП.
Вот ссылка на статьи из этой серии: Предыдущая статья / Следующая статья
В прошлом выпуске мы создали MinoGfx
, который может рисовать мино. Мы немного переработаем его следующим образом.
function MinoGfx(color, blkpos8) { const _ctx = gFieldGfx.context2d; const _color = color; const _pxpos8 = []; for (let idx = 0; idx < 8; idx += 2) { _pxpos8[idx] = blkpos8[idx] * g.Px_BLOCK; _pxpos8[idx + 1] = blkpos8[idx + 1] * g.Px_BLOCK; } this.draw = () => { _ctx.fillStyle = _color; for (let idx = 0; idx < 8; idx += 2) { _ctx.fillRect(_pxpos8[idx] + 1, _pxpos8[idx + 1] + 1 , g.Px_BLOCK_INNER, g.Px_BLOCK_INNER); } } } const t_mino = new MinoGfx('magenta', [1, 0, 0, 1, 1, 1, 2, 1]); t_mino.draw();
При создании мино с помощью MinoGfx
мы упростили указание формы мино. В случае создания T-mino мы можем прямо указать его форму как (1, 0), (0, 1), (1, 1), (2, 1). См. рис.1.
Как показано на рис. 2, Т-мино изображен зарытым в стене, так что переместите его, прежде чем рисовать.
В таком случае мы должны подумать о том, чтобы MinoGfx
добавить функцию, которая перемещает мину. Программа будет следующей. В этой статье нужно добавить много программных фрагментов, поэтому я прикрепляю весь код в конце этой статьи.
function MinoGfx(color, blkpos8) { const _ctx = gFieldGfx.context2d; const _color = color; const _pxpos8 = []; // omitted... this.move = (dx, dy) => { for (let idx = 0; idx < 8; idx += 2) { _pxpos8[idx] += dx * g.Px_BLOCK; _pxpos8[idx + 1] += dy * g.Px_BLOCK; } } }
Теперь давайте попробуем эту функцию. Как показано на рис.3, Т-мино будет нарисовано на четыре клетки вправо.
const t_mino = new MinoGfx('magenta', [1, 0, 0, 1, 1, 1, 2, 1]); t_mino.move(4, 0); t_mino.draw();
Если вы создаете o_mino
, используя MinoGfx
с добавленной функцией, вы, естественно, можете использовать новую функцию и в o_mino
. Добавьте программу ниже и запустите ее, как показано на рис.4.
const o_mino = new MinoGfx('yellow', [1, 0, 2, 0, 1, 1, 2, 1]); o_mino.move(7,0); o_mino.draw();
Как мы только что сделали, в ООП мы завершаем программу, добавляя к объекту функции, которые выполняют работу.
Теперь, когда MinoGfx
получил новую функцию для перемещения мины, давайте сделаем так, чтобы она могла перемещаться по экрану с помощью ввода с клавиатуры.
Вот что важно учитывать. Как мы обсуждали в прошлом выпуске, в ООП важно сначала рассмотреть, за что отвечает объект. В настоящее время у нас есть два functions
: gFieldGfx
, который рисует игровое поле, и MinoGfx
, который рисует мино. Очевидно, что эти двое не должны отвечать за прием ввода с клавиатуры. Итак, подготовьте новый объект gGame
для координации всей игры, включая прием ввода с клавиатуры.
При нажатии клавиши объект document
уведомляется об этом. Если функция зарегистрирована с помощью document.onkeydown
, она получает информацию о нажатой клавише. Учитывая эти вещи, gGame
будет следующим.
const gGame = new function() { let _curMinoGfx = new MinoGfx('magenta', [1, 0, 0, 1, 1, 1, 2, 1]); _curMinoGfx.move(4, 0); _curMinoGfx.draw(); document.onkeydown = (e) => { switch (e.key) { case 'ArrowLeft': _curMinoGfx.move(-1, 0); _curMinoGfx.draw(); break; case 'ArrowRight': _curMinoGfx.move(1, 0); _curMinoGfx.draw(); break; case 'ArrowDown': _curMinoGfx.move(0, 1); _curMinoGfx.draw(); break; } } }
Самостоятельный запуск программы — это кратчайший путь к пониманию ООП. Это требует некоторой работы, но, пожалуйста, запустите tetris.html
в свой браузер и попробуйте.
Если вы переместите T-mino вниз, вы увидите экран, подобный показанному ниже.
Вот что бывает, когда не стираешь Ти-мино. Теперь давайте добавим функцию удаления мино в MinoGfx
.
function MinoGfx(color, blkpos8) { const _ctx = gFieldGfx.context2d; const _color = color; const _pxpos8 = []; // omitted... this.erase = () => { _ctx.fillStyle = 'black'; for (let idx = 0; idx < 8; idx += 2) { _ctx.fillRect(_pxpos8[idx] + 1, _pxpos8[idx + 1] + 1 , g.Px_BLOCK_INNER, g.Px_BLOCK_INNER); } } }
Измените gGame
на стирание, а затем двигайтесь дальше.
const gGame = new function() { let _curMinoGfx= new MinoGfx('magenta', [1, 0, 0, 1, 1, 1, 2, 1]); _curMinoGfx.move(4, 0); _curMinoGfx.draw(); document.onkeydown = (e) => { switch (e.key) { case 'ArrowLeft': _curMinoGfx.erase(); _curMinoGfx.move(-1, 0); _curMinoGfx.draw(); break; case 'ArrowRight': _curMinoGfx.erase(); _curMinoGfx.move(1, 0); _curMinoGfx.draw(); break; case 'ArrowDown': _curMinoGfx.erase(); _curMinoGfx.move(0, 1); _curMinoGfx.draw(); break; } } }
Внесите вышеуказанные изменения и запустите его снова.
Легче понять концепцию ООП, когда вы создаете программу самостоятельно. В ООП есть и другие важные концепции, и я постараюсь разъяснить их вам. Спасибо, что прочитали эту статью.
// tetris.js 'use strict'; const divTitle = document.createElement('div'); divTitle.textContent = "TETRIS"; document.body.appendChild(divTitle); const g = { Px_BLOCK: 30, Px_BLOCK_INNER: 28, PCS_COL: 10, PCS_ROW: 20, PCS_FIELD_COL: 12, } const gFieldGfx = new function() { const pxWidthField = g.Px_BLOCK * g.PCS_FIELD_COL; const pxHeightField = g.Px_BLOCK * (g.PCS_ROW + 1); const canvas = document.createElement('canvas'); canvas.width = pxWidthField; canvas.height = pxHeightField; document.body.appendChild(canvas); const _ctx = canvas.getContext('2d'); _ctx.fillStyle = "black"; _ctx.fillRect(0, 0, pxWidthField, pxHeightField); const yBtmBlk = g.Px_BLOCK * g.PCS_ROW; const xRightBlk = pxWidthField - g.Px_BLOCK + 1; _ctx.fillStyle = 'gray'; for (let y = 1; y < yBtmBlk; y += g.Px_BLOCK) { _ctx.fillRect(1, y, g.Px_BLOCK_INNER, g.Px_BLOCK_INNER); _ctx.fillRect(xRightBlk, y, g.Px_BLOCK_INNER, g.Px_BLOCK_INNER); } for (let x = 1; x < pxWidthField; x += g.Px_BLOCK) { _ctx.fillRect(x, yBtmBlk + 1, g.Px_BLOCK_INNER, g.Px_BLOCK_INNER); } this.context2d = _ctx; } function MinoGfx(color, blkpos8) { const _ctx = gFieldGfx.context2d; const _color = color; const _pxpos8 = []; for (let idx = 0; idx < 8; idx += 2) { _pxpos8[idx] = blkpos8[idx] * g.Px_BLOCK; _pxpos8[idx + 1] = blkpos8[idx + 1] * g.Px_BLOCK; } this.draw = () => { _ctx.fillStyle = _color; for (let idx = 0; idx < 8; idx += 2) { _ctx.fillRect(_pxpos8[idx] + 1, _pxpos8[idx + 1] + 1 , g.Px_BLOCK_INNER, g.Px_BLOCK_INNER); } } this.move = (dx, dy) => { for (let idx = 0; idx < 8; idx += 2) { _pxpos8[idx] += dx * g.Px_BLOCK; _pxpos8[idx + 1] += dy * g.Px_BLOCK; } } this.erase = () => { _ctx.fillStyle = 'black'; for (let idx = 0; idx < 8; idx += 2) { _ctx.fillRect(_pxpos8[idx] + 1, _pxpos8[idx + 1] + 1 , g.Px_BLOCK_INNER, g.Px_BLOCK_INNER); } } } const gGame = new function() { let _curMinoGfx = new MinoGfx('magenta', [1, 0, 0, 1, 1, 1, 2, 1]); _curMinoGfx.move(4, 0); _curMinoGfx.draw(); document.onkeydown = (e) => { switch (e.key) { case 'ArrowLeft': _curMinoGfx.erase(); _curMinoGfx.move(-1, 0); _curMinoGfx.draw(); break; case 'ArrowRight': _curMinoGfx.erase(); _curMinoGfx.move(1, 0); _curMinoGfx.draw(); break; case 'ArrowDown': _curMinoGfx.erase(); _curMinoGfx.move(0, 1); _curMinoGfx.draw(); break; } } }