Один из лучших способов научиться веб-разработке - создать игру. Используя основные инструменты javascript, вы можете приступить к работе. Для новичка это может быть непросто. Легко попасть в ловушку «адского учебника», потому что вы думаете, что вам нужно продолжать учиться, прежде чем вы сможете начать творить. Я докажу вам, что это не так, пройдя через процесс создания шашек для настольных игр.

Я рассматриваю только HTML, JavaScript (включая DOM) и очень небольшое количество CSS, поэтому я предполагаю, что вы знаете, как использовать их основы. Вы также должны знать, как играть в шашки, конечно ...

Если вы хотите увидеть готовый код, вы можете посетить репозиторий GitHub здесь: https://github.com/RyanBranco/Checkers

Во-первых, это HTML.

Чтобы нарисовать доску для пользователя, я использую <table>. Шашечную доску можно легко создать, используя таблицу, потому что это именно то, чем является шашечная доска; стол 8х8. Между открывающим и закрывающим <table> тегами нам нужно 8 <tr> (строка таблицы). The<tr> на самом деле не создает ячейки в строках, он только определяет новую строку, в которую вы можете ввести ячейки; поэтому внутри каждого из 8 <tr>, которые вы только что создали, должно быть 8 <td> (данные таблицы), которые будут создавать фактические ячейки для платы.

Вот как должен выглядеть скелет:

Вы еще не увидите ничего, отображаемого на веб-странице, потому что внутри таблицы нет данных. Это будет следующим шагом.

Теперь это HTML, который должен иметь каждый <td>.

На доске есть клетки, в которых никогда не будет фигур, я даю им class=”noPieceHere”. Это важно для логики javascript (рассматривается позже) и CSS (поэтому они имеют другой цвет). Самый первый <td> должен иметь класс noPieceHere и должен чередоваться по всей доске (создавая эффект шахматной доски). Вот как будет выглядеть одна строка:

Теперь это нужно продублировать для всех остальных 7 строк, и помните, что это нужно чередовать. Это означает, что следующий <tr> начнется с <td>, не имеющего класса noPieceHere.

Теперь CSS для ячеек.

Каждый <td> имеет background-color: #BA7A3A, Width & Height 65px (поэтому каждая ячейка выглядит как квадрат) и text-align: center (для центрирования каждой части). Затем .noPieceHere заменяет цвет <td> на #F0D2B4 (чтобы он выглядел как шахматная доска).

* Ячейки могут быть настроены по вашему желанию *

После завершения у вас должна быть пустая доска для шашек! Теперь давайте заполним доску фигурами.

В каждом <td>, не имеющем class=”noPieceHere”, будут лежать куски. Я использую пустые теги <p> для красных фигур и пустые <span> теги для черных фигур; это не является абсолютно необходимым, но я думаю, что это упрощает итерацию по частям после перехода на JavaScript.

Красные фигуры имеют class=”red-piece”, а черные фигуры имеют class=”black-piece”, и каждая фигура должна иметь свой уникальный id.

У каждого цвета по 12 штук на противоположных концах, а в середине должно остаться 2 ряда.

Вот пример одной строки на каждом конце:

В JavaScript нам нужно будет присвоить элементам class=”king”, когда элемент достигнет противоположного конца доски, поэтому нам нужно определить .king в CSS, когда он будет добавлен JS.

Вот CSS для фигур и класса короля:

* Детали могут быть настроены по вашему желанию *

Если вы хотите, чтобы игрок знал, кто сейчас ход, вы можете добавить HTML-код под шахматной доской, который будет менять цвет в зависимости от того, у кого сейчас ход.

Цвет текста будет изменен с помощью JavaScript, но вот статический HTML:

Не забудьте добавить классы к каждому <div>, потому что нам нужно будет ссылаться на них в JavaScript, чтобы мы могли изменять стиль в зависимости от хода игрока.

Вы можете стилизовать их по своему усмотрению, но я начинаю с того, что задаю .black-turn-text серый цвет, чтобы он выглядел блеклым (чтобы пользователь знал, что игра начинается на красном ходу).

Окончательный результат HTML и CSS должен выглядеть примерно так:

Отлично! HTML и CSS завершены! Теперь начинается самое интересное ... JavaScript!

В логике JavaScript произойдут три основных события:

  1. Когда по кусочку щелкают
  2. при нажатии на ячейку
  3. Данные, которые изменяются после щелчка по ячейке

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

Во-первых, нам нужно создать представление доски на задней панели. Это можно сделать с помощью массива из 64 элементов (каждый элемент представляет собой ячейку).

Цифры, которые вы видите жестко запрограммированными, - это идентификаторы фигур на доске, расположенные там, где они должны быть на доске. Это легко позволяет нашему коду «видеть», где находятся части. Позже мы будем манипулировать этим массивом, чтобы он изменялся в зависимости от того, куда перемещаются части.

Затем нам нужно установить переменные, которые мы будем использовать для ссылки на вещи (в свойствах DOM и player).

Сначала, используя DOM, мы берем весь cells (строка 23), все части с каждой стороны (строки 24 и 25), текст поворота для каждой стороны, чтобы пользователь мог видеть, кто сейчас ход (строка 26 и 27), затем divider (строка 28). Не беспокойтесь о разделителе, если у вас его нет, все, что я собираюсь сделать, это изменить display: none, чтобы он исчезал, когда отображается победитель.

Теперь нам нужно сохранить пользовательские переменные, чтобы отслеживать состояние игры.

Я использую переменную turn для представления хода текущего игрока (true === красный ход & false === черный ход [строка 31]). Как только у игрока остается 0 фишек, побеждает противоположный игрок, поэтому нам нужно отслеживать «счет» каждого игрока (строки 32 и 33). Наконец, переменная playerPieces. Эта переменная позволяет нам динамически удерживать все фишки текущих игроков. Преимущество этого состоит в том, что нам не нужно писать код дважды (для красных и черных цветов). Мы будем динамически изменять, на какие части игрока ссылаются, поэтому нам нужно написать код только один раз.

Если переменная playerPieces немного сбивает с толку, не волнуйтесь, я скоро рассмотрю ее.

Теперь идет объект, который будет содержать свойства частей.

При щелчке по объекту selectedPiece object будет динамически изменяться в зависимости от свойств объекта, по которому щелкнул пользователь. Мы запрограммируем Javascript для анализа возможных ходов вокруг него и изменим истинные / ложные значения в зависимости от возможных ходов. Я покажу, как это делается позже. Он включает массив board, который был создан ранее.

Когда щелкают по фигуре, нам нужно взять несколько вещей: конкретный id для фигуры (строка 38), индекс доски, на которой находится фигура (строка 39), если это король (строка 40), и все возможные ходы, которые доступны (строки 41–48).

Прежде чем мы перейдем к реальной функциональности игры, я объясню свой выбор дизайна в том, как выполняется движение.

Изначально фигуры на доске могут двигаться только в противоположных направлениях, но как только они достигают противоположного конца доски, они становятся «королем» и затем совершают полное направленное движение. Я обнаружил, что «обратный» способ разработки механизма для фигур требует меньше кода. Итак, сначала мы проанализируем все доступные клетки, на которые фигура может прыгнуть (независимо от цвета фигуры, король это или нет), а затем ограничим движение в зависимости от хода игрока.

Если этого не сделать, придется написать три функции, которые выглядят почти одинаково; один для красных фигур, один для черных фигур и один для королей. Дублирование кода - не лучшая практика, потому что он более подвержен ошибкам, сложнее отлаживать, утомительно писать и много ненужного кода. Способ, который я описал, включает только одну функцию для анализа доступных окружающих ячеек и другую, которая легко редактирует selectedPiece свойства в зависимости от хода игроков. Если это немного сбивает с толку, я покажу вам, как это будет сделано в ближайшее время.

Теперь нам нужно дать частям click прослушиватель событий. Функция givePiecesEventListeners() предоставит деталям прослушиватель событий, который вызовет функцию getPlayerPieces. getPlayerPieces() - это начало цепочки функций, которые будут управлять объектом selectedPiece, который я показал ранее.

Итак, в самом низу файла JavaScript вызовите функцию, которая инициализирует прослушиватели событий, например: givePiecesEventListeners().

Следующие несколько функций поначалу могут показаться запутанными, потому что в них будут задействованы вещи, которые я еще не рассмотрел в коде. Они сбрасывают черты, которые еще не были изменены в коде. Но это должно быть сделано первым в цепочке, потому что, если пользователь щелкает фигуру, чтобы переместить, все будет проанализировано, и ячейки вокруг него получат «onclick» в зависимости от положения, в котором фигуры могут перемещаться. Но что тогда, если пользователь решит, что не хочет двигать этот кусок? Вместо этого они должны иметь возможность выбрать другой кусок. Поэтому для того, чтобы это произошло, в коде необходимо сбросить определенные свойства, чтобы новая выбранная часть могла двигаться правильно.

Здесь будет изменена переменная playerPieces. Просто if (turn) (что означает его красный поворот), установите переменную playerPieces на redsPieces, else… (что означает его черный ход) установите playerPieces на blacksPieces. Затем вызовите функцию removeCellonclick(). Затем, когда removeCellonclick() закончится, запустите resetBorders().

Во-первых, давайте рассмотрим removeCellonclick().

Эта функция перебирает все ячейки на плате (строка 81). Затем удаляет атрибут onclick (строка 82).

Позже я покажу вам, почему onclick для ячеек должны быть атрибутами, но пока все, что вам нужно знать, это то, что нам нужно будет динамически добавлять и удалять их.

Теперь рассмотрим resetBorders(), который назывался ранее.

Позже мы добавим к выбранной фигуре зеленую границу (чтобы показать, что фигура выбрана для перемещения), но если пользователь выберет другую фигуру, более старая фигура должна вернуться к значению по умолчанию. Таким образом, эта функция проходит через все playerPieces (это было установлено ранее) и заменяет части так, чтобы они имели строку border = “ 1px solid white” (89). Как только это закончится, вызывается resetSelectedPieceProperties(), затем getSelectedPiece().

Сначала давайте рассмотрим, что делает resetSelectedPieceProperties().

Это сбрасывает все свойства объекта selectedPiece в нормальное состояние. Это должно происходить при каждом щелчке по ячейке, потому что мы не знаем, какие свойства были у предыдущего выбранного элемента. Поскольку ячейки становятся onclick в зависимости от условий selectedPiece, их необходимо сбрасывать каждый раз, когда щелкают по элементу, иначе ячейки будут иметь неверные onclick атрибуты.

Теперь переходим на getSelectedPiece().

Нам нужно найти место на доске. Вот когда в игру вступает getSelectedPiece().

Если щелкнуть элемент, он выдаст вам event. Мы можем получить id фрагмента, набрав event.target.id. event.target.id возвращает строку id, и мы хотим, чтобы это было число, чтобы JavaScript мог правильно прочитать его, когда мы помещаем его в массив board. Итак, окружив parseInt() (строка 112), мы можем превратить строку в число. Также нам нужно найти, где находится фигура на доске. Здесь в игру вступает функция findPiece.

findPiece() принимает один параметр - идентификатор детали. Как только мы сохраняем parseInt() версию фрагмента (строка 18), мы передаем ее в метод javascript indexOf (строка 19) и return его. Это даст вам точное положение индекса, где находится выбранная фигура.

Следующим в цепочке функций идет isPieceKing().

Функция isPieceKing() не требует пояснений, она определяет, является ли выбранная фигура королем или нет.

Используя .classList.contains(“king”) в выбранном элементе HTML (строка 119), мы можем определить, король это или нет. Легкий! Позже вы увидите, как фигуре присваивается класс короля.

Затем вызовите getAvailableSpaces()

getAvailableSpaces() анализирует окружающие ячейки, которые кусок может образовать, не перепрыгивая через другой кусок (следующая функция покроет области перехода).

Если вы считаете клетки, фигуры могут перемещаться на +/- 7 ячеек или +/- 9 ячеек (плюс или минус, в зависимости от того, красная или черная фигура). Давайте посмотрим на первый if оператор, потому что, как только вы поймете одно из них, вы поймете остальное (строки 129–131).

Если selectedPiece.indexOfBoardPiece + 7 === null (что означает, что место доступно) и та же самая ячейка не имеет класса noPieceHere (строка 130), фигура может двигаться! Поэтому мы устанавливаем это свойство в строке selectedPiece = true (131).

Строка 130 важна, потому что 7 или 9 интервалов для фигур на краю - это ячейка на противоположном конце доски, и, очевидно, мы не можем перейти туда, но, к счастью, эти ячейки имеют класс noPieceHere. Поэтому нам нужно убедиться, что у нас нет возможности прыгнуть туда. Строка 130 предотвращает это.

Теперь давайте посмотрим на checkAvailableJumpSpaces().

checkAvailableJumpSpace() делает то же самое, что и checkAvailableSpaces(), за исключением ... как вы уже догадались ... прыгать по кусочкам.

Функция почти такая же, разница в том, что это +/- 14 или +/- 18 (плюс или минус в зависимости от того, красный или черный кусок). Дополнительным условием внутри оператора if является то, если позиция, на которую выполняется переход, равна >= 12, что означает, что фигура черная (поскольку у черных фигур id больше или равно 12), а у красных фигур id меньше 12.

К сожалению, этой функции необходимо дублировать код для каждой стороны (красной и черной). Это из-за того, как мы должны указывать детали id’s. Вероятно, есть способ избавиться от дублированного кода, но я не уверен, как это сделать. Так что, если кто-то сможет найти способ избавиться от необходимости дублировать код, не стесняйтесь оставлять сообщения в комментариях ниже.

Убедитесь, что после этой функции мы вызываем следующую, checkPieceConditions(). Я не показываю это на картинке, но это абсолютно необходимо, потому что нам нужно продолжать цепочку.

Теперь, когда мы проанализировали все возможные ходы для фигуры, нам нужно ограничить движение, основанное на повороте.

Поскольку король имеет полное движение и не имеет значения, на какой он стороне, нам не нужно манипулировать никакими свойствами. Итак, мы можем просто перейти к следующей функции - givePieceBorder() (строка 199).

Если это не король, нам нужно удалить все «минусовые» свойства движения для красных фигур и удалить нормальные свойства движения для черных фигур. Так мы ограничиваем движение.

Затем… мы переходим к следующей функции, givePieceBorder().

Чтобы кусок выглядел выделенным, нам нужно выделить его зеленым цветом. Итак ... если какое-либо из свойств selectedPiece движения истинно, мы меняем document.getElementById(selectedPiece.pieceId).style.border = “3px solid green”. Затем перейдите к следующей функции giveCellsClick() (строка 221).

Если ничего из этого не соответствует действительности, а это означает, что фигура не имеет возможного хода, ничего не делайте (return;)

Теперь нам нужно присвоить ячейкам атрибут onclick на основе возможных ходов selectedPiece.

Итак, если selectedPiece.[movement property] истинно, присвойте этой ячейке атрибут onclick, равный makeMove().

makeMove() будет принимать в качестве аргумента число или место, в которое будет перемещена фигура. Если фигура собирается переместиться на семь делений (selectedPiece.seventhSpace), присвойте ячейке onclick из makeMove(7), и это будет сделано для всех возможных ходов.

Вот причина, по которой нам нужно присвоить ячейкам атрибут onclick вместо event listener: если бы мы дали ячейкам прослушиватель событий и передали параметр, функция немедленно запустится, потому что технически она вызывается. Об использовании анонимной функции не может быть и речи, потому что ее нужно удалить, и вы не можете удалить анонимные прослушиватели событий.

Это конец цепочки функций, когда вы щелкаете по фрагменту, поэтому нам больше не нужно автоматически вызывать какие-либо функции. Следующая функция (makeMove()) - это то, что будет вызываться при щелчке по ячейке ПОСЛЕ выбора части.

Пора заняться функцией, которая выполняет большую часть тяжелой работы! Это немного сложно, но попробуй со мной.

Сначала нам нужно удалить часть на передней панели, мы можем легко сделать это с помощью .remove() (строка 259) и изменить .innerHTML ячейки на пустые строки (строка 260). С комбинацией этих двух ячейка будет выглядеть так, как будто там ничего не было.

Теперь о заявлении if…else. if (turn) (строка 261), затем… if (selectedPiece.isKing) мы хотим создать новый HTML-элемент части, которая имеет класс короля в новой ячейке, по которой щелкнули (строка 263). Затем сбросьте переменную redPieces на querySelectorAll(“p”) (строка 264); это очень важно, потому что, если этого не сделать, только что созданный новый фрагмент не будет сохранен в памяти JavaScript. Поэтому, когда вы щелкаете новый элемент, когда наступает следующий поворот, ничего не произойдет, потому что JavaScript не предоставил ему прослушиватель событий. Поэтому убедитесь, что эта строка добавлена.

Дублируйте этот код для оператора else в строке 265, но без класса короля.

Приведенный выше код можно продублировать для внешнего оператора else (строка 269), но измените все, что касается красных частей, на черные части и не забудьте изменить “p” на “span” (строки 272 и 275).

Теперь я сохраняю selectedPiece.indexOfBoardPiece в переменной (строка 279). Я не совсем уверен, почему, но вы не можете передавать свойства объекта непосредственно в аргументы функции, поэтому это необходимо сохранить как переменную.

if число - это прыжок (строка 280), следующая функция, которая будет вызвана, будет иметь третий параметр как позицию фигуры, которая прыгнула (это необходимо, потому что мы будем удалять фигуру, которая прыгнула с доски. ). else вызывает следующую функцию с двумя указанными параметрами: старое и новое.

Следующей функцией мы изменим все данные на задней панели, чтобы отразить плату на передней панели.

Здесь мы будем манипулировать всем на задней панели. А также, если необходимо, присвоить фигуре класс короля.

Сначала мы меняем исходное положение выбранной фигуры на null (строка 289), потому что фигуры там больше нет; затем измените положение новой фигуры, равное id той части, которая была перемещена (строка 290).

Выражение if в строке 291 говорит: если его красные поворачиваются, а selectedPiece.pieceId < 12, а его новое положение - >= 51 (≥ 51 - последняя строка на доске), тогда присвойте фигуре класс “king”. Затем сделайте обратное для хода черных (строки 294–296).

Теперь, если параметр removePiece существует (строка 297), измените удаленную часть массива board на null. Теперь, если его красный цвет станет красным (строка 299), заставьте фигуру исчезнуть на переднем конце и удалите счет с черного (строка 301). Тогда наоборот, если ходят черные.

Как только все это будет завершено, вызовите 3 функции: resetSelectedPieceProperties(), removeCellonclick() и removeEventListeners(). Первые два мы уже написали, поэтому я не буду их рассматривать, но их необходимо вызвать, чтобы все данные можно было сбросить для следующего хода (вот почему мы не вызывали никаких других новых функций внутри этих двух: чтобы их можно было использовать повторно).

Пришло время для removeEventListeners().

Не волнуйтесь ... мы почти закончили.

Если это красный ход (строка 315), пропустите весь redsPieces и удалите прослушиватель событий click, тогда в операторе else сделайте то же самое для blacksPieces.

Затем вызовите функцию checkForWin() .

Если какой-либо счет равен 0, измените противоположный текст хода на .style.display = “none”, а игрока, который выиграл, на «КРАСНАЯ ПОБЕДА!».

Вы можете проявить творческий подход к этому, если игрок знает, что он выиграл.

Пара примечаний: если у вас не было разделителя, не беспокойтесь об этом, а петли, которые вы видите в строках 331 и 338, вызваны тем, что у меня несколько [player]TurnText (один для мобильных устройств и один для настольных компьютеров), поэтому, если у вас только один [player]TurnText, цикл не нужен.

Теперь последняя функция changePlayer()!

Если его красный поворот, измените переменную turn на false, затем измените цвет blackTurnText на черный и redTurnText на светло-серый (наоборот, если поворот был ложным, то есть это был поворот черных).

Та же логика цикла применяется к предыдущей функции. Так что, если у вас только один [player]TurnText, цикл не нужен.

Наконец, вызовите функцию, которая была с самого начала: givePiecesEventListeners(), чтобы заново перезапустить весь цикл.

Строка 365 - это самая последняя строка файла JavaScript. Это нужно вызывать на глобальном уровне, поэтому цикл начинается после загрузки страницы (как я объяснил это, когда первоначально рассматривал функцию).

🎉 Поздравляю! Теперь у вас есть рабочая настольная игра в шашки! 🎉

Если вы хотите получить мой код, вы можете скачать его здесь: https://github.com/RyanBranco/Checkers

Теперь вы должны понимать, как можно использовать инструменты JavaScript (даже большинства языков программирования). Я призываю вас создать что-то самостоятельно, потому что это лучший способ учиться! Теперь тебе нет оправдания.