Меню Рубрики

Как написать тетрис на javascript

Как написать тетрис на javascript

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

Современная версия формата разметки HTML5 в связке c CSS и Javascript превращается не просто в массовый инструмент разработки, на котором можно писать игры из. нуля строк кода, но, того и гляди, «HTML-программисты» потеснят классических 🙂 По крайней мере, большинство детских курсов по программированию построены именно на этой связке.

Удобно то, что вся логическая разметка, будь то форма приложения или канва для вывода графики, делается обычным HTML, для оформления и вёрстки идеально подходят стили CSS, а в JS есть средства для удобного доступа к любому узлу документа, высокоуровневые средства программирования и готовые таймеры для реализации анимации. К тому же, в JS нет в чистом виде обычно непонятных начинающим «классов» с запутанными отношениями между ними, но есть произвольное конструирование объектов.

Шагая в ногу со временем, давайте и мы реализуем на HTML+Javascript что-нибудь законченное, например, классический Тетрис примерно так, как он описан в Википедии.

За разметку будет отвечать обычный HTML-файл с именем index.html , предусмотрим вёрстку в 2 колонки с идентификаторами tetrisleft и tetrisright и 2 графичеких канвы — tetriscanvas для вывода основного игрового поля и figurecanvas для отдельной отрисовки следующей фигуры. Для вывода информационных элементов игры также предусмотрим стилевой класс info .

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

Файл index.html
Файл tetrisstyle.css

Из HTML-кода видно, что мы подключили целых три яваскрипт-файла. Файл canvas.js будет отвечать за обработку основного игрового поля, controller.js добавит к приложению обработчик событий (в нашем случае — только нажатия клавиш), а game.js займётся основной логикой игры.

Все эти файлы поместим во вложенную папку с именем js .

Файл canvas.js просто получит ссылку на основную канву и займётся отрисовкой игрового поля по данным, содержащимся в массиве board файла game.js . У него будет собственная частота перерисовки канвы, равная 50 миллисекундам.

Файл js/canvas.js

Файл controller.js устроен ещё проще — там прописываются коды нужных клавиш и ставится обработчик события нажатия клавиши в документе.

Стрелки влево и вправо будут двигать фигурку в соответствующих направлениях, стрелка вверх — вращать, а стрелка вниз или пробел — ускорять падение. Паузу можно будет удобно вызвать нажатием клавиши Escape, а возобновить игру — повторным нажатием Escape или кликом по кнопке «OK» в появившемся окне «Pause» (в Javascript уже есть модальное окно window.alert , так что программировать задержки нам не придётся).

Файл js/controller.js

Сама игра, конечно же, будет подлиннее, ровно в 200 строк, но комментарии помогут вам без проблем понять её.

Файл js/game.js

Разумеется, нетрудно прикрутить прикрутить сюда, например, сохранение рекорда в Cookie-файле, предусмотреть увеличение скорости игры в зависимости от количества убранных линий или набранных очков (уменьшать переменную interval в game.js ) и т.д.

Также, кроме паузы по нажатию Esc, мы не предусмотрели каких-либо остановок в игре, по завершении текущей игры немедленно начинается новая. В общем, проект есть куда развивать.

Так как традиционная для тетриса фоновая мелодия «Коробейники» займёт 99% объёма архива, выкладывать сюда никакой архив с проектом не будем, а просто сошлёмся на звуковые файлы. Формат звука .ogg , думаю, удобнее всего, чтобы «шло» и на «Андроиде»:

  • файл sound/music.ogg, фоновая музыка «Коробейники»;
  • файл sound/pop.ogg, звук убирания линии.

Все остальные файлы проекта — выше по тексту 🙂

UPD: добавил под «стаканом» кнопки, дублирующие действие клавиш. Там всего 5 строчек кода в файле .html , апдейтить статью ради этого лень, смотрите изменения в исходнике страницы с игрой. Ну и с бесклавиатурных устройств, наверное, можно так играть 🙂

Ну, чем хуже оригинала? 🙂 Кстати, чёрно-белый оригинал от 1986 года у меня сработал под Windows7, только таймер там, видимо, абсолютный, из-за чего слишком быстро падают фигурки. Но из-под эмулятора DOS должно всё быть идеально.

Источник статьи: http://blog.kislenko.net/show.php?id=1770

Тетрис на JavaScript

Отме­няй­те все дела, пере­но­си­те встре­чи. Сего­дня мы дела­ем тет­рис, в кото­рый мож­но играть в бра­у­зе­ре и на кото­рый мож­но потра­тить весь день.

В чём идея

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

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

Если вдруг не зна­е­те, как это рабо­та­ет, вот фраг­мент с чем­пи­о­на­та мира по тет­ри­су:

Код не мой

Код, кото­рый мы раз­би­ра­ем в этом про­ек­те, напи­сал аме­ри­кан­ский раз­ра­бот­чик Сти­вен Лам­берт:

В этой ста­тье мы объ­яс­ним, как этот код рабо­та­ет.

Неожиданная сложность

Самое глав­ное при про­грам­ми­ро­ва­нии такой игры — это как-то хра­нить содер­жи­мое игро­во­го экра­на и учи­ты­вать дви­же­ние фигур.

Если бы мы писа­ли эту игру на Unreal Engine или Unity, пер­вым инту­и­тив­ным реше­ни­ем было бы сде­лать для бло­ков какую-то сущ­ность типа объ­ек­та. У него были бы свой­ства — напри­мер, кон­фи­гу­ра­ция. Может быть, мы бы захо­те­ли потом сде­лать взры­ва­ю­щи­е­ся объ­ек­ты или объ­ек­ты с замо­роз­кой, объ­ек­ты с повы­шен­ной ско­ро­стью, отрав­лен­ные объ­ек­ты или что-то ещё в таком духе.

Но есть нюанс: смысл объ­ек­та в том, что он неде­ли­мый. А в «Тет­ри­се» все объ­ек­ты запро­сто делят­ся, когда мы «закры­ва­ем линию». У какой-нибудь Т-образной фигу­ры может запро­сто про­пасть хво­стик, а у Z-образной фигу­ры — ниж­няя пере­кла­ди­на.

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

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

Сами фигу­ры тоже пред­ста­вим в виде дву­мер­но­го мас­си­ва из нолей и еди­ниц, но осо­бым обра­зом — в виде квад­ра­та, где еди­ни­цы отве­ча­ют за части фигу­ры, а ноли — за пустое место:

Если вме­сто квад­ра­та про­сто взять фак­ти­че­ские раз­ме­ры фигу­ры и загнать их в мас­сив, то при вра­ще­нии они не вле­зут в исход­ный мас­сив. А внут­ри квад­ра­та их мож­но вра­щать как угод­но — раз­мер мас­си­ва от это­го не изме­нит­ся:

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

Подготовка страницы

Игра будет рабо­тать на HTML-странице с помо­щью эле­мен­та Canvas — это холст, на кото­ром мы можем рисо­вать про­из­воль­ные фигу­ры через JavaScript.

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

Всё осталь­ное сде­ла­ем скрип­том. Доба­вим тэг сра­зу после того, как нари­со­ва­ли холст, и нач­нём писать содер­жи­мое скрип­та.

Заводим переменные и константы

  • дела­ем мас­сив для игро­во­го поля и запол­ня­ем его;
  • дела­ем мас­си­вы, кото­рые хра­нят наши фигу­ры и их цве­та;
  • в отдель­ном мас­си­ве будем хра­нить новые фигу­ры, кото­рые появят­ся сле­ду­ю­щи­ми;
  • дела­ем флаг оста­нов­ки игры. Пока он не сра­бо­та­ет — мож­но играть.

Генерируем выпадающие фигуры

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

Теперь мы можем создать после­до­ва­тель­ность из выпа­да­ю­щих фигур. Логи­ка будет такая:

  1. Зада­ём обыч­ную после­до­ва­тель­ность доступ­ных фигур.
  2. Слу­чай­ным обра­зом заби­ра­ем отту­да фигу­ру и поме­ща­ем в игро­вую после­до­ва­тель­ность.
  3. Так дела­ем до тех пор, пока от обыч­ной после­до­ва­тель­но­сти ниче­го не оста­нет­ся.
  4. У нас полу­чи­лась слу­чай­ная игро­вая после­до­ва­тель­ность фигур, с кото­ры­ми мы будем рабо­тать даль­ше.

Послед­ний этап в этом бло­ке — полу­чить из игро­вой после­до­ва­тель­но­сти, кото­рую мы толь­ко что сде­ла­ли, сле­ду­ю­щую фигу­ру, кото­рая у нас появит­ся. Мы долж­ны знать, что это за фигу­ра; как она рису­ет­ся; отку­да она начи­на­ет дви­же­ние. Обра­ти­те вни­ма­ние: на выхо­де мы полу­ча­ем не толь­ко дву­мер­ный мас­сив с фигу­рой, а ещё и назва­ние и её коор­ди­на­ты. Назва­ние нам нуж­но для того, что­бы знать, каким цве­том рисо­вать фигу­ру.

Движение, вращение и установка фигуры на место

В тет­ри­се мы можем вра­щать каж­дую фигу­ру на 90 гра­ду­сов по часо­вой стрел­ке сколь­ко угод­но раз. А так как у нас фигу­ра — это дву­мер­ный мас­сив из чисел, то быст­ро най­дём в интер­не­те гото­вый код для пово­ро­та чис­ло­вой мат­ри­цы:

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

Если про­вер­ка не про­шла, то мы не дела­ем послед­нее дви­же­ние, и фигу­ра про­сто про­дол­жа­ет падать вниз. Если ей неку­да падать и она упёр­лась в дру­гие, то нам нуж­но зафик­си­ро­вать это в игро­вом поле. Это зна­чит, что мы запи­сы­ва­ем в мас­сив, кото­рый отве­ча­ет за поле, нашу мат­ри­цу фигу­ры, про­пус­кая ноли и запи­сы­вая толь­ко еди­ни­цы.

Как толь­ко фигу­ра вста­ла, нам нуж­но про­ве­рить, полу­чил­ся целый ряд или нет. Если полу­чил­ся — сдви­га­ем на один ряд вниз всё, что свер­ху. Такую про­вер­ку дела­ем каж­дый раз при уста­нов­ке фигу­ры и начи­на­ем с ниж­не­го ряда, под­ни­ма­ясь наверх.

Что будет, когда мы проиграем

Когда фигу­ра при окон­ча­тель­ной уста­нов­ке выле­за­ет за гра­ни­цы игро­во­го поля, это зна­чит, что мы про­иг­ра­ли. За это у нас отве­ча­ет флаг gameOver, и его зада­ча — оста­но­вить ани­ма­цию игры.

Что­бы было понят­но, что игра закон­че­на, выве­дем над­пись GAME OVER! пря­мо поверх игро­во­го поля:

Обрабатываем нажатия на клавиши

Всё как в обыч­ном тет­ри­се: стрел­ки вле­во и впра­во дви­га­ют фигу­ру, стрел­ка вверх пово­ра­чи­ва­ет её на 90 гра­ду­сов, а стрел­ка вниз уско­ря­ет паде­ние.

Един­ствен­ное, о чём нуж­но не забыть — после каж­до­го нажа­тия вызвать про­вер­ку, мож­но ли так дви­гать фигу­ру или нет.

Запускаем движения и анимацию

Смысл глав­но­го цик­ла игры такой:

  • на каж­дом кад­ре мы очи­ща­ем игро­вое поле и отри­со­вы­ва­ем его зано­во с учё­том упав­ших фигур;
  • рису­ем теку­щую фигу­ру в том месте, где она нахо­дит­ся в дан­ный момент.

Так как кад­ры меня­ют­ся быст­ро, мы не заме­тим посто­ян­но­го очи­ще­ния и отри­сов­ки. Нам будет казать­ся, что фигу­ра про­сто дви­жет­ся вниз и реа­ги­ру­ет на наши дей­ствия.

Послед­нее, что нам оста­лось сде­лать, — запу­стить игру:

// старт игры rAF = requestAnimationFrame(loop);

Гото­вый резуль­тат мож­но посмот­реть на стра­ни­це с игрой.

Источник статьи: http://thecode.media/tetris/

Tetris на javascript (в 30+ строк)

Решил поддержать тему!

Тетрис в 30+ строк js кода.

  • Знает все фигуры тетриса
  • Управление с клавиатуры
  • ВВЕРХ — фигурки крутятся по часовой стрелке
  • ВНИЗ — ускорить падение
  • Скорость падания постепенно увеличивается
  • Очки подсчитываются

ссылка на jsfiddle

Принцип работы.

1. Получаем фигурки
Все фигурки хранятся в переменной fs=«1111:01|01|01|01*011|110:010|011|001*. » в виде строки. Чтобы получить массив фигур — делаем split(‘*’), далее в каждой фигуре есть от 1-го (для «палки») до 4-х (для L, Г и «пирамидки») состояния (чтобы их можно было переворачивать) — они разделены «:» — соответственно чтобы получить одно состояние — split(‘:’). Допустим получили «пирамидку» — «010|111», здесь делаем split(‘|’) — и получаем уже конечный двумерный массив для одного состояния одной фигуры. «0»- пустое пространство (не нужно отрисовывать), «1» — нужно рисовать.

2. Всё перемещение фигур делается двумя функциями — «стереть фигуру» и «попытаться построить».
При любых перемещениях вправо-влево или вниз, или даже переворот фигуры — сначала стираем текущую отображаемую фигуру, потом пытаемся построить фигуру на новом месте — если при постройке какой-то из квадратиков вылазит за край «стакана», или попадает на место, где уже есть заполненный квадратик от предыдущих фигур — стираем «активную фигуру», и стоим ее на предыдущем месте.

3. Перемещения делаются с клавиатуры. По таймеру просто вызывается функция, которая обрабатывает нажатия с клавиатуры с кодом кнопки ВНИЗ. При каждом вызове таймаута — время до вызова уменьшается.

4. Если не удалось переместить фигуру вниз — значит она уперлась в предыдущие фигуры или дно. В таком случае проверяем нет ли заполненных строк, и рисуем новую фигуру вверху стакана. Если вверху стакана нарисовать фигуру не удалось — игра закончена!

Источник статьи: http://habr.com/ru/post/202628/

Как написать свой Тетрис на Java за полчаса

В предыдущих статьях этой серии мы уже успели написать сапёра, змейку и десктопный клон игры 2048. Попробуем теперь написать свой Тетрис.

Нам, как обычно, понадобятся:

  • 30 минут свободного времени;
  • Настроенная рабочая среда, т.е. JDK и IDE (например, Eclipse);
  • Библиотека LWJGL (версии 2.x.x) для работы с графикой (опционально). Обратите внимание, что для LWJGL версий выше 3 потребуется написать код, отличающийся от того, что приведён в статье;
  • Спрайты, т.е. картинки плиток всех возможных состояний (пустая, и со степенями двойки до 2048). Можно нарисовать самому, или скачать использовавшиеся при написании статьи.

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

С чего начать?

Начать стоит с главного управляющего класса, который в нашем проекте находится выше остальных по уровню абстракции. Вообще отличный совет – в начале работы всегда пишите код вида if (getKeyPressed()) doSomething() , так вы быстро определите фронт работ.

Это наш main() . Он ничем принципиально не отличается от тех, что мы писали в предыдущих статьях – мы всё так же инициализируем поля и, пока игра не закончится, осуществляем по очереди: ввод пользовательских данных ( input() ), основные игровые действия ( logic() ) и вызов метода отрисовки у графического модуля ( graphicsModule.draw() ), в который передаём текущее игровое поле ( gameField ). Из нового разве что метод sync – метод, который должен будет гарантировать нам определённую частоту выполнения итераций. С его помощью мы сможем задать скорость падения фигуры в клетках-в-секунду.

Вы могли заметить, что в коде использована константа FPS . Все константы удобно определять в классе с public static final полями. Полный список констант, который нам потребуется в ходе разработки, можно посмотреть в классе Constants на GitHub.

Оставим пока инициализацию полей на потом (мы же ещё не знаем, какие нам вообще понадобятся поля). Разберёмся сначала с input() и logic() .

Получение данных от пользователя

Код, честно говоря, достаточно капитанский:

Все данные от ввода мы просто сохраняем в соответствующие поля, действия на основе них будет выполнять метод logic() .

Старт 5 октября, 8 месяцев, Онлайн, От 71 200 до 76 000 ₽

Теперь уже потихоньку становится понятно, что нам необходимо. Во-первых, нам нужны клавиатурный и графический модули. Во-вторых, нужно как-то хранить направление, которое игрок выбрал для сдвига. Вторая задача решается просто – создадим enum с тремя состояниями: AWAITING, LEFT, RIGHT . Зачем нужен AWAITING ? Чтобы хранить информацию о том, что сдвиг не требуется (использования в программе null следует всеми силами избегать). Перейдём к интерфейсам.

Интерфейсы для клавиатурного и графического модулей

Так как многим не нравится, что я пишу эти модули на LWJGL, я решил в статье уделить время только интерфейсам этих классов. Каждый может написать их с помощью той GUI-библиотеки, которая ему нравится (или вообще сделать консольный вариант). Я же по старинке реализовал их на LWJGL, код можно посмотреть здесь в папках graphics/lwjglmodule и keyboard/lwjglmodule.

Интерфейсы же, после добавления в них всех упомянутых выше методов, будут выглядеть следующим образом:

Отлично, мы получили от пользователя данные. Что дальше?

А дальше мы должны эти данные обработать и что-то сделать с игровым полем. Если пользователь сказал сдвинуть фигуру куда-то, то передаём полю, что нужно сдвинуть фигуру в таком-то направлении. Если пользователь сказал, что нужно фигуру повернуть, поворачиваем, и так далее. Кроме этого нельзя забывать, что 1 раз в FRAMES_PER_MOVE (вы же открывали файл с константами?) итераций нам необходимо сдвигать падающую фигурку вниз.

Сюда же добавим проверку на переполнение поля (в Тетрисе игра завершается, когда фигурам некуда падать):

Так, а теперь мы напишем класс для того магического gameField, в который мы всё это передаём, да?

Не совсем. Сначала мы пропишем поля класса Main и метод initFields() , чтобы совсем с ним закончить. Вот все поля, которые мы использовали:

А инициализировать мы их будем так:

Если вы решили не использовать LWJGL и написали свои классы, реализующие GraphicsModule и KeyboardHandleModule , то здесь нужно указать их конструкторы вместо, соответственно new LwjglGraphicsModule() и new LwjglKeyboardHandleModule() .

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

Класс GameField

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

Хранить информацию о поле…

…и о падающей фигуре

TpReadableColor — простой enum, содержащий элементы с говорящими названиями (RED, ORANGE и т.п.) и метод, позволяющий получить случайным образом один из этих элементов. Ничего особенного в нём нет, код можно посмотреть тут.

Это все поля, которые нам понадобятся. Как известно, поля любят быть инициализированными.
Сделать это следует в конструкторе.

Конструктор и инициализация полей

«Что это за OFFSET_TOP ?» – спросите вы. OFFSET_TOP это количество неотображаемых ячеек сверху, в которых создаются падающие фигуры. Если фигуре не сможет «выпасть» из этого пространства, и хоть одна ячеек theField выше уровня COUNT_CELLS_Y будет заполнена, это будет обозначать, что поле переполнено и пользователь проиграл, поэтому OFFSET_TOP должен быть строго больше нуля.

Далее в конструкторе стоит заполнить массив theField значениями константы EMPTINESS_COLOR , а countFilledCellsInLine – нулями (второе в Java не требуется, при инициализации массива все int‘ы равны 0). Или можно создать несколько слоёв уже заполненных ячейкам — на GitHub вы можете увидеть реализацию именно второго варианта.

А что это там за spawnNewFigure()? Почему инициализация фигуры вынесена в отдельный метод?

Вы правильно догадались, spawnNewFigure() действительно инициализирует поле figure . А в отдельный метод это вынесено, потому что нам придётся делать инициализацию каждый раз, когда будет создаваться новая фигура.

На этом с хранением данных мы закончили. Переходим к методам, которые отдают информацию о поле другим классам.

Методы, передающие информацию об игровом поле

Таких метода всего два. Первый возвращает цвет ячейки (для графического модуля):

А второй сообщает, переполнено ли поле (как это происходит, мы разобрали выше):

Методы, обновляющие фигуру и игровое поле

Начнём реализовывать методы, которые мы вызывали из Main.logic() .

Сдвиг фигуры

За это отвечает метод tryShiftFigure() . В комментариях к его вызову из Main было сказано, что он «пробует сдвинуть фигуру». Почему пробует? Потому что если фигура находится вплотную к стене, а пользователь пытается её сдвинуть в направлении этой стены, никакого сдвига в реальности происходить не должно. Так же нельзя сдвинуть фигуру в статические ячейки на поле.

Что мы сделали в этом методе? Мы запросили у фигуры ячейки, которые бы она заняла в случае сдвига. А затем для каждой из этих ячеек мы проверяем, не выходит ли она за пределы поля, и не находится ли по её координатам в сетке статичный блок. Если хоть одна ячейка фигуры выходит за пределы или пытается встать на место блока – сдвига не происходит. Coord здесь – класс-оболочка с двумя публичными числовыми полями (x и y координаты).

Поворот фигуры

Падение фигуры

Сначала код в точности повторяет предыдущие два метода:

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

Так как в результате переноса ячеек какая-то линия может заполниться полностью, после каждого добавления ячейки мы проверяем линию, в которую мы её добавили, на полноту:

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

Теперь GameField реализован почти полностью — за исключением геттера для фигуры. Её ведь графическому модулю тоже придётся отрисовывать:

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

Класс фигуры

Реализовать это всё я предлагаю следующим образом – хранить для фигуры (1) «мнимую» координату, такую, что все реальные блоки находятся ниже и правее неё, (2) состояние поворота (их всего 4, после 4-х поворотов фигура всегда возвращается в начальное положение) и (3) маску, которая по первым двум параметрам будет определять положение реальных блоков:

Rotation мод здесь будет выглядеть таким образом:

Соответственно, от самого класса Figure нам нужен только конструктор, инициализирующий поля:

И методы, которыми мы пользовались в GameField следующего вида:

Вдобавок, у фигуры должен быть цвет, чтобы графический модуль мог её отобразить. В тетрисе каждой фигуре соответствует свой цвет, поэтому цвет мы будем запрашивать у формы:

Форма фигуры и маски координат

Чтобы не занимать лишнее место, здесь я приведу реализацию только для двух форм: I-образной и J-образной. Код для остальных фигур принципиально не отличается и выложен на GitHub.

Храним для каждой фигуры маску координат (которая определяет, насколько каждый реальный блок должен отстоять от «мнимой» координаты фигуры) и цвет:

Реализуем методы, которые использовали выше:

Ну а сами маски координат я предлагаю просто захардкодить следующим образом:

Т.е. для каждого объекта enum‘а мы передаём с помощью импровизированных (других в Java нет) делегатов метод, в котором в зависимости от переданного состояния поворота возвращаем разные реальные координаты блоков. В общем-то, можно обойтись и без делегатов, если хранить в каждом элементе отсупы для каждого из режимов поворота.

Наслаждаемся результатом

P.S. Ещё раз напомню, что исходники готового проекта доступны на GitHub.

Хинт для программистов: если зарегистрируетесь на соревнования Huawei Honor Cup, бесплатно получите доступ к онлайн-школе для участников. Можно прокачаться по разным навыкам и выиграть призы в самом соревновании.

Перейти к регистрации

Источник статьи: http://tproger.ru/articles/java-30-mins-tetris/


0 0 голоса
Article Rating
Подписаться
Уведомить о
guest

0 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии