Архитектор BPM решений
0,0
рейтинг
27 августа 2014 в 11:54

Разработка → Архитектура простой 2D игры на Unity3D. План, факт и работа над ошибками

Недавно команда Whistling Kite Framework выпустила в релиз очередную игру, на этот раз — Змейку, написанную на Unity3D. Как и в большинстве игровых проектов, при решении вопроса о том, насколько детально нужно проектировать приложение, критическим фактором было время. В нашем случае причина проста: т.к. разработка велась в свободное от основной работы время, то идеальный подход к проектированию отложил бы релиз ещё на год. Поэтому, составив первоначальное разделение на модули, мы закончили проектирование и приступили к разработке. Под катом описание того, что из этого получилось, а также пара уроков, которые я вынес для себя.


Осторожно, картинки!

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

Функциональность


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

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

  • 4 кнопки — задать новое направление движения;
  • 2 кнопки — поворот налево/направо от текущего направления;
  • свайпы — провести пальцем в нужном направлении.


Планируем добавить еще два:

  • гироскоп — наклон устройства задает новое направление;
  • джойстик-”пузырь” — смещение пальца от первой точки касания задает новое направление.


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

Решение о старте разработки змейки мы принимали, преследуя две цели:

  • Попробовав большую часть существующих под Андроид «змеек», мы не обнаружили такой, в которую хотелось поиграть нескольким участникам команды, мы решили исправить эту ситуацию;
  • Мы впервые решили выйти на мобильный рынок и использовать движок Unity3D, поэтому выбрали заведомо несложную игру, чтобы освоиться с ним.


Архитектура


Этап 1. Концепция

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

  • Слой инициализации;
  • Слой интерфейса;
  • Слой логики;
  • Слой контроллеров;
  • Слой управления.



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

Хорошие идеи:
  • Выделение слоя инициализации: в последующих версиях это потеряли, что приводило иногда к довольно запутанным последовательностями действий.
  • Правильная общая структура разделения приложения на модули: в дальнейшем границы различались и понятие инкапсуляции было практически забыто(можно привести картину «там баг» с котами, только заменить на «так нельзя! Как же инкапсуляция»


Промахи:
  • Раздельные слои контроллеров и управления: впоследствии от контроллеров в терминах MVC отказались в силу избыточности.
  • Слишком высокоуровневое описание: непонятно, какой писать код. Хотя это и нельзя назвать чистым промахом, т.к. на момент создания этой архитектуры не был выбран ещё движок.


Следующим этапом стал выбор платформы, хотя, строго говоря, выбор делался между unity и разработкой на чистой java. Беглый обзор других существующих движков не вызвал энтузиазма в их изучении. Мы пришли к достаточно ожидаемому выводу: написать змейку проще вообще без платформы, к тому же это выглядело интереснее — кто ж не любит делать свои велосипеды? Однако, мы выбрали unity, с целью ознакомления с платформой, близкой к статусу стандарта де-факто в области гейм-дева. Да, мы получили солидный оверхед из-за того, что unity — трёхмерный движок (на момент начала разработки у unity еще не было нативной поддержки 2D), а делали мы двумерную игру, но полученный опыт того стоил.

Этап 2. Проект

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

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

Сущность-связь (понятно, что это скорее относится к разряду требований к системе, чем к её архитектуре, но в контексте статьи я не могу её не упомянуть:



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

Диаграммы компонентов и кооперации. Для их восприятия предварительно опишу ряд договоренностей:

Общая архитектура — носит общий концептуальный характер, являясь, по сути, продолжением диаграммы этапа 1. На ней отображены:
  • Компоненты системы (прямоугольники);
  • Подсистема хранения (символ БД, магнитного диска);
  • Примечания (желтые “листки”);
  • Потоки управления (закрашенные стрелки);
  • Потоки данных (пунктирные стрелки).

Детализирующие диаграммы содержат:
  • Компоненты, объекты (прямоугольники);
  • События, обрабатываемые объектами (шестиугольники);
  • Действия, выполняемые по событиям (эллипсы).




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

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

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

Отдельно выделены компоненты для логирования и расшаривания рекордов.

Игровой мир состоит из объектов, взятых с ERD. Ключевую роль играют партия и змейка.

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

Хорошие идеи:
  • Выделение двух основных независимых подсистем: при разработке это не было учтено, что первоначально привело к большому количеству лишних сцен.


Промахи:
  • Не учитывает разделение по сценам и работу с подсистемой хранения из игровой сцены
  • Не отвечает на вопрос, как реализовывать подсистему GUI и как подсистемы взаимодействуют между собой.
  • Объект Партия взял на себя функции контроллера в терминах MVC.


Этап 3. Фактический результат

После старта разработки архитектуру уже никто не корректировал. Я только оставлял отдельные заметки в соответствующем разделе дизайн-документа о том, как реализованы те или иные узкие места, но основная цель была выпустить релиз. Что ж, этой цели мы достигли, и я сел за рефакторинг, чтобы подготовиться к разработке второго релиза. Уже на тот момент я понимал, что в первую очередь надо переводить игру на честный 2d, поддержка которого как раз появилась в unity. И ещё было понимание, что полученный результат далёк от идеального, в первую очередь в части разделения функций по объектам и их взаимодействию, поэтому, я поставил себе две задачи:

  1. Составить общую диаграмму классов
  2. Составить диаграммы последовательности для основных сценариев приложения.


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

Обзор созданных классов

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



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

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

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

Для создания экземпляров фруктов (FruitInstance) используется фабрика, т.к. в будущем планируется увеличивать количество разных типов фруктов.

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

Пакет с контроллерами содержит управляющие классы. Они добавляются к змейке и взаимодействуют напрямую с объектом Snake, используя паттерн command, а змейка выполняет поступившие команды в той же последовательности, но раздельно по своим шагам. Это было сделано для корректной обработки быстрых последовательных команд, например для разворота на 180 градусов вдоль своего хвоста.

Пакет с провайдерами содержит только один класс, интересный для данной статьи — это dataProvider. Этот класс содержит набор статичных функций, являющихся оберткой над вызовами стандартных методов для работы с хранимыми свойствами. На обоих предыдущих этапах проектирования работа с сохраненными данными предполагалась только однократно: загрузить их в память и больше не обращаться к более медленным носителям. Такой подход не учитывал вопрос независимости сцен в Unity: в результате, при переходе между сценами главного меню и игрового поля, все необходимые данные приходилось перечитывать заново.

Начальные состояния

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



Главное меню создается двумя классами, Player и GUInavigator, присоединенными к единственному объекту на сцене, к камере. Player отвечает за загрузку всей основной информации об игроке, а GUInavigator инициирует нужное поведение из пакета интерфейсов и обеспечивает дальнейшие переходы между интерфейсами.

Игровая сцена содержит гораздо больше объектов. Большинство из них статические, представляющие собой мир змейки: фон, игровое поле, стены. Дополнительные поведения прикрепляются только к двумя объектам: к камере (GUInavigator, Player, party, fruitfabric) и голове змейки (Snake, Controllerselector). Controllerselector отвечает за выбор управляющего контроллера в соответствии с настройками игрока.

Запуск приложения

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



Загрузка приложения состоит из обработки двух событий: awake и start. Событие awake обрабатывает объект player: в этот момент он обращается к DataProvider-у для загрузки информации об игроке, а потом вызывает собственный метод, отвечающий за применение текущих настроек, например, отключение звука.

Событие Start обрабатывается чуть посложнее:
  1. GUInavigator в методе initMain инициирует все необходимые в данной сцене интерфейсы, каждому проставляя признак неактивности.
  2. Далее он проверяет, не было ли сохранено незавершенной игры. Это возможно, например, при прерывании игры входящим звонком.
    1. Если игра есть, то происходит загрузка игровой сцены, а данный сценарий прерывается.
  3. Условно одновременно с этим обрабатываются события Start для всех инициированных классов интерфейсов — в них происходит дополнительная инициализация, свойственная каждому конкретному интерфейсу в отдельности (в т.ч. интерфейс рекордов загружает данные по таблице рекордов, используя dataProvider).
  4. Интерфейсу главного меню присваивается признак активного, и он отображается пользователю.


Запуск игры

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



Первыми срабатывают события Awake для объектов Player (загрузка всех настроек, аналогично старту приложения) и Snake (метод Init() — инициализиция хвоста “по умолчанию” из двух сегментов). Потом срабатывает событие Start для выбора контроллера (для примера использован FourButtonController), загрузки GUI и инициализации игры. Чуть подробнее об обработке этого события:
  1. Controller selector в событии Start, проверяет настройки игрока и подгружает нужный контроллер управления.
  2. GUInavigator в методе initGame инициирует все необходимые в данной сцене интерфейсы, каждому проставляя признак неактивности. Определение, какой метод вызвать, прроисходит на основе параметра, заданного через редактор. Инициализация и включение активного интерфейса происходят аналогично запуску приложения.
  3. Далее событие Start обрабатывает объект party. Первым делом он проверяет, нет ли сохраненной игры.
    1. Если игра есть, то party вызывает dataProvider для восстановления, который, прочитав данные вызывает метод Restorebackup объекта party.
    2. Он, в свою очередь вызывает аналогичный метод объекта Snake, а тот по цепочке для всех звеньев.
    3. После восстановления данных, управление возвращается объекту party, он включает паузу и ждет действий игрока.

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


Игровой цикл: движение змеи

Игровая логика сосредоточена вокруг обработки двух событий: update обрабатывается в змейке методом movehead, здесь происходит движение змеи и управление движением ее сегментов; и ontriggerenter, где происходит обработка столкновений головы змеи с фруктами, стенами и собственным хвостом.



Разберем событие столкновения подробнее:
  1. При возникновении события сначала проверяется, с кем мы столкнулись: если это яблоко, то вызывается метод Eatfruit, а если хвост или стена, то Killme. В обоих случаях информация о столкновении сбрасывается в логи.
  2. Случай яблока:
    1. Змейка выставляет флаг необходимости добавления звена и вызывает метод Eatfruit объекта party.
    2. Party увеличивает рейтинг партии и вызывает свой же метод createfruit, использованный также при запуске игры.
    3. В этом методе сначала удаляется текущий экземпляр фрукта, а затем создается новый через обращение к фабрике.

  3. Случай стены или хвоста:
    1. Змейка передает обработку события объекту party в метод Endgame.
    2. Party выставляет режим паузы и, через навигатор интерфейса, переводит игрока на экран с результатами партии.


Движение же змейки реализовано следующим образом:
  1. В событии update вызывается метод movehead
  2. Здесь проверяется скорость змейки: наступил момент перехода или нет
  3. Если да, то проверяется флаг необходимости добавления звена
    1. Если нужно, то создается звено, аналогичное следующему за головой
    2. Смещаются только голова и одно вновь созданное звено.

  4. Если флага нет, то смещается голова, а за ней по цепочке все звенья, методом adjust.


Заключение

За рамки данной статьи осталось достаточно много логики: это логирование, как внутреннее, так и с использованием внешних средств аналитики; это работа навигатора интерфейсов, который инкапсулирует все особенности, связанные с двумя сценами; это работа с данными, с устройством, показ рекламы и еще много других особенностей. Если кому-то будет интересно, я могу написать об этом отдельно.

Основные выявленные проблемы:

  • Замена 3d на 2d. Из диаграмм это не следует, но очевидно с точки зрения предпочтительности использования инструментов по их назначению.
  • Объект party слишком перегружен функциями: он управляет и яблоками, и змейкой, и обработкой завершения игры.
  • Перегружен игровой интерфейс — его надо разделять на два состояния: в игре и после столкновения.
  • Отсутствует фасад для работы с функциями устройства, не предусмотренными движком: в нашем случае для андроида это переопределенный метод вибрации, реализация кнопки share (вызов системного окна для выбора приложения) и реализация вызовов гугл- аналитики.


Какие выводы для будущих проектов я сделал, исходя из всего вышесказанного?
  1. Надо знать платформу, прежде чем браться за что-то серьезное. Это в общем -то очевидно, но на всякий случай решил это повторить, может кого-то убережет от запуска очередного убийцы топ ММО без опыта.
  2. Необходимо проектировать разделение приложения (даже простого) на компоненты. На этом этапе главное — минимизировать количество взаимодействий и потоков данных.
  3. Продумывая структуру классов, нужно сразу продумывать, как объекты будут соотносится с компонентами и какие функции (группы функций) будут они выполнять.
  4. С одной стороны, нужно поддерживать связь с запланированной архитектурой, но с другой стороны, нужно быть готовым ее изменять, при возникновении новых условий.

Если вы видите еще какие-то не оптимальные решения, то прошу высказываться в комментариях, с удовольствием обсудим.

На всякий случай в конце привожу все ссылки, релевантные к статье:
  • Unity3D
  • Диаграмма архитектуры этапа 1 нарисована в yEd.
  • Диаграммы архитектуры этапа 2 нарисованы на andoroid-планшете в приложении DrawExpress Lite;
  • Диаграммы архитектуры этапа 3 нарисованы в Visual Paradigm;
  • Обсуждаемое приложение можно посмотреть в play.google.
  • Узнать больше о нашей команде можно на нашем сайте.


P.S. За время подготовки данной статьи, часть найденных ошибок уже была исправлена, и, вероятно, были внесены новые, т.к. разработка не стоит на месте.
Иван Аниканов @JSas
карма
16,0
рейтинг 0,0
Архитектор BPM решений
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (27)

  • +2
    Работа конечно огромная, а игра где?
    • +1
      Ссылки приведены в конце статьи, дублирую.
  • +2
    Очень серьезный подход к разработке, молодцы! Такой же еще и к визуальному оформлению применить бы.
    • 0
      Спасибо за отзыв :)
      Может быть есть какие-то конкретные рекомендации по визуальному оформлению? По сути мы сейчас развиваемся методом проб и ошибок, поэтому нам крайне важно получить независимую оценку. Кроме того, данный интерфейс (версии 1.3) уже существенно отличается в лучшую сторону от первоначального (версий 1.0-1.2).
      • 0
        Критик из меня так себе, но я попробую перечислить моменты, которые бросились в глаза:

        • Текстуры заметно тайлятся. Трава на фоне и на поле, где ползает змейка, идёт очень заметными повторяющимися кусками. Не лучше ли было использовать одну текстуру без повторений? Или вот, например, на фоновой картинке таблицы рекордов тоже стоит тайлящаяся картинка, но за счет изменений цветности и «выбеленности» некоторых участков ощущение повторяемости уже не такое сильное. Я понимаю, что вы старались придерживаться мультяшной стилистики, но всё же текстуры можно сделать на основе фотографий, их много на cgtextures, в резделе nature/grass.
        • Границы сред никак не обозначены. На стыках фоновой травы и камней можно сделать небольшое затенение, чтобы визуально улучшить разделение этих объектов. У вас так сделано на границах досок. А вот на траве поля его нет, — и сразу кажется, это ковер, расстеленный на камнях. Одинаковых, кстати.
        • Элементы управления сливаются с окружением. Тот же эффект тени, отбрасываемой ими на картинку игры, и выделение их другим цветом создадут впечатление того, что они не имеют отношения к миру игры, как камни, например.
        • Я бы порекомендовал больше использовать эффекты полупрозрачности. Плавные переходы наше всё :)
        • Слишком много «рамочек». Полосатый фон, потом белая рамка, потом фон травы, потом окаймление из камней. Впрочем, это дело вкуса.

        Прошу не принимать мои предложения как критику. Статья все же о пайплайне разработки, а не о графике. Удачи!
        • 0
          Полосатый фон и рамочки использованы только для скриншотов, в самой игре их нет.
          В любом случае спасибо, постараемся учесть это и улучшить интерфейс :)
  • 0
    Unity для 2D змейки мне кажется аналогией «из пушки по воробьям» стрелять. Почему выбор пал именно на Unity? Рассматривали ли вообще другие технологии?
    • 0
      Совершенно согласен и, более того, упоминал об этом в статье. Змейку гораздо проще и быстрее было бы написать на «голом» ADT, но одной из целей было как раз познакомиться с Unity, поэтому выбрали что-то не сложное.
      Мы смотрели пару других платформ, но ни одна не заинтересовала так, как Unity.
    • +1
      Да ладно, Юнити не такой уж сложный движок, в свете того, что в него добавили 2д, еще проще стало.
      • +1
        В любом случае, для его изучения надо с чего-то начинать. И начинать всегда лучше с какой-то реальной, но посильной задачи.
    • +1
      Unity — это очень удобная пушка. С неё и по воробьям не грех пострелять.
  • 0
    Отличная статья, спасибо. Эх, давно хочу научиться так проектировать приложения, только не знаю, как.

    Что касается самой игры, то на экране с разрешением 800*480 интерфейс выглядит очень мелко. Пошаговое движение змейки сильно напрягает. В остальном всё прекрасно.
    • 0
      Спасибо за комментарий, про пошаговое движение уже многие тоже написали — так что, думаю, мы исправим это в одном из ближайших обновлений.
      А про проектирование, думаю, здесь только опыт может помочь: с учетом количества допущенных ошибок, упомянутых в статье, мне еще тоже есть чему учиться :)
  • 0
    Статья хорошая, но очень уж сложная. Мне кажется, в конце можно было бы выделить ключевые моменты проектирования игры на Unity в разрезе особенностей работы с объектами этого же Unity (у вас получилось очень обобщенно). Я тоже начинающий Unity-разработчик и уже успел ощутить, насколько там сложно разделить код на модули. Мы с коллегой-художником тоже решили начать с простых 2D игр для Android, 2 из которых в итоге, как и вы, успешно опубликовали в Google Play. В первом проекте в скриптах творился полный хаос. Второй я начал писать более осмысленно, старался как можно больше инкапсулировать, но все равно связей осталось много, и какие-то выводы сделать достаточно сложно. Заметил, что учитывая простоту, вы все равно решили разбить по сценам. Я же, после первого проекта, наоборот решил, что одной сцены вполне хватит (что позволило, к слову, сделать плавные выезжающие менюшки).
    • 0
      Если бы у меня были простые рецепты, я бы их написал, но, к сожалению, это не так. Предлагаю вместе двигаться в сторону открытия этих рецептов.

      Я вижу преимущество в разделении сцен в том, что независимые компоненты максимально разделены, в т.ч., при добавлении различных новых режимов игры (например, кампании), я буду только расширять состав сцен и модифицировать интерфейс меню. Основная же логика останется в неприкосновенности, это должно минимизировать вероятность внесения новых ошибок в отлаженную функциональность.
      А выплывающие меню можно сделать путем переиспользования классов, отвечающих за построение меню, например, в моем случае это можно сделать так:
      1. Взять код, отвечающий за построение интерфейса меню (содержимое метода OnGUI) и выделить его в отдельный класс.
      2. Изменить класс MainGUI (или SettingsGUI — в зависимости от того, какое именно меню хотим выделить), убрав из него отрисовку меню, но пронаследовав от нового созданного класса.
      3. Пронаследовать GameGUI от нового класса и добавить вызов отрисовки меню + анимацию выезжания

      Собственно, я бы сделал именно так, независимо от того, была бы у меня одна сцена или несколько.
      Но в любом случае, я инкапсулировал всю логику, связанную со сценами, в один класс GUINavigator, так что в моем случае замена на одну сцену — дело 15 минут, если возникнет такая необходимость.
      • 0
        Много, однако, в вашем сообщении аббревиатур GUI. А я вот в процессе изучения матчасти не раз встречал плохие отзывы об этом компоненте движка. Якобы метод OnGUI слишком тяжеловесный, создает дополнительные DrawCalls, что негативно сказывается на производительности, в особенности на мобильных устройствах. Хотя, поиграв в вашу Змейку, могу сказать, что работает все очень гладко и быстро, видимо продуманная архитектура дает плоды:)

        И все таки из-за загрузки новой сцены вы не сделаете красивые переходы. Это ведь как одностраничный и многостраничный сайт. Ну это так, на вкус и цвет. Мне просто понравились мои переходы (можете заценить)

        Еще немножко оценю вашу игру, если вы не против. Текст на кнопках очень мелкий, вы не отталкиваетесь от размера экрана при их инициализации? Рекламу сделали, а вот онлайн таблицу рекордов нет. Посчитали ненужным или не захотели перегружать игру излишним функционалом?
        • +3
          И все таки из-за загрузки новой сцены вы не сделаете красивые переходы.

          Не будьте так категоричны. В своей первой игре (Fondemzel Labyrinth) я использовал плавные переходы между сценами таким образом: перед выгрузкой сцены экран затеняется в ноль, а при загрузке другой — постепенно высветляется до нормального состояния из кромешной черноты. Думаю, таких примеров можно привести множество.
          Использование нескольких сцен — одна из возможностей распределения игровых ресурсов и их сортировки, причем очень мощная и удобная.
        • 0
          Вы правы в том, что к разработке OnGUI необходимо подходить очень тщательно, чтобы не было проблем с производительностью. Об этом пишут в т.ч. сами разработчики Unity, и уже вышла бета-версия с новым механизмом UI, который должен заменить устаревший OnGUI. Вы, кстати, ее уже попробовали? На чем делали свой интерфейс?

          Однако, вы ошибаетесь в том, что раздельные сцены не позволят сделать красивые переходы: это вещи совершенно не связанные.
          Возможно, раздельные сцены не позволят сделать переходы так, как они сделаны у вас, но это совсем другая формулировка, верно?
          Разделение на сцены объективно имеет как преимущества, так и недостатки. Но субъективную оценку красоты предлагаю оставить игрокам, все-таки, это им должно нравится приложение, а не нам с вами.

          Вам правда интересно про таблицу рекордов? Просто в вашей фразе
          Посчитали ненужным или не захотели перегружать игру излишним функционалом?

          оба варианта суть одно и то же. Обычно так говорят, когда для себя уже все решили, что печально. Если все же интересно, то онлайн таблицу рекордов мы планируем сделать одновременно с дополнительным режимом игры — прохождением кампании.
          • 0
            Насчет новой системы пока не слышал. Но нынешняя настолько неудобна, что интерфейс делали на спрайтах. Там, где требовался вывод какой-то динамичной информации, я добавлял объекту компонент GUIText, в редакторе настраивал местоположение и в коде при надобности менял ему текст. То есть метод OnGUI я не использовал. Но как это работает внутри, неизвестно. Возможно, компонент GUIText подразумевает, что текст все равно рисуется каждый кадр, возможно — нет.
            • 0
              Скорее всего текст действительно рисуется каждый кадр, т.к. он может может быть динамическим. Я использовал GUIText для вывода значения рейтинга на игровом поле.
          • 0
            На сайте юнити есть хороший и простой урок по плавным переходам между сценами unity3d.com/ru/learn/tutorials/projects/stealth/screen-fader
  • 0
    Не добавляйте управление гироскопом! Я читал несколько статей, где это критикуют. Сам играл в гоночную игру с управлением гироскопом — всего лишь надо влево-вправо поворачивать телефон, чтобы мотоцикл ездил влево-вправо, но было жуууутко неудобно.
    • 0
      Дополнительный вариант ещё никому не помешал, даже если им никто не будет пользоваться.
      • 0
        Согласен. Принцип нашей игры в том, что игрок сам выбирает, какой стиль управления ему подходит.
  • +1
    Добавлю, что 2d режим в юнити ещё сыроват. Я делал небольшой проект, где-то с десятком анимаций и сотней статичных спрайтов. Через некоторое время стало крашиться где-то в недрах движка на загрузке анимаций. Воспроизводилась эта бага месяц назад точно.
    • 0
      Поправьте меня, если я ошибаюсь, но юнити изначально 3d движок. 2d в нем — это что-то вроде частного случая с плоскими объектами и ортогональной камерой. Для упрощения его использования добавили 2д физику и еще некоторые плюшки, но вроде процесс отрисовки остался как и раньше — юзается 3d.
      • 0
        Да. Я даже скажу больше. Во всех современных 2D движках используется такой способ рендеринга. =) Наверное, только в Adobe Flash ещё не до конца к этому пришли, но прогресс идёт.

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