Размер шрифта:
TowerDefence #2. Карта проходимости
Play

TowerDefence #2. Карта проходимости

В прошлом уроке мы определились с базовой структурой нашей игры и создали небольшую заготовку. Следующий наш этап — это определиться с базовой структурой мира и заложить фундамент.

Но прежде немного вернемся к предыдущему уроку и вспомним домашнее задание. Я, конечно, рассчитывал на то, что домашнее задание достаточно простое и его не придется отдельно разбирать. Но проблема возникла там, где её никто не ждал. Согласно не проверенным пока (мною) данным в новом Flash CS5 появился новый рутовый не документированный класс World. �? из-за него пользователям этой версии Flash не удалось создать World.as. Чтобы избежать этой проблемы класс World.as мы переименуем в Universe.as.

Возвращаемся к теме урока. Определение структуры игрового мира — это наиболее важный момент в разработке игры, от которого многое зависит. Честно признаюсь, до этого я еще не написал ни одного TowerDefence, поэтому сейчас мне сложно однозначно определить какая структура будет наиболее подходящей и удобной. Так что придется немного импровизировать и возможно, по ходу дальнейших уроков, вносить какие-либо изменения в игру. Но, я думаю, что даже если это и случится, то это будет интересной демонстрацией того, как находить свои недочеты и своевременно их исправлять :)

�?гровой мир

Любой игровой мир делится на несколько частей:

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

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

Динамические объекты — это объекты, которые принимают участие в игровом процессе и могут влиять на ход его развития. В нашем случае динамические объекты будут разделены на два типа: постройки игрока и движущиеся боевые единицы врагов.

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

Немного о графике

Прежде, чем приступить к программированию игрового мира, нам нужно немного забежать вперед и решить какие масштабы и какой вид графики будут в игре. Тут я хочу немного разочаровать всех тех, кто возможно ожидал от этого урока не только программные решения, но и примеров создания графики. Это все достаточно большая и трудоемкая работа, поэтому я решил сделать основной упор на написание кода игры, а графика будет стилизованная и простая. Таким образом я выбрал оптимальный для меня вид сверху. Размер одной ячейки будет иметь 32x32 пикселя, а общий размер карты сделаем 20x15 клеток, что как раз будет соответствовать размеру игрового экрана. Если вам показались такие размеры не очень комфортными, то не переживайте об этом. Размеры карты мы сделаем масштабируемыми и при необходимости можем добавить прокрутку.

Карта проходимости

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

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

  • СВОБОДНА (FREE) — по такой ячейке сможет перемещаться враг, и игрок сможет строить свои постройки;
  • ТОЛЬКО_ПОСТРОЙК�? (BUILD_ONLY) — в такой ячейке игрок может строить башни, но враг не может проникать в эти ячейки, даже если ячейка не занята башней игрока;
  • ЗАНЯТА (BUSY) — в такой ячейке нельзя строить постройки, и враг не может по ним перемещаться.

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

Что ж, от теории к делу, реализуем карту проходимости.

Программируем кусочками

Я очень люблю поэтапное программирование — это когда большая задача делится на небольшие части и они по очереди реализуются. Такой подход позволяет минимизировать количество ошибок и сократить время на отладку кода. Суть поэтапного написания кода заключается в том, что мы решаем задачу постепенно дописывая важные кусочки кода, и проверяем результат их работы. То есть, мы не пишем сразу кучу методов и классов, которые выполняют разные вычисления для решения одной задачи, а потом запускаем компиляцию приложения и начинаем его дебажить, чтобы все начало работать правильно.

Примечание: Возможно некоторым профи не понравится такой подход программирования. Но для новичков это 100% верный путь. Такой подход упрощает поиск ошибок, потому что программист знает, какие строки только что редактировал и где мог наделать опечаток.

Реализация карты проходимости у нас будет состоять из следующих задач:

  • Задача 1 — игроку не обязательно видеть сетку карты и тем более карту проходимости, но нам на этапе разработки обязательно все это нужно будет видеть. Поэтому первым делом мы сделаем сетку карты.
  • Задача 2 — реализовать карту проходимости.
  • Задача 3 — связать карту проходимости с отладочной сеткой так, чтобы можно было видеть состояние ячеек карты проходимости на сетке.
  • Задача 4 — оптимизировать то безобразие, которое у нас получится в итоге :)

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

Задача 1

Прежде, чем начнем писать код, подготовим графику для отображения сетки. Создаем новую папку в библиотеке нашего fla проекта с названием «Debug» и в папке этой создаем новый MovieClip с названием DebugCell_mc. В свойствах клипа ставим галочку экспорта в ActionScript и открываем его для редактирования. В этом клипе у нас будет 2 кадра, в каждом из которых будет содержаться визуальное состояние для свободной и занятой ячейки. Для этого в первом кадре рисуем зеленый квадратик размером 32x32 пикселя, а во втором кадре красный квадратик тоже 32x32. Для этих квадратиков желательно сделать цвет обводки контрастным цветом (например черным или белым). Сохраняем результат.

�?нформация: Поскольку названия экспортируемых клипов в ActionScript могут совпадать с именами рабочих классов приложения, то в целях избежания этой проблемы всем экспортируемым клипам из библиотеки к их имени я добавляю постфикс. В данном случае это «_mc». При создании экземпляра клипа в коде, этот постфикс дает нам понять, что мы работаем не классом DebugCell, а именно с клипом из библиотеки. Тут, возможно, существуют какие-то стандарты использования постфиксов и префиксов, но я с ними не сталкивался, поэтому вот так.

Теперь приступаем непосредственно к коду. Первым делом добавим три статические константы в Universe.as которые будут у нас отвечать за максимальный размер карты в ячейках и за размер одной ячейки:

Пишем метод makeDebugGrid(), который будет создавать отладочную сетку.

Добавляем вызов метода makeDebugGrid() в конструктор класса Universe.as и компилируем нашу игру. Если все сделано верно, то перед вами должен получится зеленый экран в клеточку. Если экран зеленый и нет клеточек, значит вы забыли сделать обводку для квадратиков в клипе DebugCell_mc. Так же не забудьте добавить import flash.display.MovieClip; в классе Universe.as, чтобы Flash не ругался на якобы неизвестный ему MovieClip.

Задача 2

Теперь сделаем карту проходимости. В текущей версии AS3 нет родной поддержки многомерных массивов, но вкладывая один массив в другой мы сможем получить нужный нам результат. Для этого напишем небольшой метод создания чистой карты проходимости. Но вначале создадим еще две глобальные статические константы, которые у нас будут отвечать за состояние клеток в карте проходимости. Значения констант будут соответствовать номеру кадра в клипе DebugCell_mc:

Добавим локальную переменную _mapMask:Array — это будет наша карта проходимости. �? напишем метод clearMapMask(), который будет создавать новую карту проходимости:

Добавляем вызов метода clearMapMask() в конструктор класса Universe.as обязательно перед вызовом метода makeDebugGrid(). Тестируем нашу игру. Если сделано все правильно, то не должны возникнуть ошибки и мы должны увидеть результат предыдущей задачи, так как в данном шаге мы не сделали никаких визуальных изменений.

Задача 3

Теперь настало время сделать отображение реального состояния ячеек в карте проходимости. Для этого нам понадобятся два публичных метода для работы с картой проходимости. Первый метод getCellState() будет возвращать состояние указанной ячейки, а второй setCellState() будет устанавливать новое состояние для указанной ячейки.

Примечание: обращаясь к двумерному массиву, помните, что первым параметром у нас указывается номер строки, а вторым номер ячейки.

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

Теперь пора сделать реальное отражение состояния карты проходимости. Вернемся к методу makeDebugGrid(), найдем в нем строку cellSprite.stop() и заменим её на cellSprite.gotoAndStop(getCellState(ax, ay)); Компилируем игру и в случае успеха окно игры должно выглядеть так же, как и после первой задачи без каких-либо ошибок и изменений. А все потому, что на нашей карте проходимости нет ни одного препятствия! :)

Чтобы установить препятствие, используем теперь метод setCellState() и изменим конструктор класса Universe.as следующим образом:

Примечание: Помните, что координаты ячеек у нас всегда начинаются с 0 и заканчиваются по ширине 24, по высоте 14 (всегда на 1 меньше чем указано в константах). Поэтому чтобы установить препятствие в первую ячейку нам нужно передать нулевые координаты: setCellState(0, 0, STATE_CELL_BUSY);

Компилируем игру и в результате мы должны увидеть два красных квадратика. Правда все просто?! Но не спешите радоваться.

Задача 4

Профи, написавшие хотя бы одну игру, уже должны были заметить, что способ, которым я решил так наглядно сделать вывод игровой сетки и карты проходимости, хоть и удобен с точки зрения дизайна, но крайне не производителен! Все дело в том, что для отображения каждой ячейки мы создаем отдельный экземпляр MovieClip'a, а каждый экземпляр кушает производительность и ресурсы нашей игры. Я сейчас не буду вдаваться в технические подробности, но на деле, чем меньше визуальных объектов вроде MovieClip на сцене, тем быстрее будет работать наша игра. А в данном примере на одну только сетку 20 * 15 = 300 клипов используется, что является полным расточительством драгоценных ресурсов! Этот недочет нельзя оставлять на потом (до того как игра начнет тормозить), и обязательно нужно сделать небольшую оптимизацию здесь и сейчас.

Чтобы сделать оптимизацию, нам нужно все это множество клипов объединить в один клип, а еще лучше сделать сетку растровой картинкой. Для этого заведем локальную переменную _debugGrid:Bitmap; и импортируем базовые классы:

Далее перепишем метод makeDebugGrid() следующим образом:

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

�?нформация: Я знаю, что опытные профи предпочли бы нарисовать сетку ручками без использования клипов вообще, но я хотел сделать все как можно нагляднее. �? вообще позже эта отладочная сетка может стать хорошим инструментом, чтобы подсказывать игроку, где можно строить башни и где нельзя. А использование клипов для создания сетки позволит легко изменять её внешний вид.

Домашнее задание

Если вы помните, то в начале урока речь шла о трех состояниях ячеек: свободная (FREE), занятая (BUSY) и только для постройки башен (BUILD_ONLY). Сегодняшнее домашнее задание будет заключаться в самостоятельном добавлении третьего состояния ячейки в игровой мир. Это достаточно простая задача. Добавьте новую публичную константу, новый кадр в DebugCell_mc ну и пробуйте установить данное состояние ячейки соответствующим методом в конструкторе мира.

Заключение

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

Вопросы относительно уроков вы можете задавать мне на почту. Контактный e-mail есть на странице «Об авторе».

📎📎📎📎📎📎📎📎📎📎