Создание змейки в excel

Обновлено: 04.07.2024

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

Теперь этого можно будет избежать, ведь в разработке находится пошаговая 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 возвращает индекс цвета пикселя из массива тайлов.

'Возвращает координату текстуры Function getTexOffset(dCoordinate As Double) As Integer getTexOffset = (dCoordinate - 1) Mod main.blockSize End Function

Почему сперва происходит проверка спрайтов, а затем тайлов?

Все просто. Программа просто не трогает те «пиксели», которые уже закрашены спрайтами, что влияет на производительность в лучшую строну.

P.S. На следующий день я понял, что проверка условия «закрашенности» спрайтами должна производиться в начале. Тогда это повлияет на производительность. Привет, оптимизация.

Вторая проблема — это проверка спрайтов для каждого пикселя экрана. Предположим, что на уровне находится 40 спрайтов. При размере экрана 96*64 = 6144 пикселей количество итераций цикла достигает 6144 * 40 = 245760. Если пойти другим путём и проверять спрайты не для каждого пикселя, а по условию нахождения в поле зрения камеры, то количество итераций не превысит 40*16*16 = 10240. Эта проблема решается быстро.

Для того, чтобы поместить спрайт игрока в рендер-массив нужно посчитать смещение спрайта относительно координат камеры.

Sub renderPlayer(imagePose As Integer) Dim offsetX As Double, offsetY As Double, cntRow As Integer, _ cntCol As Integer 'Считаем смещение относительно координат камеры offsetX = main.playerX - main.cameraX offsetY = main.playerY - main.cameraY 'Если игрок не находится за пределами камеры If offsetX >= 0 And offsetY >= 0 And _ offsetX < main.cameraX + main.screenW And _ offsetY < main.cameraY + main.screenH Then For cntRow = 0 To main.blockSize - 1 For cntCol = 0 To main.blockSize - 1 'Если пиксель текстуры заполнен If main.arrPlayerSpr((cntRow + 1) + (imagePose * main.blockSize), cntCol + 1) <> 0 Then 'Если пиксель не заходит за пределы камеры If cntRow + main.playerY <= main.screenH + main.cameraY And cntCol + main.playerX <= main.screenW + main.cameraX Then arrRender(offsetY + cntRow, offsetX + cntCol) = main.arrPlayerSpr((cntRow + 1) + (imagePose * main.blockSize), cntCol + 1) End If End If Next Next End If End Sub

Аргумент imagePose будет использоваться для имитации поворота игрока при движении.

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

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

Вот, что получается в итоге:

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

В любом случае жду критику и советы. Всегда интересно послушать, что скажут люди.

Я создал паблик ВКонтакте, куда буду выкладывать свои мысли, алгоритмы, код, ссылки на эти статьи и конечно мемасики:) Если вам интересно наблюдать за разработкой игр и разных странных вещей, добро пожаловать.

Доброго дня дорогие друзья, сегодня мы продолжаем поддержку Народного aджема материалом и сегодня поговорим о том, как создать змейку.

Для тех, кто ещё не видел, напомним. На DTF сейчас проходит Народный Джем и наша школа программирования ЯЮниор учредила две номинации:

25 000 рублей за лучшую игру на Unity
25 000 рублей за лучший код на Unity

Подробнее про джем:

Напомним, что данная серия статей нацелена на совсем начинающих!

В процессе создания змейки нам потребуется графика, для этого мы возьмем бесплатный LowPoly пакет из Asset Store.

Elevate your workflow with the LowPoly Environment Pack asset from k0rveen. Find this & other…

Начнем мы с построения уровня.
Для начала нужно создать саму землю, для этого нажимаем ПКМ по окну иерархии и выбираем 3D Object -> Plane, после выполнения этого действия у вас появится плоский объект, который будет играть роль земли.

После этого земля стала нужного размера, но её вид оставляет желать лучшего, нужно придать ей зеленоватый цвет. Для этого в окне Project мы создаем в Asset папку Material, переходим в неё, нажимаем ПКМ и выбираем Create -> Material, после чего появляется новый материал, который мы называем Ground.

После создания материала его следует настроить, для этого нажимаем ЛКМ на материал и в поле Albedo редактируем цвет на нужный.

Далее нужно материал просто перетащить на наш Plane, после чего он станет зеленым.

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

Первым делом сделаем загон для нашей змейки, для этого в environment создаем объект куб и устанавливаем координаты в 0, если это не так.

Далее нажатием на куб в отмеченной части переходим в ортографический режим для упрощения редактирования.

И теперь при помощи переключения между осями приводим сцену к следующему виду:

Далее отдаляем камеру и устанавливаем Scale куба по X на 40
Важно! Проверьте, что куб в нулевых координатах.

Далее с выделенной стенкой нажимаем комбинацию клавиш CTRL+D(Создание копии объекта) и с зажатой клавишой CTRL передвигаем новую стенку на 40 юнитов вверх, по оси Z (или можете просто установить значение в инспекторе).

2 стены готовы, остались ещё 2.
Выделяем обе стены одновременно и снова нажимаем CTRL+D, далее выбираем инструмент для вращения и устанавливаем режим редактирования Center, чтобы поворачивать наши стены относительно центра.

Далее зажимаем Ctrl, это позволит нам вращать наши стены на строго заданный угол. Теперь у нас готова коробка.

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

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

У меня получилась следующая картина:

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

Открываем отладчик кадров при помощи Window -> Analysis -> Frame Debugger и запускаем нашу игру. Что мы видим?
Мы видим, что на отрисовку у нас тратится 606 Draw call.

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

Как мы помним, мы собрали все эти объекты в пустой объект environment.
Теперь мы выделяем его и ставим галочку Static, после чего соглашаясь сделать все дочерние объекты статическими тоже.

После этого снова запускаем игру и видим, что стало всего 40 Draw call, что гораздо меньше.

Создаем простой 3D объект куб, который будет играть роль змеи.
При желании можете создать новый материал для змеи и присвоить к ней.

Как вообще строится змейка и как она двигается?

Змейка по сути своей состоит из головы и хвоста, который за ней тянется.
А её движение - это история её перемещения, т.е. с каждым ходом блоки смещаются вперед на позицию своего выше стоящего звена.

Создаем папку для скриптов и в ней скрипт Snake

Давайте разберемся, как работает наш скрипт.В Update у нас вызываются методы для поворота и движения, начнем разбор с поворота.

Метод Rotate принимает в себя скорость поворота, а после считывает, нажата ли клавиша A или D, для которых свойственен поворот(это могут быть также стрелочки или другие клавиши, если вы изменяли настройки Axis), умножается на скорость и на время прошедшее с прошлого кадра.

Зачем это домножение? У каждого игрока количество кадров разное, у кого-то 100, у кого-то 200, у кого-то 30, а это значит, что у одного Update выполнится за секунду 30 раз, а у другого 200 и змейка сдвинется на разное расстояние, будет двигаться с разной скоростью(Такое можно заметить в Half-Life, когда спидраннеры изменяют FPS для разгонов)

Домножая на Time.deltaTime мы нормализуем скорость для всех, ведь это время прошедшее с прошлого вызова Update.

Движение головы не сложное, мы просто двигаем голову вперед с учетом скорости.

А вот движение хвоста интересное, его стоит разобрать подробнее.

Обратите внимание на проверку в строке 32. Если между текущей костью и следующей костью дистанция больше, чем дистанция которая должна быть между костьми, то тогда мы подтягивает хвосты поближе. Получается, что мы постоянно меняем их местами, двигая все ближе и ближе. Если же дистанции не достигли, то мы и для других костей не достигли и можно прервать обработку.

Также нам нужна еда. Для еды попрошу создать скрипт и удалить из него методы Start и Update, чтобы он оказался пустым. Еда не выполняет ничего, скрипт нам нужен только для того, чтобы мы могли определить, что столкнулись именно с едой.

Создадим еду, создайте куб на сцене, добавьте на него красный материал, компонент Food который создали. Также на нашем кубе еды есть компонент Box Collider, в нем есть поле Is Trigger, поставьте туда галочку, после чего разместите еду по всей карте.

Чтобы мы могли сталкиваться с едой, нам нужно немного модернизировать голову. На голове уже есть колайдер, но чтобы мы могли взаимодействовать с едой - триггером нам нужно иметь компонент RIgidbody на голове. После добавления Rigidbody снимите галочку с Use Gravity и поставьте галочку напротив Is Kinematic.

Далее возвращаемся в скрипт и дописываем код поедания.

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

В целом у нас получилось создать простейшую змейку, которая может ползать, собирать еду и разростаться.

Также можете посмотреть этот курс на нашей платформе в другом формате и с созданием менюшки, добавлением звуков!

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

Моя змея ест мыши :

нажмите, чтобы посмотреть youtube

 Геймплей

Вы можете заставить его играть из github и, надеюсь, он будет работать

Чтобы быть понятным, он полностью функциональный - просто нажмите кнопку «начать» или запустите макрос «DrawGameBoard», чтобы начать и использовать клавиши со стрелками для навигации.

К сожалению, эта змея работает на событии Worksheet_Selection_Change . Поскольку мне нужно сохранять значения после выхода из процедуры, я не мог понять, как реализовать Class .

Некоторые другие вещи, о которых я должен упомянуть, с которыми я боролся -

  1. Зная, где конец змеи, чтобы стереть его.
  2. Растущая змея, когда она ест мышь.
  3. Сроки, конечно. Я думаю, что мог использовать библиотеку?
  4. Незаконные действия - вы не можете повернуть назад
  5. Форматирование - оно подходит для моего представления, но это довольно локально.
  6. Сохранение пути змеи и текущего местоположения для извлечения после перемещения, что означает, что существует множество названных диапазонов.
  7. VBA не предназначен для этого

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

Я предполагаю, что многое можно улучшить, и я не ожидаю, что кто-нибудь справится со всем этим.

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

Листовой модуль

Константы

Создать панель

Таймер и автоматическое перемещение

Мясо его

1 ответ

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

Вот как я справился с некоторыми вещами, с которыми вы боролись.

изменение направления . Для изменения направления я использовал API GetAsyncKeyState

И в методе изменяется одно из четырех направлений.

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

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

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


Прошло уже больше двух месяцев с момента моей демобилизации. Я уже освоился на свободе, пришло время рассказать правду. Служил я в разведке. «Секретная» в/ч ОсНаз ГРУ. По распределению попал в отдел ЦРП (Центр радиоперехвата). Работенка не пыльная, сидишь ночами в наушниках и слушаешь врага. Но речь пойдет не об этом.

У каждого оператора поста радиоперехвата (таковыми мы числились) был в распоряжении компьютер с подключенными к нему РПУ (радио-приемными устройствами). На компьютере стоял спецсофт + Excel. Все остальное было заблочено. После N-ного дежурства я стал скучать… Зачесались руки.

Вспомнил, что когда-то имел дело с VBA. Никогда дальше написания простейшей функции не заходил. Решил посмотреть, что может получится при более детальном изучении. После ночи мучений я родил черепаху змею! Классическая змейка, реализованная путем закраски нужных ячеек. Была возможность создавать себе препятствия, опять же, просто закрашивая нужные ячейки.


После змейки было еще парочка небольших игрушек, но все это мелочи…


Сослуживцы ходили и клянчили, чтобы я им все это скинул, всем надоело играть в косынку. Способов сделать это я не знал. Порты USB отключены аппаратно. Но, как выяснилось, все компьютеры были объединены в локальную сеть. Запустив через Excel командную строку, я смог зайти на соседний компьютер и скинуть туда необходимые файлы. И тут меня осенило… Excel, VBA, чтение/запись файлов, локальная сеть… Смекаете?

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


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


Респект этим ребятам. До сего момента я не видел возможность использования форм на VBA. Не заметил сразу, да и не задумался над этим. Этой же ночью было решено переписать морской бой на формах. Код получился компактней, интерфейс дружелюбней, работоспособность выше.


Работа в отделе встала! Морской бой любят все… кроме меня. Ну не нравится мне эти глупые перестрелки. В общем, на нем я не остановился. Но, забегая вперед, скажу, что морской бой был самым популярным.

Bomber Man
Кто-то в шутку сказал «а слабо написать Bomber Man?».
Вызов принят! Месяц неторопливых мучений и в свет вышел ОН.



Миниатюрки отрисованы в Paint. Игра рассчитана на одновременное подключение 4-х человек с разных компьютеров. Приложение клиент-серверное. Сервер был запущен в отдельном приложении. Количество жизней не ограничено (я думаю, по количеству надгробий это понятно).
Но все не так гладко, как кажется. При записи/чтении файлов возникали ошибки из-за одновременного обращения и добиться более 10 кадров в секунду мне не удалось. Если игра шла на двух человек, то все укладывалось в 25-30 кадров, но на четырех добиться хороших скоростей мне не удалось. Отсутствие интернета не дало возможности найти решение.

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

Texas Holdem Poker
Я очень люблю покер. В армии мне его сильно не хватало. Реализацию покера на 9 человек я видел не такой уж и сложной. Разве что над алгоритмами поиска собранной комбинации пришлось поломать голову, но это было интересно. Отсутствие интернета заставило поднапрячься. Одну ночь я потратил на отрисовку колоды карт. Другую на стол и фишки. А дальше начался процесс написания. Покер у меня получился быстрее Bomber Man. В одну из ночей мы оттестировали его на максимальное количество игроков, тесты прошли вполне успешно.


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

Вообще говоря, это было очень опасное и кропотливое дело, не дай бог кто-то спалит за таким занятием, да еще на боевом дежурстве. Кто служил, тот поймет. За обычную косынку снимали с дежурств и ставили в наряды, а тут такое… Спасибо одному хорошему человеку за то, что скинул себе на флешку все эти скриншоты и переслал мне на почту. Оставлю себе на память.

Читайте также: