Как создать игры для android. Провал длиною в год, или опыт разработки игры под Android

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

Идея

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

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

Список особенностей

Так как создавать игры на "Андроид" невозможно без их детальной проработки, на данном этапе придется продолжить работу в и описать особенности, которые будут в игре. Приведем пример: серия God of War - это слэшер. Оружие главного героя - кнут. Во время боя можно делать длинные красивые комбо-атаки. Каждый уровень завершается схваткой с боссом.

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

Диздок

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

  • Объектной модели и функциональности сущностей.
  • Функциональных спецификаций.
  • Контента игры.
  • Интерфейса.
  • По необходимости может быть добавлена база знаний.
  • Объектная модель.

Объектная модель

Объектная модель заключает в себе информацию о каждой игровой сущности: оружие, броня, NPC, заклинания, игрок. Она уникальна для каждой игры.

Под функциональностью следует понимать:

  • Можно ли надеть\снять\купить\продать\улучшить.
  • Останется ли в инвентаре после смерти.
  • Потеряет ли прочность с течением времени либо при каком-нибудь действии.
  • Повышает ли характеристики персонажа или группы.
  • Имеет ли набор особых свойств.

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

Функциональные спецификации

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

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

Контент

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

Интерфейс

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

Выбор движка или конструктора

Еще один шаг, который придется выполнить до того, как создавать игры на "Андроид", - выбрать Давно прошли те времена, когда всё необходимо было делать с нуля. Сегодня, взяв готовый конструктор, можно выполнить все работы, написав минимум кода.

Выбор движка стоит делать, основываясь на некоторых его особенностях:

  • Условия использования.
  • Возможности.
  • Цена.
  • Поддержка разработчиками.
  • Задокументированность.
  • Размер комьюнити.
  • Простота.
  • Расширяемость.

Условия использования : возможно, один раз купив конструктор, вы не станете его полноправным владельцем. Бывает, что при коммерческом успехе игры приходится выплачивать процент от прибылей разработчикам движка. Главное - перед тем как создавать игры на "Андроид", ознакомьтесь с продукта, выбранного для реализации.

Возможности : они должны полностью перекрывать потребности разработчика. Если же продукт предлагает больше, чем нужно, игру можно будет легко расширить, задействовать новые функции диздока. Но задумайтесь и о балансе! Использовать Unreal Engine для тетриса - глупо.

Поддержка : в первую очередь, важно выяснить, имеет ли продукт развитие? Исправляются ли ошибки от версии к версии? Обрастает ли он новым функционалом и инструментарием? Движок, который быстро развивается, имеет преимущество перед движком, замороженным несколько лет назад.

Комьюнити : каково число тех, кто использует конструктор? Если пользователей большое количество, найти документацию, уроки, мастер-классы, примеры не является проблемой. Если же пользователей сравнительно мало, эта информация может быть доступна не в том объеме, который позволит вам сделать что-то конкурентоспособное.

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

UDK

Unreal Development Kit - не самый простой в освоении игровой движок, но зато один из самых мощных. Его используют не только начинающие разработчики, но и крупные корпорации. Если вы ищете ответ на вопрос: "Как создать 3Д-игру и какой движок для этого выбрать?" - вам стоит изучить возможности UDK.


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

Torque 2D/3D

Torque - один из самых популярных конструкторов игр для мобильных платформ. Обладает всем необходимым набором редакторов и средств отладки. При разработке программисты уделили большое внимание удобству и старались сделать все утилиты наиболее простыми и доступными.

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

Для написания скриптов в Torque встроен язык Torque Script. Свойства каждого объекта могут быть определены заранее. Также в конструктор встроен набор Box2D, который занимается физическими расчетами.


Если вы пытаетесь найти ответ на вопрос: "Как создать и какой движок для этого выбрать?" - можно смело заявить, что Torque позволит сделать мультиплеерный проект в сжатые сроки. Весь необходимый функционал встроен заранее, а примеры, находящиеся на официальном сайте, покажут, как его использовать наиболее рационально.

Изучение

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

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

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

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

Как создать игру на андроид?











Создание игры для любой операционной системы предполагает знание программирования. Даже для простейших игр типа Flappy Bird нужно знать его основы. Давайте рассмотрим, каким образом происходит создание игры на примере операционной системы Андроид.

Движок

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

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

Среда программирования

Также весьма важным моментом является использование среды программирования. Под этим термином подразумевается инструмент, в котором будет происходить написание игры. В случае с движком LibGDX средой программирования будет Exlipce. Так как движок игры был написан на языке программирования java, то для того чтобы использовать среду программирования, вам придется использовать базовые навыки и знания программирования именно в этом языке. Поэтому если вы не знаете азов, вам нужно будет их изучить.

Создание проекта

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

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

Посмотрело: 13378

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

1) Создание геймплея и разработка прототипа

Весь геймплей нашей игры явился воплощением идей, изложенных в моей предыдущей статье . Если вкратце, то хорошая игра под Андроид - крайне простая, однообразная, с однокликовым управлением, без наказания за смерть, поощряющими и порицательными звуками и красивой минималистичной графикой.
И вот однажды мне в голову пришла интересная идея игры, которая бы сочетала в себе все вышеприведенные свойства. Суть, как говорится, такова: две спаренные гондолы летят в пространстве, а на них надвигаются препятствия с симметричными дырками в них, сквозь которые необходимо пролететь.
Чем-то задумка напоминает Jetpack Joyride и старую добрую игру, в которой вы мчитесь по тоннелю, проскакивая в отверстия во вращающихся кругах.

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

Именно его мы и будем оттачивать, модифицировать, улучшать.

2) Оттачивание геймплея и бета-тесты

На всех стадиях разработки нужно думать о конечном пользователе, проводить испытания на друзьях, знакомых и родных. Так как делаете игру вы лично и вы все о ней знаете, то велик шанс потерять объективность. Для этого и нужны альфа- и бета- тесты. Когда Вы наблюдаете за другим человеком, использующим ваше приложение, смотрите, как он ведет себя, что ему кажется неочевидным в интерфейсе, где ему сложно. Крайне важно учитывать этот фидбек, а не вырывать из рук девайс с криком:«Смотри как надо».
Например, в нашей игре людям не нравилась слишком высокая скорость полета и длина гондол. Поэтому пришлось уменьшить скорость и укоротить гондолы.
Не бойтесь и глобальных изменений, если они пойдут на пользу. Так в нашей игре изменилась ориентация полета, гондолы космического корабля стали червяками, а монетки для сбора внутриигровой валюты стали звездами, набираемыми для ухода в варп.

3) Интерфейс, звук, графика

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

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

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

4) Магазин и внутриигровые товары

Магазин- очень важная вешь в игре, которая заставляет игрока оставаться и проходить ее. У магазина должен быть простой и понятный интерфейс, что нам не удалось реализовать в полной мере.
Продавать в магазине можно любые вещи. У нас, например, продаются червяки, фоны и бонусы. Продавая бонусы, важно помнить, что они должны заметно влиять на легкость прохождения, но не изменять механику игры.
Самой важной, на мой взгляд, частью в интерфейсе магазина является незавершенность. То есть, если куплено не все, то интерфейс должен четко это показывать. Люди ненавидят незавершенность и стремятся её устранить (именно в этом секрет успеха «системы трех звезд» из Angry Birds). В нашей игре мы реализовали незавершенность пузырьками с числом возможных покупок рядом с разделами магазина.

Ещё скажу кое-что о ценообразовании магазина.
Через пару-тройку раундов и грок уже должен смочь купить что-либо, и ему нужно наглядно показать, что что-то можно уже купить. А вот самым дорогим товарам нужно назначать цену так, чтобы игрок должен был сыграть 20-30 раз довольно удачно, чтобы купить их. Вот, к примеру, цены на наших червяков, учитывайте факт, что опытный игрок за раунд набирает от 2000 до 6000, а новичок от 300 до 1000 очков.

5) Монетизация

Тут все просто. Для игр под Андроид выбор стоит между двумя вариантами заработка. Либо реклама, либо внутриигровые покупки, потому что платные игры в Google Play особой популярностью не пользуются. Мы решили совместить оба варианта. Реклама в меню, возможность её платного отключения, возможность покупки внутриигровой валюты за реальные деньги.

6) Оформление страницы в Google Play

Страница в GP - это лицо вашей игры. Именно по ней люди решают скачивать приложение или нет.

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


  • б) Название. Короткое и броское. Желательно из двух слов, которые выражают всю суть игры. Нашу мы назвали Speedy Flyer. Коротко, ясно, звучит.

  • в) Скриншоты. Чтобы понять о чем игра, пользователь будет смотреть скрины. Они должны быть не менее хорошими, чем иконка - яркими, желательно с постобработкой и призывными девизами на них. К примеру, вот наши неплохие скриншоты


  • г) Баннер вверху страницы. Необязательная, но желательная часть лица игры. Хороший баннер придает атмосферы странице на Google Play

  • д) Видео. Видео - это как скриншоты. Человек смотрит его, чтобы увидеть геймплей. Лучше делать на заказ, ибо handmade-ролики смотрятся убого.

  • е) Оценки. Пользователь обязательно посмотрит на оценки прежде, чем скачать игру. Зависят они лишь от качества приложения.

  • ж) Описание. Я считаю описание самой неважной частью страницы. Его почти никто не читает. Но если вы все же хотите написать хорошее описание, почитайте Дэна Кеннеди, например его книгу «Продающее письмо»

7) Первый фидбек и первое обновление

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

  • а) Новые движущиемя и вращающиеся препятствия, которые увеличат динамику игры

  • б) Немного уменьшим скорость, потому что игра кажется людям слишком быстрой

  • в) Радужный выхлоп на весь экран при уходе в варп. Это добавит красоты

  • г) Новые планеты и червяки. Это нужно, чтобы уже прошедшие игру вернулись и новым игрокам было куда стремиться

  • д)Еще много свистелок-перделок

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

8) Издание

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

«Мне понравилось издательством заниматься, в целом. Очень интересно помогать начинающим игроделам, давать им советы. Чувствуешь себя немного учителем. Если кому-то ещё захочется выпустить игру - пишите. Может и Вам смогу помочь.»

Так что и Вы не зарывайте свои мечты и делайте крутые игры.

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

Привет, Хабрасообщество!

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

Предыстория
Какое-то время назад в течение двух лет я разрабатывал игры под Windows Phone. За это время было выпущено 6 игр, только 2 из них были платные + триал и принесли всего лишь несколько долларов, остальные были бесплатные без рекламы. Разрабатывал игры под эту платформу, потому что там был XNA + C#, мне очень нравилась эта связка, но потом пошли слухи, а затем и реалии о том, что поддержка XNA прекращается. Начался поиск других движков, среди которых больше всех мне приглянулся Unity. Сразу говорю, что не хочу спорить о целесообразности его использования для маленьких игр, меня здесь все устраивает, даже если в других движках есть редактор, C#, кросплатформенность и т.п. На тот момент поддержки Windows Phone в Unity еще не было, но меня это не смутило, т.к. разрабатывать под эту платформу мне больше не хотелось, по крайней мере пока (как сказал один знакомый, зачем ездить на дохлой лошади). Говоря откровенно, мои игры были слабенькие, но выбор был сделан. Подписка разработчика Windows Phone была закрыта, началась новая полоса изучений, прохождения туториалов и, собственно, разработки игр.
Выбор игры, начало
Как известно, нельзя начинать со сложного. По этой причине для пробной игры была выбрана идея, которую я уже начал реализовывать под Windows Phone, а именно – головоломка в стиле Братьев Пилотов (скриншот), но с немного другой механикой. В моей игре при щелчке на объекте состояние переключается не у всего ряда и всего столбца, а только у выбранного объекта и у соседних объектов (вверху, внизу, слева, справа). Для визуального отображения эффекта переключения было решено использовать непосредственно включение или зажигание, а что может гореть? Звезды! Потому название Shining stars было выбрано достаточно быстро. Началась разработка.

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

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

Вращение – в Unity есть методы для вращения объектов. Но если честно, работают они для меня порой странно, потому отчасти я просто тыкал наугад и искал вариант, который меня устроит.

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

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

Уровни
В самом-самом первом варианте игры я хотел сделать 20 уровней. Мда, интересная была бы игра. По мудрому совету это число было расширено до ~100 (в планах). Конечно, придумывать уровни самому и проверять, решаемы ли они, мне совсем не хотелось. Я же программист, так зачем издеваться над собой! Через некоторое время был готов генератор, который выдавал все возможные комбинации уровней заданного размера, затем проверял, решаем ли уровень, и выводил его в текстовый файл. Тут-то и была самая большая моя проблема в этой игре. Если при размере игрового поля 4*4 количество уровней измерялось десятками тысяч, то для поля 5*5 уже были миллионы, для поля 6*6… Ну, вы поняли. Да, я программист, я могу написать генератор, могу научить его решать уровни, но я не могу научить его выбирать, какой уровень красивый и войдет в игру, а какой будет отсеян. Потому все эти миллионы уровней нужно было просмотреть, вручную выбрать нужные. Я даже сначала попытался, но потом прикинул масштабы действия (а я хотел сделать игровое поле аж до размера 10*10) и стало мне дурно.

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

Пример создания уровня

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

Число 120 было выбрано неслучайно. На разных размерах телефонов на экране выбора уровней можно вместить разное число иконок этих уровней. При цифре 120 я получилось разбить иконки на группы с одинаковым числом иконок в каждой, что немаловажно. Мне не хотелось, чтобы на первых страницах было по 12 иконок, а на последней всего лишь, например, 10.

Графика
Графика в моей игре прошла, на мой взгляд, целую эволюцию! Изначально я хотел сделать игру очень быстро и выпустить «то, что есть» с теми 20 уровнями. И это «то» сначала мне казалось действительно неплохим, но получив пару советов от других людей, я сильно задумался и стал все перерисовывать. За основу были взяты различные знаменитые игры, например, Angry Birds, из которых я взял идею внешнего вида UI, расположения иконок уровней при их выборе.

Старый вариант графики

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



Окно информации об игре


Окно выбора уровня



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

Финальный вариант графики

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


Окно информации об игре


Окно выбора уровня


Окно с уровнем в процессе игры (один из вариантов фона; всего их 3, выбираются случайным образом)



Внимание уделялось абсолютно всему. Игровые объекты улучшались, чтобы быть красивыми. Объекты UI (кнопки, иконки уровней) совершенствовались, чтобы быть и красивыми, и интуитивно понятными только по их картинке. Даже для иконки было разработано несколько вариантов, из которых с трудом был выбран финальный, а впоследствии заменен на еще один, который казался мне более подходящим. А сколько раз я перерисовывал все тексты!

Иконки

Начальные варианты для выбора

Финальный вариант

В компоновке графики мне очень сильно помогла программа TexturePacker , настоятельно ее рекомендую! Ее бесплатных возможностей вполне хватает для наших целей, а атласы она собирает действительно неплохо. Мною было подготовлено несколько атласов – один для кнопок и два набора для игровых объектов (с локализацией на русский и английский языки). Правда, в старой версии Unity мне пришлось вырезать нужные куски с помощью uv-координат, что несколько неудобно. В новой версии Unity все можно сделать гораздо проще с помощью спрайтов.

Звуки и музыка
Здесь нет никаких проблем. Существует множество ресурсов, где звуки можно купить или взять бесплатные. Т.к. бюджета не было, я выбрал несколько подходящих звуков и мелодий, которые эти ресурсы позволяют скачать бесплатно, т.е. все абсолютно законно и без воровства (да, для меня это важно).
Основная идея была – создать спокойную обстановку, в которой игрок может подумать с тихой музыкой на фоне. Но первый же апдейт игры поменял ситуацию – была добавлена более ритмичная музыка с возможностью выбора нужной мелодии (спокойной или быстрой) в настройках.
Релиз, продвижение, результаты
Отмечу, что игру я собирался выпускать только под Android, т.к. у меня есть соответствующий телефон, но нет устройств iOS, на которых я мог бы потестировать игру, а выпускать без реального теста я не хочу. Так же поводом послужило то, что регистрация в Google Play – это разовый платеж 25 долларов, а в Apple нужно каждый год платить 99 долларов. Возможно, я выпущу игру и под iOS, но несколько позже.

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

На данный момент количество загрузок очень мало. Я догадывался, что будет некий провал, но настолько… Отчасти это связано с тем, что я выкладывал на форумах не только ссылку на Google Play, но и сам apk файл, причем судя по статистике, его загрузки превышают загрузки игры из маркета.

Игра бесплатная. В качестве монетизации был выбран показ рекламы. В процессе разработки я хотел подключить несколько модулей – если не получилось отобразить один, показывать второй и т.д. Первым был подключен известный Chartboost, далее пытался подключить RevMob и PlayHaven, но с ними вышли некоторые заминки (откровенно, они не заработали в игре, причину я так и не выяснил), потому остался только один модуль, от которого и должен был идти весь доход. Но если учесть вышесказанное (количество загрузок и желание пользователей кликать рекламу или ставить рекламируемые игры), вы понимаете, что дохода-то и нет.

Выводы
Как говорят, плохой результат – тоже результат. С момента начала разработки на Unity (время на Windows Phone не считаю) до релиза прошло около 14-15 месяцев. По-хорошему, такая игра пишется за месяц неполной занятости, и то это с запасом. Причина была в нехватке времени. Нет, времени было достаточно – пара часов по вечерам, несколько часов на выходных вполне хватало, чтобы продолжать делать игру без длительных пауз. А не хватало мне морального самочувствия. В этот период было очень много личных проблем, при этом было желание делать игру, были идеи, но я садился за компьютер, тупо смотрел в монитор, затем понимал, что у меня нет вдохновения, что я не могу реализовать абсолютно ничего, и выключал его. Так и пролетел этот год.

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

Но положительных моментов, наверное, больше. В первую очередь, это опыт! На этой игре я изучил основные возможности Unity с одной оговоркой – игра разрабатывалась на старой версии, где еще не было 2d. Сейчас 2d игры можно разрабатывать с этой новой технологией, потому большинство моих наработок из игры уже могут не понадобиться.

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

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

Метки: Добавить метки

Приветствую всех.
Этот урок был написан в марте 2012. Сейчас я решил опубликовать его на данном ресурсе. Код решил не менять, прошу сильно к нему не придираться(сам бы придрался). Немного доработан сам текст урока и комментарии в коде. Цель урока - не научить делать игры, что в рамках одного урока само по себе невозможно, а продемонстрировать основы работы с мобильным AIR. Почему именно Android, а не ios? Потому что на момент написания урока под рукой был только он. Под ios всё делается практически так же, но есть некоторые отличия, о которых написано в конце второй части урока.
Буду рад любым комментариям, замечаниям, указанием на ошибки.

Немного теории.

Adobe AIR позволяет создавать на ActionScript 3 и MXML для iOS и Android приложения, которые для пользователя ничем не будут отличать от нативных. Их, как и любые нативные приложения, можно распространять через фирменные магазины приложений Apple AppStore и Google Play Store (бывший Android Market). С версии AIR 3.2 появилась поддержка Stage3D. Для работы AIR приложения на Android нужно установить на устройство AIR Runtime, или же при компиляции в captive-runtime среда выполнения вшивается в apk. При этом установка AIR Runtime на девайс не требуется.

При работе с мобильными устройствами стоит учитывать, что разрешения их дисплеев ниже(уже есть и такие, у которых выше), чем у мониторов компьютеров и их физические размеры тоже значительно меньше. Также есть такие понятия, как «физический размер пикселя» и «плотность пикселей», поэтому нужно уделить внимание размеру различных графических элементов(кнопок, персонажей игры и т.д.). В общем, это целая наука и мы не будем останавливаться на ней подробно.
Метод ввода - сенсорный дисплей. Для обработки сенсорного ввода существует специальное событие TouchEvent , хотя и события мыши обрабатываются корректно. Также есть другие особенности, о которых я расскажу в ходе урока.
Мы будем делать очень простую игру для Android. Запускать её можно будет на смартфонах, планшетах и любых других устройствах.
Для работы среды выполнения AIR есть некоторые аппаратные и программные требования.
Для Android они выглядят следующим образом:
- Android версии 2.2 или выше;
- Процессор с архитектурой ARM7 с частотой минимум 550MHz;
- минимум 256 мегабайт оперативной памяти.

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

Для выполнения урока нам понадобится следующее:
- FlashDevelop 4.2;
- Flex SDK 4.6;
- AIR SDK 3.5;
- библиотека от greensock ;
- устройство на Android. Можно обойтись и эмулятором, но это не так интересно;
- установленный на устройство AIR Mobile .

Версии указаны актуальные на момент публикации урока. Уже есть Flex SDK 4.8, но по сути это тот же самый 4.6. И с 4.8 у меня FlashDevelop начинает непомерно поглощать оперативку, непонятно почему. Лучше всегда использовать последнюю версию AIR SDK, так как с каждым релизом добавляются новые возможности и исправляются ошибки. AIR SDK нужно просто распаковать в папку Flex SDK с заменой файлов и подключить Flex SDK к FlashDevelop.

Что именно будем делать.

Делать мы будем небольшую игру. Она представляем из себя следующее: Сверху экрана падают фигуры. Снизу находится платформа, которой надо эти фигуры ловить или не ловить в зависимости от их вида. Игра состоит из трёх экранов:
- главное меню с фоном, логотипом и двумя кнопками. Одна для начала игры, другая для выхода из приложения;
- игровой экран. Содержит платформу, падающие фигуры а также индикаторы текущего уровня и набранных очков. И кнопка выхода в главное меню;
- экран отображения количества набранных очков с кнопками для выхода в меню и «сыграть ещё раз».

Пара скриншотов:

Приступаем.

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

Запускаем FlashDevelop и создаём новый проект AIR AS3 Mobile app. Вот так выглядит созданный проект:

Что мы перед собой видим:

Папка bat содержит несколько пакетных файлов:
- CreateCertificate.bat нужен для генерации self-signed сертификата для Android. Без этого сертификата не получится собрать apk;
- InstallAirRuntime.bat устанавливает AIR runtime на Android устройство из %FLEX_SDK%\runtimes\air\android\device\runtime.apk. Лучше самостоятельно установить последнюю версию из Android Play Store;
- Packager.bat упаковывает флешку в apk(android) или ipa(ios);
- SetupApplication.bat содержит различные параметры приложения (пути к сертификатам, пароли от них, название приложения и т.п.);
- SetupSDK.bat содержит путь к FlexSDK. Определяется автоматически, если не определился, нужно прописать вручную. И также содержит путь к AndroidSDK. На самом деле из него нужны только три файла: adb.exe , AdbWinApi.dll и AdbWinUsbApi.dll . Причём они уже содержатся в дистрибутиве FlashDevelop и путь к ним прописывается также автоматически.

В папку bin помещается скомпилированный swf файл.
В папке cert должны лежать сертификаты.
Папка icons содержит наборы иконок.
Две стандартные папки lib и src для библиотек и классов соответственно.

Корневая папка:
- Стандартный для AIR приложений application.xml с различными параметрами приложения;
- PackageApp.bat позволяет выбрать платформу и тип упаковки приложения. После выбора упаковывает. Появляется папка dist c apk или ipa;
- Run.bat заливает приложение на мобильное устройство и запускает его там;
- Два файла AIR_Android_readme.txt и AIR_iOS_readme.txt с инструкциями.

Настройка проекта.

Открываем настройки проекта Project/Propeties , меняем цвет фона на чёрный. Разрешение значения не имеет. Будем подстраиваться под текущее.
Версию AIR выставляем 3.5. В итоге параметры должны выглядеть так:

Открываем файл application.xml , в нём во второй строке меняем версию AIR на 3.5:

Открываем настройки AIR - Project/AIR App Properties . В открывшемся окне переходим к вкладке Initial window . В этом окне выбираем Non-Windowed Platforms .
Переключаем режим отображения на портретный. И выключаем Auto Orient, так как игра будет только в портретном режиме. Render Mode выставляем CPU или Direct . Настройки должны выглядеть вот так:


Далее прям во FlashDevelop двойным щелчком открываем файл Run.bat и меняем строку goto desktop на goto android-debug . Это нужно для тестирования проекта на устройстве. Если оставить goto desktop , проект будет запускаться на эмуляторе.

Теперь нам нужно сгенерировать сертификат. Без него не получится запаковать приложение в.apk. Если папка cert отсутствует в проекте, создайте её вручную. Это важно, так как bat файл не сможет достучаться до несуществующей папки. Запускаем файл bat/CreateCertificate.bat (правый клик/Execute), в папке cert появляется наш сертификат со стандартным паролем «fd». Стоит сказать, что любое приложение для Android должно быть подписано сертификатом. То, что мы сгенерировали, это так называемый «сертификат для разработки». Его нам на данном этапе достаточно.

Из архива с сайта greensock.com достаём файл greensock.swc , кладём его в папку lib и подключаем к проекту (правый клик/Add To Library).

С настройкой всё. Добавим к проекту нужную графику и иконки. Создайте папку assets и положите туда графику из архива , также замените иконки. Или же можете нарисовать собственные аналоги.

Приступаем к коду.

А теперь самое главное и интересное - пишем код. Классы игры выглядят следующим образом:

ItemType - типы предметов. Тех самых, которые падают сверху экрана
package constants { /** * Статические константы для определния типа игрового объекта. * * @author illuzor */ public class ItemType { /** обычный объект. Прибавляет единицу к очкам */ public static const GOOD:String = "goodItem"; /** "очень хороший" объект. Прибавляет 5 к очкам */ public static const VERY_GOOD:String = "veryGoodItem"; /** "злой" объект. Отнимает единицу от очков */ public static const EVIL:String = "evilitem"; } }
ScreenType - типы экранов. Всего у нас их три, и описаны они выше
package constants { /** * Статические константы для определения типа экрана * * @author illuzor */ public class ScreenType { /** главное меню */ public static const MAIN_MENU:String = "mainMenu"; /** игровой экран */ public static const GAME_SCREEN:String = "gameScreen"; /** экран с отображением результата игры */ public static const SCORE_SCREEN:String = "scoreScreen"; } }
GameEvent - игровые события
package events { import flash.events.Event; /** * Игровые события. * * @author illuzor */ public class GameEvent extends Event { /** выход из игры в главное меню через кнопку menu */ public static const EXIT_GAME:String = "exitGame"; /** игра проиграна */ public static const GAME_OVER:String = "gameOver"; public function GameEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false) { super(type, bubbles, cancelable); } public override function clone():Event { return new GameEvent(type, bubbles, cancelable); } public override function toString():String { return formatToString("GameEvent", "type", "bubbles", "cancelable", "eventPhase"); } } }
Теперь рассмотрим пакет elements . Это различные графические элементы, которые используются в игре.

Button - класс кнопки. Состоит из графического изображения и текста. Имеет два состояния: нажато/не нажато
package elements { import com.greensock.TweenLite; import flash.display.Bitmap; import flash.display.Sprite; import flash.events.Event; import flash.events.TouchEvent; import flash.text.TextField; import tools.Bitmaps; import tools.Tools; /** * Класс кнопки, которая используется в меню и в других местах. * * @author illuzor */ public class Button extends Sprite { /** @private текст для отображения на кнопке */ private var text:String; /** @private битмап для фона кнопки */ private var buttonImage:Bitmap; /** * Конструктор слушает добавления на сцену. * тут stage нам нужен на случай, если произойдёт тап по кнопке и перемещение пальца в сторону от кнопки * * @param text текст для отображения на кнопке */ public function Button(text:String) { this.text = text; addEventListener(Event.ADDED_TO_STAGE, addedToStage); } /** * @private добавление на сцену. * добавляем графику и текстовое поле кнопки. * * @param e событие добавления на сцену */ private function addedToStage(e:Event):void { removeEventListener(Event.ADDED_TO_STAGE, addedToStage); buttonImage = Bitmaps.buttonBitmap; // добавляем битмап buttonImage.smoothing = true; addChild(buttonImage); var textField:TextField = Tools.generateTextField(50, text); // генерируем текстовое поле... textField.x = (buttonImage.width - textField.width) / 2; // ... позиционируем его и добавляем в дисплейЛист textField.y = (buttonImage.height - textField.height) / 2; addChild(textField); this.addEventListener(TouchEvent.TOUCH_BEGIN, touchBegin); // прикосновение к кнопке addEventListener(Event.REMOVED_FROM_STAGE, removedFromStage); // слушатель удаления со stage } /** * анимируем по альфе на половину * * @param e событие прикосновения пальцем к кнопке */ private function touchBegin(e:TouchEvent):void { TweenLite.to(buttonImage, .3, { alpha:.5 }); stage.addEventListener(TouchEvent.TOUCH_END, touchEnd); // убирание пальца от дисплея после прикосновения к кнопке } /** * возвращаем альфу к единице * * @param e событие убирания пальца */ private function touchEnd(e:TouchEvent):void { TweenLite.to(buttonImage, .3, { alpha:1 }); stage.removeEventListener(TouchEvent.TOUCH_END, touchEnd) } /** * при удалении со stage убиваем более не нужные слушатели * * @param e событие удаления со сцены */ private function removedFromStage(e:Event):void { removeEventListener(Event.REMOVED_FROM_STAGE, removedFromStage); this.removeEventListener(TouchEvent.TOUCH_BEGIN, touchBegin); stage.removeEventListener(TouchEvent.TOUCH_END, touchEnd) } } }
Item - предмет, который падает сверху в процессе игры. Может быть трёх типов, они описаны в коде
package elements { import constants.ItemType; import flash.display.Shape; import flash.filters.GlowFilter; /** * Класс айтема (предмета), который падаёт сверху экрана. * * @author illuzor */ public class Item extends Shape { /** тип айтема из constants.ItemType */ public var type:String; /** скорость движения айтема */ public var speed:uint; /** * В конструкторе рисуется графика айтема. * * @param type тип айтема */ public function Item(type:String) { this.type = type; switch (type) { // проверяем, какой тип передан в конструктор // и в зависимости от этого рисуем соответствующую графику. case ItemType.GOOD: // рисуем зелёный квадрат this.graphics.beginFill(0x00A400); this.graphics.drawRect(0, 0, 14, 14); this.graphics.endFill(); break; case ItemType.VERY_GOOD: // рисуем синий квадрат со свечением this.graphics.beginFill(0x01A6FE); this.graphics.drawRect(0, 0, 14, 14); this.graphics.endFill(); this.filters = ; break; case ItemType.EVIL: // рисуем красный круг graphics.beginFill(0xFF0000); graphics.drawCircle(0, 0, 7); graphics.endFill(); break; } } } }
Platform - «главный герой» игры. Небольшая платформа, которой управляет игрок.
package elements { import flash.display.Shape; import flash.filters.GlowFilter; /** * Платформа для ловли объектов, которая находится в нижней части экрана. * Тут всё очень просто, рисуется белый прямоугольник и применяется фильтр свечения. * Отдельный класс для того, чтобы платформа воспринималась, как отдельная игровая единица. * * @author illuzor */ public class Platform extends Shape { public function Platform() { this.graphics.clear(); this.graphics.beginFill(0xFFFFFF); this.graphics.drawRect(0, 0, 110, 24); this.graphics.endFill(); this.filters = ; } } }
Пакет tools с инструментами.

Класс Bitmaps содержит прикреплённую графику и методы её получения извне
package tools { import flash.display.Bitmap; /** * "Генератор" битмапов из прикреплённых файлов * * @author illuzor */ public class Bitmaps { /** @private прикреплённый файл графики фона */ private static var BackgroundBitmap:Class; /** @private прикреплённый файл графики для кнопки */ private static var ButtonBitmap:Class; /** @private прикреплённый файл графики для логотипа */ private static var LogoBitmap:Class; /** Битмап фона */ public static function get backgroundBitmap():Bitmap { return new BackgroundBitmap() as Bitmap; } /** Битмап кнопки */ public static function get buttonBitmap():Bitmap { return new ButtonBitmap() as Bitmap; } /** Битмап логотипа */ public static function get logoBitmap():Bitmap { return new LogoBitmap() as Bitmap; } } }
Класс Tools содержит другие инструменты. Пока что, только генератор текстфилда
package tools { import flash.text.TextField; import flash.text.TextFormat; /** * Класс с небольшими инструментами. * Пока что содержит только генератор текстовового поля. * * @author illuzor */ public class Tools { /** * Генератор текстфилда по заданным параметрам * * @param size размер шрифта * @param text текст для отображения * @param color цвет текста * @return настроенное текстовое поле */ public static function generateTextField(size:uint, text:String = "", color:uint = 0xFFFFFF):TextField { var textFormat:TextFormat = new TextFormat(); textFormat.color = color; textFormat.size = size; var textField:TextField = new TextField(); textField.selectable = false; textField.defaultTextFormat = textFormat; textField.text = text; textField.width = textField.textWidth +4; textField.height = textField.textHeight +4; return textField; } } }
Экраны приложения из пакета screens

MainMenu - главное меню игры. Отображается сразу после запуска. Содержит кнопки, фон и логотип.
package screens { import flash.display.Bitmap; import flash.display.Sprite; import flash.events.Event; import flash.display.DisplayObject; import tools.Bitmaps; import elements.Button; /** * Класс главного меню игры. * Тут отображется фон, логотип и две кнопки. * * @author illuzor */ public class MainMenu extends Sprite { /** кнопка "PLAY" */ public var playButton:Button; /** кнопка "EXIT" */ public var exitButton:Button; /** * В конструкторе просто слушаем добавление на сцену */ public function MainMenu() { addEventListener(Event.ADDED_TO_STAGE, adddedToStage); } /** * создаём и добавляем фон, логотип, кнопки * * @param e событие добавления на сцену */ private function adddedToStage(e:Event):void { removeEventListener(Event.ADDED_TO_STAGE, adddedToStage); // создаём битмап фона и добавляем его на сцену var background:Bitmap = Bitmaps.backgroundBitmap; background.smoothing = true; addChild(background); placeBackground(background); // позиционируем фон // создаём битмап логотипа, задаём размер относительно ширины сцены // задаём положение и добавляем на сцену var logo:Bitmap = Bitmaps.logoBitmap; logo.smoothing = true; logo.width = stage.stageWidth * .7; logo.scaleY = logo.scaleX; logo.x = (stage.stageWidth - logo.width) / 2; logo.y = stage.stageHeight / 5; addChild(logo); // контейнер для кнопок для удобного позиционирования этих кнопок var buttonsContainer:Sprite = new Sprite(); addChild(buttonsContainer); // создание кнопок "PLAY" и "EXIT", подгонка их размеров и добавление в контейнер playButton = new Button("PLAY"); buttonsContainer.addChild(playButton); playButton.width = stage.stageWidth / 2; playButton.scaleY = playButton.scaleX; exitButton = new Button("EXIT"); buttonsContainer.addChild(exitButton); exitButton.y = buttonsContainer.height + 25; exitButton.width = stage.stageWidth / 2; exitButton.scaleY = exitButton.scaleX; // позиционирование контейнера с кнопками buttonsContainer.x = (stage.stageWidth - buttonsContainer.width) / 2; buttonsContainer.y = (stage.stageHeight - buttonsContainer.height) / 2 + stage.stageWidth / 6; } /** * Эта функция делает так, что переданный ей DisplayObject заполняет собой всю сцену * без изменения пропорций. В нашем случае это фоновое изображение * * @param scaledObject DisplayObject для подгонки */ private function placeBackground(scaledObject:DisplayObject):void { scaledObject.scaleX = scaledObject.scaleY = 1; var scale:Number; if (scaledObject.width / scaledObject.height > stage.stageWidth / stage.stageHeight){ scale = stage.stageHeight / scaledObject.height; } else { scale = stage.stageWidth / scaledObject.width; } scaledObject.scaleX = scaledObject.scaleY = scale; scaledObject.x = (stage.stageWidth - scaledObject.width) / 2; scaledObject.y = (stage.stageHeight - scaledObject.height) / 2; } } }
В классе GameScreen проходит игровой процесс. Сверху падают айтемы. В зависимости от их типа нужно ловить их или избегать. Управляет игровым циклом
package screens { import com.greensock.TweenLite; import constants.ItemType; import elements.Button; import elements.Platform; import elements.Item; import events.GameEvent; import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.events.TimerEvent; import flash.events.TouchEvent; import flash.text.TextField; import flash.utils.Timer; import tools.Tools; /** * Основной класс игры. Что предствляет собой игровой процесс: * Внизу экрана находится платформа, которую можно двигать влево/вправо движением пальца по экрану в любой части экрана. * Сверху падают так называемые айтемы, которые могут быть трёх видов: * - ItemType.GOOD - обычный айтем. при собирании прибавляет единицу к очками, при пропускании(уход за нижнюю границу экрана) * отнимает единицу от очков * - ItemType.VERY_GOOD - "усиленный" айтем. При собирании прибавляет 5 к очками. При пропускании ничего не происходит. * - ItemType.EVIL - "злой" айтем. При собирании отнимает единицу от очков. При пропускании ничего не происходит. * * При значении очков меньше пяти засчитывается проигрыш, а результатом игры считается максимальное набранное количество очков. * С каждым новым уровнем айтемы движутся быстрей, чем в предыдущем. * * Сверху слева находится идикатор набранных очков, справа сверху кнопка выхода в главное меню. * Снизу в центре под платформой находится индикатор текущего уровня. * * * @author illuzor */ public class GameScreen extends Sprite { /** @private контейнер для платформы и айтемов */ private var gameContainer:Sprite; /** @private кнопка для выхода в главное меню */ private var menuButton:Button; /** @private платформа */ private var platform:Platform; /** @private игровой таймер. нужен для добавления нового айтема */ private var gameTimer:Timer; /** @private номер текущего уровня */ private var currentLevel:uint; /** @private текущее количество очков */ private var currentScore:int; /** @private текстовое поле для отображения номера уровня */ private var levelText:TextField; /** @private текстовое поле для отображения очков */ private var scoreText:TextField; /** максимальное количество очков */ public var maxScore:uint; /** * В конструкторе слушаем добавление на сцену. */ public function GameScreen() { addEventListener(Event.ADDED_TO_STAGE, addedtToStage); } /** * создаём элементы экрана * * @param e событие добавления на сцену */ private function addedtToStage(e:Event):void { removeEventListener(Event.ADDED_TO_STAGE, addedtToStage); gameContainer = new Sprite(); // контейнер addChild(gameContainer); platform = new Platform(); // создаём платформу, подгоняем её размер и положение и добавляем на сцену platform.width = stage.stageWidth * .18; platform.scaleY = platform.scaleX; platform.x = (stage.stageWidth - platform.width) / 2; platform.y = stage.stageHeight * .88; addChild(platform); scoreText = Tools.generateTextField(30, "SCORE: 0"); // текстовое поле очков scoreText.width = stage.stageWidth / 2; scoreText.x = scoreText.y = 10; addChild(scoreText); levelText = Tools.generateTextField(30, "LEVEL: 1"); // текстовое поле уровня levelText.width = levelText.textWidth + 4; levelText.x = (stage.stageWidth - levelText.width) / 2; levelText.y = stage.stageHeight - levelText.height - 20; addChild(levelText); menuButton = new Button("MENU");// кнопка для выхода в главное меню. addChild(menuButton); menuButton.width = stage.stageWidth / 3.2; menuButton.scaleY = menuButton.scaleX; menuButton.x = stage.stageWidth - menuButton.width - 10; menuButton.y = 10; startNewLevel(); // запускаем новый уровень menuButton.addEventListener(TouchEvent.TOUCH_TAP, exitGame); // событие нажатия на кнопку выхода в меню stage.addEventListener(MouseEvent.MOUSE_MOVE, moveplatform); // событие движения пальца по экрану stage.addEventListener(MouseEvent.MOUSE_UP, moveplatform); // событие убирания пальца с экрана addEventListener(Event.ENTER_FRAME, updateGame); // обновление состояния игры addEventListener(Event.REMOVED_FROM_STAGE, removedFromStage); } /** * @private двигаем платформу в зависимости от положения пальца на дисплее. * * @param e событие движения пальца по экрану или его убирания с экрана */ private function moveplatform(e:MouseEvent):void { if(mouseY > menuButton.y + menuButton.height)TweenLite.to(platform, .36, { x:mouseX + -platform.width/2 }); } /** * @private запуск нового уровня. каждый уровень состоит из 20 айтемов */ private function startNewLevel():void { var interval:uint = 2300; // интервал вызова таймера if (2300 - currentLevel * 350 < 250) { // чем выше уровень, тем меньше интервал interval = 350; } else { interval = 2300 - currentLevel * 350; } gameTimer = new Timer(interval, 20); // создаём и запускаем таймер. gameTimer.start(); gameTimer.addEventListener(TimerEvent.TIMER, addItem); gameTimer.addEventListener(TimerEvent.TIMER_COMPLETE, cicleEnd); } /** * @private Создаём новый айтем по таймеру * * @param e событие тика таймера */ private function addItem(e:TimerEvent):void { var randomRange:Number = Math.random(); // случайное значене var itemType:String = ItemType.GOOD; // тип нового айтема. по умолчнанию все айтемы обычные if (randomRange > 0.65 && randomRange < .95) { // если случайное значение в заданном диапазоне (30%)... itemType = ItemType.EVIL; // айтем злой } else if (randomRange >= .95){ // 5% айтемов пусть будут "усиленными" itemType = ItemType.VERY_GOOD; } var item:Item = new Item(itemType); // создаём новый айтем со сгенерированным типом item.x = stage.stageWidth * Math.random(); // помещаем на случайны.x item.y = -item.height; // а по.y убираем за пределы сцены item.speed = currentLevel+1; // скорость айтема gameContainer.addChild(item); // и добавлеем его на сцену } /** * @private когда таймер закончил работу, очищаем, что не нужно и запускаем новый уровень * * @param e событие окончания работы таймера */ private function cicleEnd(e:TimerEvent):void { gameTimer.removeEventListener(TimerEvent.TIMER, addItem); gameTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, cicleEnd); gameTimer = null; // удаляем слушаели с таймера и ссылку на таймер currentLevel++; // увеличиваем уровень на единицу levelText.text = "LEVEL: " + String(currentLevel + 1); // обновляем текст уровня levelText.width = levelText.textWidth +4; startNewLevel();// запускаем новый уровень } /** * @private Игровой цикл * Обновляем положения айтемов. если они ушли за пределы сцены, удаляем. * Обратываем их столкновения с платформой * * @param e enterFrame событие */ private function updateGame(e:Event):void { // в цикле проходимся по всему содержимому игрового контейнера, кроме платформы (i = 1) for (var i:int = 1; i < gameContainer.numChildren; i++) { var tempItem:Item = gameContainer.getChildAt(i) as Item; // берём айтем tempItem.y += (tempItem.speed * 3) * .8; // увеличиваем его.y координаты в зависимости от его скорости. if (tempItem.y > stage.stageHeight) { // если он ушёл вниз за сцену... gameContainer.removeChild(tempItem); //... удаляем его... if (tempItem.type == ItemType.GOOD) currentScore--; // .. а если его тип при этом оказался обычным, отнимаем единицу от очков. } if (tempItem.hitTestObject(platform)) { // если айтем пойман платформой // в зависимости от его типа, производим действие. // думаю, тут ничего не надо объяснять switch (tempItem.type) { case ItemType.GOOD: currentScore++; break; case ItemType.VERY_GOOD: currentScore +=5; break; case ItemType.EVIL: currentScore--; break; } // также при попадании на платформу айтем больше не нужен, удаляем его со сцены gameContainer.removeChild(tempItem); } } scoreText.text = "SCORE: " + currentScore; // обновляем тексовое поле с очками if (maxScore < currentScore) maxScore = currentScore; // записываем максимальное количество очков if (currentScore < -5) { // если количество очков меньше, чем -5.. dispatchEvent(new GameEvent(GameEvent.GAME_OVER)); //... генерируем событие проигрыша } } /** * @private генерируем событие выхода из игры * * @param e событие прикосновения к кнопке выхода в меню */ private function exitGame(e:TouchEvent):void { dispatchEvent(new GameEvent(GameEvent.EXIT_GAME)); } /** * @private удаляем все ненужные больше слушатели * * @param e событие удаления со сцены */ private function removedFromStage(e:Event):void { removeEventListener(Event.REMOVED_FROM_STAGE, removedFromStage); removeEventListener(Event.ENTER_FRAME, updateGame); stage.removeEventListener(MouseEvent.MOUSE_MOVE, moveplatform); stage.removeEventListener(MouseEvent.MOUSE_UP, moveplatform); menuButton.removeEventListener(TouchEvent.TOUCH_TAP, exitGame); gameTimer.stop(); gameTimer.removeEventListener(TimerEvent.TIMER, addItem); gameTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, cicleEnd); } } }
ScoreScreen отображается после проигрыша. Показывает текстовое поле с итоговым результатом и кнопки для возврата в меню и повторения игры
package screens { import elements.Button; import flash.display.Sprite; import flash.events.Event; import flash.text.TextField; import tools.Tools; /** * Экран для отображения результата игры в виде количества набранных очков. * Состоит из текста "YOUR SCORE: " и кнопок "MENU" и "AGAIN" * * @author illuzor */ public class ScoreScreen extends Sprite { /** @private количество очков для отображения */ private var score:uint; /** кнопка "MENU" */ public var menuButton:Button; /** кнопка "AGAIN" */ public var againButton:Button; /** * В конструкторе ждём добавления на stage. Тут stage нужен для позиционирования элементов * @param score количество очков для отображения */ public function ScoreScreen(score:uint) { this.score = score; addEventListener(Event.ADDED_TO_STAGE, addedToStage); } /** * создаём текстовое поле для отображения очков и кнопки для повтора игры и возврата в главное меню. * * @param e событие добавления на сцену */ private function addedToStage(e:Event):void { removeEventListener(Event.ADDED_TO_STAGE, addedToStage); // текстовое поле отображет количество набранных очков var scoreText:TextField = Tools.generateTextField(40, "YOUR SCORE: " + score); scoreText.x = (stage.stageWidth - scoreText.width) / 2; scoreText.y = (stage.stageHeight - scoreText.height) / 2 - stage.stageHeight / 6; addChild(scoreText); // кнопка для выхода в главное меню menuButton = new Button("MENU"); addChild(menuButton); menuButton.width = stage.stageWidth / 2; menuButton.scaleY = menuButton.scaleX; menuButton.x = (stage.stageWidth - menuButton.width) / 2; menuButton.y = scoreText.y + scoreText.height + 30; // кнопка "сыграть ещё" againButton = new Button("AGAIN"); addChild(againButton); againButton.width = stage.stageWidth / 2; againButton.scaleY = againButton.scaleX; againButton.x = (stage.stageWidth - againButton.width) / 2; againButton.y = menuButton.y + menuButton.height + 30; } } }
И последнее - класс Main из корня. Основной класс игры. Служит для переключения экранов и очистки при этих переключениях. Слушает кнопки экранов на нажатие и игровые события. В зависимости от них очищает сцену и показывает нужный экран
package { import constants.ScreenType; import events.GameEvent; import flash.desktop.NativeApplication; import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; import flash.events.TouchEvent; import flash.ui.Multitouch; import flash.ui.MultitouchInputMode; import screens.GameScreen; import screens.MainMenu; import screens.ScoreScreen; /** * Основной класс игры. Управляет отображением разных экранов игры. * Игра представляет из себя следующее: внизу экрана находится "платформа", которую можно передвигать. * Сверху падают "предметы" трёх типов: красный, зелёный, синий. Нужно ловить их платформой. * * Создано с помощью FlashDevelop 4.0.1 и Flex SDK 4.6 * С использованием библиотеки от greensock - http://www.greensock.com/v11/ * * @author illuzor * @version 0.6 */ public class Main extends Sprite { /** @private экран главного меню */ private var menuScreen:MainMenu; /** @private экран игры */ private var gameScreen:GameScreen; /** @private экран отображения результата игры (набарнных очков) */ private var scoreScreen:ScoreScreen; /** @private эта переменная хранит текстовое значения типа экрана * из constants.ScreenType, который отображается в данный момент * нужна для корретной очистки от слушателей и экранных объектов */ private var currentScreen:String; /** * Главный конструктор */ public function Main():void { stage.scaleMode = StageScaleMode.NO_SCALE; stage.align = StageAlign.TOP_LEFT; // событие деактивации приложения. то есть выхода из него или сворачивания (нажитии кнопки "домой" или "назад") stage.addEventListener(Event.DEACTIVATE, deactivate); // тип ввода. TOUCH_POINT выставлен по умолчанию и нам он подходит Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT; showMenu(); // показываем главное меню. } /** * @private показываем главное меню */ private function showMenu():void { currentScreen = ScreenType.MAIN_MENU; // применяем тип экрана menuScreen = new MainMenu(); // создаём меню и добавляем его на сцену addChild(menuScreen); menuScreen.playButton.addEventListener(TouchEvent.TOUCH_TAP, startGame); // добавляем слушатели к кнопкам меню menuScreen.exitButton.addEventListener(TouchEvent.TOUCH_TAP, deactivate); } /** * @private показываем игровой экран * * @param e событие прикосновения к кнопке playButton главного меню */ private function startGame(e:TouchEvent):void { clear(); // очищаем currentScreen = ScreenType.GAME_SCREEN; // применяем тип экрана gameScreen = new GameScreen(); // создаём игровой экран и добавляем на сцену addChild(gameScreen); gameScreen.addEventListener(GameEvent.EXIT_GAME, exitGame); // событие выхода из игры по кнопке gameScreen.addEventListener(GameEvent.GAME_OVER, gameOver); // событие проигрыша } /** * @private выход из игры по нажатию кнопки * * @param e событие выхода из игры */ private function exitGame(e:GameEvent):void { clear(); // очищаем showMenu(); // показываем главное меню } /** * @private игра проиграна, показ результата * * @param e событие проигрыша */ private function gameOver(e:GameEvent):void { var score:uint = gameScreen.maxScore; // количество очков. достаётся из игрового экрана из переменной maxScore clear(); // очищаем currentScreen = ScreenType.SCORE_SCREEN; // применяем тип экрана scoreScreen = new ScoreScreen(score); // создаём и показываем экран результатов addChild(scoreScreen); scoreScreen.menuButton.addEventListener(TouchEvent.TOUCH_TAP, exitScore); // слушатели кнопок экрана результатов scoreScreen.againButton.addEventListener(TouchEvent.TOUCH_TAP, startGame); } /** * @private выход из экрана результатов по кнопке. * нужно сделать то же самое, что и при нажатии кнопки выхода из игры, поэтому просто вызываем exitGame() * * @param e событие прикосновения к кнопке выхода из экрана результатов */ private function exitScore(e:TouchEvent):void { exitGame(null); } /** * @private очистка от ненужных слушателей и экранных объектов * в зависимости от текущего экрана. */ private function clear():void { switch (currentScreen) { case ScreenType.MAIN_MENU: menuScreen.playButton.removeEventListener(TouchEvent.TOUCH_TAP, startGame); menuScreen.exitButton.removeEventListener(TouchEvent.TOUCH_TAP, deactivate); removeChild(menuScreen); menuScreen = null; break; case ScreenType.GAME_SCREEN: gameScreen.removeEventListener(GameEvent.EXIT_GAME, exitGame); gameScreen.removeEventListener(GameEvent.GAME_OVER, gameOver); removeChild(gameScreen); gameScreen = null; break; case ScreenType.SCORE_SCREEN: scoreScreen.menuButton.removeEventListener(TouchEvent.TOUCH_TAP, exitScore); scoreScreen.againButton.removeEventListener(TouchEvent.TOUCH_TAP, startGame); removeChild(scoreScreen); scoreScreen = null; break; } } /** * выход из приложения через NativeApplication * при нажатии кнопки "домой" или "назад" приложение закрывается * * @param e событие деактивации */ private function deactivate(e:Event):void { NativeApplication.nativeApplication.exit(); } } }

Компиляция и запуск.

Проект настроен, графика нарисована, код написан. Это всё. Можно тестировать игру. Берём смартфон, заходим в Developer Options , включаем USB Debug Mode . Подключаем его кабелем к компьютеру.

Теперь нужно совершить некоторые действия во FlashDevelop.
- компилируем проект Project/BuildProject (F8). В папке bin появится флешка;
- запускаем PackageApp.bat . Вводим «2», нажимаем Enter. Ждём, пока проект запакуется в apk и появится в папке dist ;
- в меню выбираем Debug/Start Remote Session ;
- Запускаем Run.bat - приложение зальётся на устройство, запустится там и подключится к дебаггеру.

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

Все эти 4 действия можно произвести другим способом. Просто выбираем Project/Test Project (F5). Всё выполнится автоматически.

Когда приложение протестировано и закончено, запускаем PackageApp.bat , вводим единицу. В папке dist появится релизный apk.

Конец первой части.