Пошаговая RPG в Excel (Начало)
Вам бывает скучно на работе? Часто сидите два-три дня без единого намёка на трудовую деятельность и пытаетесь ее имитировать, а руководство так и ждет ошибки с вашей стороны?
Теперь этого можно будет избежать, ведь в разработке находится пошаговая RPG в стиле Dragon Quest на NES для офисного приложения Excel.
Конечно, идея не нова. В Excel и раньше делали разнообразные игры (даже полноценный шутер). Но если вы читали мои предыдущие статьи, то поймёте, что создавать троллейбусы из батонов белого (или чёрного) хлеба — мое небольшое хобби.
Перейдём же к делу
На данный момент готова альфа-версия графического движка и редактор карт. С вероятностью в 99% они будут дорабатываться в процессе разработки.
Сперва немного расскажу о некоторых технических характеристиках.
1. Скорее всего в игре будет использоваться событийная модель, а не циклическая, ведь для пошаговой стратегии цикл не так уж и важен. Это будет зависеть от того, как быстро Excel справится с рендером одного кадра. Минус такого подхода заключается в том, что написать какой-нибудь платформер на этой основе не получится. В любом случае я попробую оба варианта.
2. Все текстуры имеют размер 16*16 пикселей. Палитра состоит всего из 56 цветов (стандартный размер палитры Excel). В качестве основы я взял палитру NES.
3. Текстуры я рисую в программе Aseprite, а в Excel из. bmp перевожу с помощью небольшого, написанного на VBA софта, который нашел в интернете.
4. Бэкграунд состоит из тайлов, спрайты же привязаны к системе координат.
Графический движок
Карта уровня представляет собой двумерный массив с кодовым обозначением тайла в формате «XXXY», где XXX — номер текстуры по порядку, Y — значение, указывающее на то, можно ли пройти сквозь тайл. Вторая функция пока что не реализована.
Спрайты хранятся в отдельном массиве в формате: координата X, координата Y, порядковый номер спрайта, тип спрайта и тэг. Два последних значения пока не используются.
Для создания графического движка сперва необходимо инициализировать различные переменные. Часть из них хранится на отдельном листе, а в коде я сделал их публичными (знаю, что так нельзя):
- высота и ширина игрового экрана;
- диапазон игрового экрана;
- массив игрового экрана для рендеринга;
- размер одной текстуры;
- координаты камеры;
- координаты игрока;
- номер уровня;
- карта тайлов и координаты спрайтов;
- массив спрайтов, тайлов и спрайтов игрока.
Все текстуры хранятся на отдельном листе с отображением индексов цветов палитры.
Так как это только первая версия движка, нажатие на «Новую игру» тут же запускает метод fillWithTextures (процесс создания массива цифровых значений цвета).
Первым делом происходит поиск спрайтов, которые нужно отрисовать. Для этого программа проверяет каждый «пиксель» игрового экрана, считает смещение этого пикселя относительно стартовых координат камеры, а также смещение относительно всех спрайтов на уровне. Если смещение «пикселя» относительно спрайта по каждой оси равно от 0 до 15 (так как размер текстуры 16*16), берётся индекс нужного цвета из массива спрайтов.
Вторым пунктом программа на основе смещения относительно координат камеры высчитывает позицию «пикселей» на карте тайлов. Когда нужный тайл найден, программа с помощью функции getTextOffset возвращает индекс цвета пикселя из массива тайлов.
Почему сперва происходит проверка спрайтов, а затем тайлов?
Все просто. Программа просто не трогает те «пиксели», которые уже закрашены спрайтами, что влияет на производительность в лучшую строну.
P.S. На следующий день я понял, что проверка условия «закрашенности» спрайтами должна производиться в начале. Тогда это повлияет на производительность. Привет, оптимизация.
Вторая проблема — это проверка спрайтов для каждого пикселя экрана. Предположим, что на уровне находится 40 спрайтов. При размере экрана 96*64 = 6144 пикселей количество итераций цикла достигает 6144 * 40 = 245760. Если пойти другим путём и проверять спрайты не для каждого пикселя, а по условию нахождения в поле зрения камеры, то количество итераций не превысит 40*16*16 = 10240. Эта проблема решается быстро.
Создание игрока и рендеринг изображения
Для того, чтобы поместить спрайт игрока в рендер-массив нужно посчитать смещение спрайта относительно координат камеры.
Аргумент imagePose будет использоваться для имитации поворота игрока при движении.
В конце-концов рендер-массив готов, поэтому вызываем последнюю процедуру, которая рисует сформированный кадр на игровом поле. Ее я показывать не буду, потому что она очень сырая и будет дорабатываться.
Будут дорабатываться и текущие процедуры, потому что на данный момент быстродействие немного хромает и реализовать упомянутую циклическую модель в данный момент проблематично.
Вот, что получается в итоге:
На этом можно пока закончить. Надеюсь, что из ваших глаз не пошла кровь от «лучшего в мире» языка программирования и попыток написать что-то осмысленное.
В любом случае жду критику и советы. Всегда интересно послушать, что скажут люди.
Немного саморекламы
Я создал паблик ВКонтакте, куда буду выкладывать свои мысли, алгоритмы, код, ссылки на эти статьи и конечно мемасики:) Если вам интересно наблюдать за разработкой игр и разных странных вещей, добро пожаловать.