Forum bat: Форум — Всё о The Bat!
Содержание
Пишем тетрис в bat-файле / Хабр
В комментарии к моей предыдущей статье, «Какие задачи не решаются bat-файлами?», предположили, что на bat-файлах не получится написать Doom. Насчет Дума я пока не уверен, а вот тетрис у меня получился.
Сразу оговоримся, что код, который мы будем разбирать – это proof of concept. Он имеет недоработки, но я намеренно оставил его таким, чтобы не усложнять.
Bat-файл выложен на Яндекс-диск: ссылка
Upd. На Яндекс-диске превышен лимит скачивания. Вот тот же файл на Google Drive
Геймплей
Ввод с клавиатуры
В играх определенных жанров есть необходимость проверять, какие клавиши нажаты в данный момент, и при этом не останавливаться в ожидании нажатия. В С++ это делается всего одним вызовом GetKeyboardState(). В пошаговых играх и в играх типа тетриса может пригодиться посимвольный ввод, т.е. ожидание ввода одного символа без ожидания нажатия на Enter. В C++ это тоже один вызов – _getch(). Поэтому обидно, что в bat-файле ничего подобного сделать нельзя.
Но у нас есть choice.exe. Эта утилита ожидает ввода одного из перечисленных символов и выставляет errorlevel
, равный порядковому номеру выбранного варианта. Допускаются только буквы и цифры. Есть опция таймаута. При этом требуется указать вариант по умолчанию, который выбирается по истечении таймаута.
Напрашивается решение: использовать клавиши WASD, как обычно в играх, для движения. Только движения вверх в тетрисе нет, поэтому W у нас будет поворот. При этом S, т.е. вниз, будет вариант по умолчанию, который будет срабатывать каждую секунду.
choice /C "sawd" /D s /T 1 > NUL
Вывод перенаправлен в NUL, потому что подсказку по управлению мы выведем покрасивее и сбоку от игрового поля.
И тут возникает ограничение на скорость игры. Таймаут указывается как целое количество секунд. То есть, минимальный таймаут – одна секунда. Значение 0 допустимо, но в этом случае всегда возвращается вариант по умолчанию, и никакого ввода не получается.
Кроме того, таймаут отсчитывается от запуска choice.exe. Иными словами, от предыдущего ввода, так как у нас эта команда будет в бесконечном цикле. Таким образом, если постоянно нажимать вправо-влево, то фигура вообще перестает двигаться вниз. Это первый недостаток моей реализации игры.
Представление игрового поля
В bat-файлах нет массивов. Однако, можно использовать переменные с именами, содержащими индексы. Таким образом организуются псевдо-массивы. В нашем случае это двумерный массив field_%Y%_%X%
. Y меняется от 0 до 15, причем, 0 – это верх. X – от 0 до 7. Элементы равняются либо 0 – блок свободен; либо 1 – блок занят.
Перед началом игры поле инициализируется нулями:
for /L %%a in (0,1,15) do ( for /L %%b in (0,1,7) do set field_%%a_%%b=0 )
Рендеринг игрового поля
Псевдографика позволит изобразить по две строки поля в одной строке текста. Половина ячейки символа – один блок. Так блоки получаются более-менее квадратными. Символ с кодом 223 представляет собой закрашенную верхнюю половину ячейки, 220 – нижнюю, а 219 – полностью закрашенная ячейка.
В bat-файлах нет способа указать код символа цифрами, зато можно вставить псевдографику как есть. Для интерпретатора это обычные символы, которые могут быть частью переменной или параметра команды.
Напомню, что bat-файл надо сохранять в кодировке 866. Кодировку можно изменить командой chcp, но нам это не понадобится.
Когда обе половины ячейки свободны, нужно вывести пробел. Это значит, в скрипте будет строка, оканчивающаяся пробелом. Его не видно, а он есть, и есть риск его удалить при редактировании. В кавычки его ставить нельзя. Если с командами SET и ECHO использовать кавычки, они попадают, соответственно, в переменную и на экран.
Перемещение курсора
Чтобы обновить весь экран, есть вариант его очистить командой CLS, а потом вывести всё содержимое. Но в нашем случае рендеринг идет дольше смены кадров, и изображение мигает, если очищать экран. Поэтому нужно переместить курсор в точку (1;1) и вывести новое поле поверх старого.
Для перемещения курсора нам понадобится код ANSI-терминала <ESC>[1;1H. <ESC> – это символ с кодом 27 (033 или 0x1B). Его, как и псевдографику, можно вставлять в команды как есть. Главное – найти текстовый редактор, который позволит это делать. Notepad++ подойдет. Спасибо @horror_x за подсказку.
Алгоритмы
Написание своей реализации тетриса – это упражнение не столько на графику, сколько на алгоритмы. Нужно определиться с представлением фигуры в памяти, рассчитывать поворот фигуры и ее коллизии с «ландшафтом».
Выбор фигуры
В таблице ниже представлены все фигуры тетриса
Все фигуры
Случайным образом (через переменную %RANDOM%) выбираются значения переменных figtype
(от 0 до 3) и figflip
(0 или 1). Для figtype
от 0 до 2 изменение figflip
– это зеркальное отражение относительно вертикальной оси. Фигуры с figtype
3 симметричны относительно обеих осей, поэтому для этого случая figflip
выбирает саму фигуру. Это позволяет не кодировать явно все 8 фигур. Достаточно пяти. Фигуры с figtype
<3 и figflip
=1 получаются отражением.
Представление фигур
Функция :getfigure
заполняет переменные figdefx
и figdefy
. В каждой из них 4 цифры. Каждая цифра – координата. figdefx
– это X-координаты, а figdefy
– Y. Таким образом закодированы положения 4 блоков, из которых состоит фигура. Эти переменные не меняются при повороте фигуры по ходу игры, и figflip
их тоже не меняет. Вместо этого координаты преобразуются при помощи матрицы поворота и отражения.
Матричная алгебра
:getfigure
инициализирует матрицу либо единичной матрицей, либо матрицей отражения в зависимости от figflip
. Матрица хранится в 4 переменных: mat11
, mat12
, mat21
и mat22
.
При нажатии на W происходит умножение матрицы поворота на 90° по часовой стрелке на текущую матрицу. При умножении матриц от перестановки множителей произведение меняется. Первой должна идти матрица, описывающая преобразование, выполняемое последним.
Если поворот приводит к коллизии, выполняется поворот в другую сторону аналогичным образом. В общем случае в играх так не делают, потому что накопление погрешности вычислений. Но у нас частный случай, когда все числа целые, повороты только на 90°, и элементы матрицы могут равняться только 0, 1 и -1.
Способ кодирования фигур не допускает отрицательных координат. Вся фигура занимает один квадрант и поворачивается относительно своего конца, а не центра. Это еще одна вещь, которую я не исправлял.
Применение фигуры
Функция :applyfigure
принимает 3 параметра: колбэк и координаты. Функция рассчитывает координаты каждого блока фигуры с учетом указанного смещения и матрицы и вызывает колбэк, передавая ему эти координаты.
Используются следующие колбэки:
:setblock
заполняет блок, т.е. присваивает значение 1 элементу псевдо-массиваfield_%2_%1
. Таким образом, фигура рисуется на поле;:clearblock
очищает блок. Фигура стирается с поля;:testcollision
возвращает errorlevel 1 если указанный блок заполнен или находится за границами поля. Коллизия с верхним краем тоже проверяется. Она может произойти при повороте фигуры.
Если колбэк возвращает errorlevel 1
, :applyfigure
прерывает выполнение и тоже возвращает 1.
Жизненный цикл фигуры
На метке :nextfigure
после выбора очередной фигуры эта фигура рисуется на поле в точке (3; 0). Если при этом возникает коллизия, игра заканчивается. Как вы помните, координаты блоков в определении фигуры неотрицательны, а флаг figflip
отражает фигуру относительно вертикальной оси. Таким образом, коллизия с верхним краем исключена. Коллизии с правым и левым краем тоже исключены, потому что нет таких широких фигур. Таким образом, коллизия может быть только с ландшафтом.
Если при повороте фигуры, движении вправо или влево происходит коллизия, движение или поворот не выполняется (в случае поворота вычисляется поворот обратно). Помните, что точка, вокруг которой поворачивается фигура, – это не центр фигуры. Если фигура не поворачивается, отодвиньте ее от края.
Если коллизия происходит при движении вниз, фигура становится частью ландшафта, т.е. не стирается с поля. С поля удаляются заполненные ряды, если такие есть, и выбирается следующая фигура.
За удаление заполненных рядов отвечает функция :removefulllines
. Надеюсь, там ничего сложного.
Заключение
Мы убедились, что на языке bat-файлов вполне можно писать игры. В приведенном примере кода есть недостатки, зато я надеюсь, что он прост для понимания.
На этом примере мы показали использование команды choice
для ввода с таймаутом, псевдографику и коды ANSI-терминала.
Мы продемонстрировали практическое использование псевдо-массивов, колбэков и базовых алгоритмов.
Специальная авиационная служба — Индекс
Ангар SAS
Объявления | 459 сообщений 9 тем | Последнее сообщение от SAS~CirX в Re: Пожертвование SAS 30 октября 2022 г., 22:32:18 | |
Гостиная | 123105 сообщений 10436 тем | Последнее сообщение от KingTiger503 в Re: КЦ Скриншоты и Ви… к Сегодня в 10:32:58 | |
Дочерние доски : Фильмы и скриншоты, Доска не на английском языке, Запросы и идеи, Новый симулятор полета, Plastix, SAS Dogfight Server, Linx | |||
Зона безопасности для новых участников | 13481 сообщений 2178 тем | Последнее сообщение от Cybersnow46 в Re: HOTAS продолжает падать 10 ноября 2022 г. , 14:41:19 |
Battlefield — Airborne — Tactical (BAT)
Отдельные модификации и наборы для Ил-2 1946 г.
SAS Essentials / Базовые модификации | 3123 сообщений 26 тем | Последнее сообщение от Frankiek в Re: UltraSpain 2019 07 октября 2022 г., 17:30:37 | |
Детские доски : Модакт SAS | |||
Самолет | 64475 сообщений 1176 тем | Последнее сообщение от Dandolo513 в Re: Kyushu Q1W1 Tokai … на Сегодня в 05:51:30 | |
Дочерние доски : Самолеты, Близнецы, Тяжелые, Гидросамолеты, Немецкие_Одиночные, Япония_Одиночные, Великобритания_Одиночные, США_Одиночные, СССР_Одиночные, Итальянские_Одиночные, Польские/Чешские_Одиночные, Французские/Испанские_Одиночные, Другие_Одиночные, Вертолеты/Дирижабли, Ангар 9 Первой мировой войны, Франкенштейн | |||
скинов, карт, миссий и кампаний | 71413 Сообщений 6774 Тем | Последнее сообщение от oldschoolie в Re: A6M2 Type 0’Saburo S. .. на Сегодня в 11:36:49 | |
Дочерние доски : Skinshop, Skin&Markings, Templates, SkinRequests, German Skins, Italian Skins, French, Polish & Czech Skins, USA Skins, USA Skins, UK Skins, Japanese Skins, WWI Skins, MapWorx (Common), ETO Карты, карты PTO, карты MTO, карты WWI, текстуры карт, карты WIP, миссии (общие), обсуждения миссий и запросы, кампании, MissionPro, кампании и миссии WW1 | |||
Другие моды | 72940 Сообщений 2004 Темы | Последнее сообщение от Dandolo513 в Kyushu Nankai Q3W1 и S… на Сегодня в 12:19:54 | |
Дочерние доски : WIP, SlotRequest, FM Discussions, Cockpits (Common), Cockpits (New Flyables), Cockpits (ReSkins), Sounds (Common), Sounds (Speech), Sounds (Motors), Sounds (Arms) , Звуки (FX), Корабли, Транспорт + Артиллерия, Визуальные моды, 3Dfix, Эффекты, Наборы оружия, Stereo3D, ModWorx | |||
Ultrapack Обсуждение и поддержка | 5092 Сообщений 536 Тем | Последнее сообщение от KingTiger503 в Re: Отчет об ошибке Ultrapack. .. на Сегодня в 10:42:15 | |
Дочерние доски : Загрузки и обновления Ultrapack | |||
4.09 База повстанцев | 2390 сообщений 156 тем | Последнее сообщение от Epervier в Re: Breda Ba.65 — DH-89 … 11 ноября 2022 г., 05:14:51 |
Фабрика SAS — техническая помощь, древние моды и т. д.
Техническая помощь (кроме BAT или IL-2 Great Battles) | 18968 Сообщений 2894 Тем | Последнее сообщение от WxTech в Re: Perfect Landscape Ca… на Сегодня в 07:02:05 | |
Дочерние доски : Техническая помощь : Использование модов, Техническая помощь : Создание модов | |||
На память: старые пакеты модов, игровые версии и гостевые моды | 18386 Сообщений 1852 Тем | Последнее сообщение от Sergent Pepper в Re: VP Modpack 1. 2 Beta от 30 октября 2022 г., 07:20:23 | |
Дочерние доски : Обсуждение и поддержка Dark Blue World, Универсальный патч сообщества (C.U.P.), Обсуждение и поддержка HSFX, VP Modpack, Cliffs of Dover, IL-2 Great Battles |
Карта сайта.
|