Как сделать изометрическую игру

Создание изометрических миров: Руководство для разработчиков игр

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

Хотите найти еще больше статей на тему создания изометрических миров? Обратите внимание на следующие статьи, Создание изометрических миров: Руководство для разработчиков игр, её продолжение и Книга Juwal, Основы разработки игр.

1. Изометрический мир

Как сделать изометрическую игру

Diablo 2 Как сделать изометрическую игру

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

2. Плиточные игры

Как сделать изометрическую игру

Несколько простых плиток

Эти плитки одинакового размера, квадратной формы, поэтому высота и ширина у плиток одинаковы.

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

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

Как сделать изометрическую игру

Простой уровень, в представлении вида сверху.

Мы можем усложнить это добавив плитки углы и отдельные вертикальные и горизонтальные плитки стен, это потребует пять дополнительных видов плиток:

Как сделать изометрическую игру

Усложнённый уровень с номерами плиток

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

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

3. Изометрическая проекция

Лучшее техническое объяснение того, что означает «изометрическая проекция», насколько мне известно, размещено в статье Clint Bellanger:

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

Как сделать изометрическую игру

Декартова сетка против Изометрической.

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

Размещение изометрических плиток

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

Эти функции показывают как вы можете их конвертировать из одной системы в другую:

Псевдокод для цикла выглядит следующим образом:

Как сделать изометрическую игру

Наши стены пастбища в изометрической проекции.

В качестве примера давайте посмотрим, как обычная 2D позиция преобразуется в изометрическую:

Вышеуказанный метод позволяет нам создать соответствие между 2D уровнем данных и изометрическими координатами. Мы можем найти координаты плиток из данных декартовых координат с помощью следующей функции:

(Предполагается, что ширина высота и плитка плитка равны, как и в большинстве подобных случаев).

Следовательно из пары координат экрана (изометрических), мы можем найти координаты плитки путем выполнения команды:

Точкой экрана может быть, например, положение клика мышкой или какая-либо другая позиция.

Совет: Другим способом размещения является модель Зигзага, которая использует совсем другой подход.

Перемещение в изометрических координатах

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

Создание глубины

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

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

Как сделать изометрическую игру

Большое изображение делится на несколько фрагментов стандартного изометрического измерения

4. Создание рисунков

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

При создании изометрического рисунка, общие правила таковы

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

5. Изометрические персонажи

Как сделать изометрическую игру

8-и сторонний способ передвижения в изометрической проекции.

Как сделать изометрическую игру

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

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

Мы можем легко вычислить новые изометрические координаты, как мы уже обсуждали:

Обнаружение взаимодействий

Обнаружение взаимодействий осуществляется проверкой того, является ли новая позиция плитки возможной или нет. Когда мы вычисляем новую позицию, мы не сразу перемещаем туда персонажа, а сначала проверяем, какие плитки занимают это пространство.

Глубина для персонажей

Рассмотрим персонаж и плитку дерево для изометрического мира.

Когда они имеют одинаковые x координаты, то мы решаем, основываясь на единственном y координате: у кого он больше, тот и перекроет другого. Когда они имеют одинаковые y координаты, то мы решаем, основываясь на единственном x координате: у кого он больше, тот и перекроет другого.

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

6. Вперёд!

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

Нажмите, чтобы SWF был в фокусе, а затем используйте клавиши со стрелками. Нажмите здесь чтобы получить полную версию.

Это класс может быть полезным (я написал его на AS3, но вы должны быть в состоянии понять на любом другом языке программирования):

Если вы запутались, вот код моего демо (в виде кода Flash и AS3):

Точки соприкосновений

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

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

Обнаружение взамодействий

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

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

Хитрости

В демоверсии я просто перерисовываю каждый кадр сцены на основе новой позиции героя. Мы находим плитку, которую герой занимает и рисуем героя поверх плитки земли, когда цикл рендеринга достигает этих плит.

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

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

Заключение

К настоящему времени у вас должна быть отличная основа для создания собственных изометрических игр: вы можете отображать мир и объекты в нем, представлять данные уровня в простых 2D-массивах, преобразовывать между декартовыми и изометрическими координатами и разбираться с такими понятиями, как создание глубины и анимация персонажей. Наслаждайтесь созданием изометрических миров!

Источник

Изометрический плагин для Unity3D

Как сделать изометрическую игру

Сказ о том, как написать плагин для Unity Asset Store, поломать голову над решением известных проблем изометрии в играх, да еще и немного денег на кофе с этого поиметь, а так же понять на сколько Unity имеет расширяемый редактор. Картинки, реализации, графики и мысли под катом.

Завязка

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

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

Постановка целей

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

Сортировка

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

Как сделать изометрическую игру
На скриншоте сначала рисуется зеленый спрайт (2,1), потом сверху него синий (1,1)

Как сделать изометрическую игру
На скриншоте показана неверная сортировка, когда сначала рисуется синий

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

Как сделать изометрическую игру
На скриншоте правильно отсортированные вытянутые объекты 3×1 и 1×3 с «заборами» размерами 3×0 и 0x3

И тут начинаются проблемы и начинается место где нужно принимать решение по какому пути идти:

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

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

Как сделать изометрическую игру
На скриншоте у синего куба зависимость от красного

Как сделать изометрическую игру
На скриншоте у зеленого куба зависимость от синего

Псевдокод для определения зависимости по двум осям (с осью Z аналогично):

С помощью такого подхода строим зависимости между объектами и рекурсивно проходимся по всем объектам и их зависимостям выставляя дисплейную Z координату. Подход довольно универсальный, а главное работает. Подробнее описание этого алгоритма можно почитать, например, тут и тут. А еще этот подход используется в популярной flash-библиотеке для изометрии (as3isolib).

Всё бы хорошо, да вот алгоритмическая сложность данного подхода O(N^2), так как для построения зависимостей нужно сравнить каждый объект с каждым. Но оптимизации были отложены на более поздние версии, разве что я сделал ленивую пересортировку, что бы ничего не сортировалось, когда ничего не двигается. Об оптимизациях позже.

Расширение редактора

Цели были поставлены следующие:

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

Как сделать изометрическую игру
На скриншоте мои gizmos для перемещения объектов в изометрическом мире

Выпуск

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

Мучительные 5 дней (примерно столько же я потратил на написание первой версии, но я знал что делал, без особых поисков и дум, что дало мне бОльшую скорость по сравнению с людьми, которые только начинают копать в сторону изометрии) и пришло письмо от Unity, что плагин одобрен и можете идти любоваться им в сторе и смотреть нулевые (пока) продажи. Отметился на местном форуме, встроил Google Analytics на страницу в сторе и стал ждать с моря погоды.

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

Оптимизации

Основные претензии были к двум вещам:

Алгоритм

При 100 объектах и O(N^2) было 10’000 проверок, что бы только найти зависимости, а еще нужно по всем ним пройтись и дисплейный Z выставить для сортировки. Нужно было что-то решать. В конечном итоге я перепробовал огромную кучу вариантов, не мог спать мучаясь над решениями. Не буду описывать все опробованные механизмы, опишу лучше на чем остановился в данный момент.

Во-первых, естественно, сортируем только видимое. Это значит, что нужно постоянно знать кто в кадре, при появлении в кадре добавлять объект в сортировку, при уходе — забывать. Юнити не даёт нам возможность узнать Bounding Box объекта вместе с его детьми в дереве сцены, пробегать по всем детям (причем каждый раз, потому что они могут добавляться и удаляться) не вариант — медленно. События OnBecameVisible и иже с ними тоже нельзя, потому что работают только для родительского объекта. Зато есть возможность получить все Renderer компоненты из нужного объекта и его детей. Вариант этот не блещет красотой, но такого же универсального способа с приемлемой производительностью я не нашел.

Тут есть тонкость, что используется функция GetComponentsInChildren, с помощью которой можно получить компоненты без аллокаций в нужный буффер, в отличии от той, что возвращает новый массив с компонентами

Во-вторых, нужно всё равно что-то делать с O(N^2). Я пробовал многие варианты разбития пространства, но остановился на простой двухмерной сетке в дисплейном пространстве, куда я проецирую свои изометрические объекты, в каждом такой секторе содержится список изометрических объектов, которые его пересекают. Идея простая: если проекции объектов не пересекаются, то и зависимости между объектами строить смысла нет. Далее бежим по всем видимым объектам и строим зависимости только в нужных секторах, тем самым понижая алгоритмическую сложность и увеличивая драгоценное быстродействие. Размер сектора выбираю как среднее между размерами всех объектов, результат меня устроил.

Общая производительность

Тут можно и отдельную статью написать конечно… Если коротко, то кэшируем компоненты (GetComponent их ищет и это не быстро), осторожнее со всем что касается Update, всегда нужно понимать, что это делается каждый кадр и нужно быть осторожнее. Помним про всякие интересные особенности типа кастомного сравнения с null. И прочее и прочее, в конце концов об этом всём узнаёшь во встроенном профайлере и начинаешь решать и помнить.

Также там узнаёшь о боли сборщика мусора. Нужна производительность? Забудьте о всём что может выделять память, а в C# (а особенно в местном старом Mono) это может делать всё, начиная от foreach(!) заканчивая создающимися лямбдами, а уж какой-нибудь LINQ противопоказан даже в самых простых случаях. В конечном итоге из красивых синтаксических конструкций и прочего сахара C# превращается в этакий Си со смешными возможностями.

Приведу полезные ссылки по теме:
Часть1, Часть2, Часть3

Результаты

Я никогда не видел, чтобы так улучшали этот алгоритм, поэтому особенно был рад результатам. Если в первых версиях буквально на 50 двигающихся объектах игра превращалась в слайд-шоу, то сейчас на 800 объектов в кадре — всё крутится на запредельных скоростях, пересортировываясь всего за 3-6мс, что для такого количества объектов и изометрии оооочень хорошо. Плюс после инициализации почти не выделяя в кадре памяти вообще.

Дополнительные возможности

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

Микс 2D и 3D

Смешивание 2d и 3d в изометрических играх интересная возможность, которая позволяет минимизировать отрисовку разных вариантов движения и поворотов (например 3д модели персонажей с анимациями). Делается не сложно, но нужно встроить это всё в систему сортировки. Всего лишь нужен Bounding Box модели со всеми детьми и сдвигать модель по дисплейному Z на его ширину.

Так можно вычислить Bounding Box модели со всеми её детьми

Как сделать изометрическую игру
А вот так всё получается в конечном итоге

Кастомные настройки изометрии

Тут всё относительно просто, просили сделать возможность задавать угол изометрии, аспект, высоту тайлов. Помучившись немного с математикой можно получить такое:

Как сделать изометрическую игру

Физика

А вот здесь интереснее. Так как изометрия симулирует 3д мир, то и физика должна быть трёхмерной, с высотой и прочим. Был придумал интересный трюк. Я дублирую все компоненты физики, такие как Rigidbody, Collider и прочее для изометрического мира. По этим описаниям и настройкам повторяю невидимый физический трёхмерный мир средствами самого движка и встроенного в него PhysX‘а. Далее беру вычисленные данные симуляции и возвращаю в те дублирующие компоненты для изометрического мира. Таким же образом симулирую события столкновений и триггеров.

Как сделать изометрическую игру
Гифка физической демки тулсета

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *