Введение
![](https://habrastorage.org/files/32a/3e3/4d1/32a3e34d15ac4014afe583d85c3c5383.gif)
В основном это список приемов и бесплатных инструментов, которые я использовал при создании мобильного endless runner’а Good Cat Gone Bad, который выйдет в Google Play 4 мая 2016 г.
Игра разрабатывалась как проект-хобби, используя Unity 5, и заняло все это дело около 4 месяцев на постоянной основе (полный рабочий день).
Дизайн
После того, как я определился с этой сумасшедшей идеей, я начал набрасывать список компонентов приложения и описывать их с помощью коротких тезисов. Например:
Компоненты:
- game over меню
- уровень
- зона уровня (трасса, парк, жилой район)
- игрок
- коп
- очки
- здоровье
Game over меню:
- показывается при смерти игрока
- пункт Играть Заново
- пункт Воскресить
- пункт Выйти
Уровень:
- может быть пройден
- чтобы пройти, нужно набрать X очков
- чем больше уровень, тем больше требуется очков
- генерируется процедурно
- вид напоминает столбец
- состоит из бесконечного списка зон
Игрок:
- гигантский кот
- идет всегда вперед
- может смещаться влево, вправо
- уничтожает все на своем пути
- имеет здоровье
- имеет очки
Коп:
- стоит на месте
- стреляет в игрока
- дает очки при смерти
Я использую Google Keep для хранения этих записей и прочей ерунды.
В целом, вы видели большую часть документа с начальным дизайном. Этого было достаточно для того, чтобы начать разработку. Позже я улучшил дизайн, используя такие штуки, как Core Loop, Subversion, Bartle Test.
Архитектура
Проект состоит из модулей. Модуль — это набор компонентов (логика) и asset’ов (текстуры, звуки и т.д.), относящихся к какой-то фиче или к части инфраструктуры.
Assets/Src/Catzilla
+-- AppModule
+-- CommonModule
+-- GameOverMenuModule
+-- LevelAreaModule
+-- LevelModule
+-- LevelObjectModule
+-- MainMenuModule
+-- MarketingModule
+-- PlayerModule
L-- SkillModule
Компоненты и asset’ы внутри модуля структурированы по типу.
Assets/Src/Catzilla/LevelObjectModule
+-- Animation
+-- Config
+-- Controller
+-- Materials
+-- Mesh
+-- Model
+-- Prefab
+-- Sound
+-- Texture
L-- View
У каждого модуля (кроме модулей, состоящих сугубо из asset’ов, например Marketing) есть компонент, отвечающий за инициализацию модуля (регистрация сервисов, конфигурационных значений, обработчиков событий и т.д.).
В качестве точки входа в приложение выступает специальный модуль (App). В нем регистрируются и инициализируются остальные модули.
Все это дело основано на библиотеке Zenject, которая, помимо этого, предоставляет гибкое внедрение зависимостей.
Архитектура проекта основуется на событийном подходе. В моем случае это означает, что ключевые компоненты ничего не знают друг о друге и пораждают события, которые обрабатываются посредниками, отвечающими за взаимодействие компонентов.
Графика
На протяжении уровня, в каждом кадре < 5 тыс. вершин и < 20 вызовов отрисовки (draw call).
Все эти хипстерские модели я делал в Blender. Каждая модель содержит < 200 треугольников. Большинство моделей состоят из отдельных частей (например, ноги, руки, голова, тело), чтобы их можно было разрушить.
![](https://habrastorage.org/files/d95/f6b/fc6/d95f6bfc674f4c7a802e32f31e127b8c.png)
Коп. Куча кубиков.
![](https://habrastorage.org/files/e58/fca/735/e58fca7358a14439b165eb2e1c63f5fd.png)
Начало трассы. Обычная плоскость.
![](https://habrastorage.org/files/93f/dd2/597/93fdd2597ef94b9cbc8924d9093825d5.png)
Некоторые люди не верят, что это Impala 64.
![](https://habrastorage.org/files/797/40e/33f/79740e33ff4e4f4e89d3ebeb4d5e2557.png)
А некоторые думают, что эта пальма не с Майами.
Текстурирование было сделано с помощью техники Hyden’а, и текстуры всех обьектов уровня были объединены в одну большую (256х256) текстуру, в качестве оптимизации.
![](https://habrastorage.org/files/4f3/544/ce9/4f3544ce9c874a6ab848b67a398ec7dc.png)
Объединенная текстура всех обьектов уровня.
Для зон уровня (например, парк, трасса) я использовал материал с шейдером Mobile / Unlit (Supports Lightmap), а для обьектов уровня — шейдер Mobile / VertexLit (Only Directional Lights).
В игре только 1 направленный (directional) источник света и нет теней.
Эталонное разрешение (reference resolution) компонента Canvas Scaler = 480x800, что покрывает большую часть устройств. Кстати, кто-нибудь знает о существовании этой страницы?
Цветовая палитра игры была сгенерирована в Paletton.
Для текста я использовал шрифт Press Start 2P, который можно найти в Google Fonts.
Для плавающего текста (например, очки над обьектом) я использовал Text Mesh, вместо UI Text в режиме world space, в качестве оптимизации.
Иконки для UI (например, здоровье, умения) были позаимствованы у пакета 64 Flat Game Icons.
Анимация
Анимация была сделана в Unity (Mecanim). Всего 3 типа обьектов уровня анимированы: игрок, гражданский и бонус (например, вращающаяся штука, похожая на доллар).
![](https://habrastorage.org/files/ac3/611/8d3/ac36118d3e1048a7a6a7d7db7a9590ba.gif)
Бегущий гражданский. 5 кадров.
Аудио
Звуки были сгенерированы в Sfxr, мне показалось, что у него это получается лучше, чем у Bfxr.
Компоненты Audio Source используют 2D режим (spatial blend = 0). У каждого обьекта уровня свой Audio Source, кроме игрока, у которого их 2, для большего числа одновременных звуков. Но для UI всего 1 общий Audio Source.
Звуки проигрываются через специальный компонент — аудио менеджер, который следит за тем, чтобы одновременно не проигрывалось много звуков, а также склеивает (на самом деле отбрасывает) идентичные звуки, если они проигрываются в течение очень короткого интервала времени, чтобы амплитуда не зашкаливала в выходном аудиосигнале.
Материалы для маркетинга
Иконка. Настоящий шедевр. Была сделана отдельной сценой в Unity, путем размещения одной из зон уровня и игрока, и подогнав камеру.
![](https://habrastorage.org/files/148/698/0e9/1486980e9d3548e7a40d28b2230901ea.png)
Вы уже догадались что это. Иконка.
Я использовал Icon Slayer для того, чтобы добавить к ней крутых эффектов (скругленные уголки) и получить нужные размеры.
Следующий кусок… всмысле шедевр — это Feature Graphic (баннер, промо, не важно), которая сделана тем же способом, как отдельная сцена.
![](https://habrastorage.org/files/8de/1f9/12d/8de1f912d722410486172674262c970b.png)
Может, мне стоит снимать блокбастеры, вместо всего этого?
Трейлер был сделан в iMovie, используя один из его шаблонов — Блокбастер.
Уверен, Стивен Спилберг поставил бы ему палец вверх.
Веб-сайт
Frontend сайта сделан с помощью Boostrap 3. Серверной логики на backend’е нет, просто HTTP сервер Nginx. Расположено все это дело на DigitalOcean. Для развертывания я использовал Vagrant.
Разное
На проекте используется пакет Smart Localization (вы уже догадались для чего).
Для контроля версий я использовал Git. Исходный код хранится в Bitbucket.
Комментарии (24)
inborn_killer
27.04.2016 20:57+3Вспомнилось, как я в прошлом году участвовал в Ludum Dare.
Если научитесь делать свою игру за 48 часов вместо 4 месяцев, то трешачок оценят особые любители )))
FrozmatGames
27.04.2016 21:09+1На протяжении уровня, в каждом кадре < 5 тыс. вершин
Как можно 5000 вертексов наплодить в кадре с такой графикой?
«Коп» состоит из 6 кубиков, 6х8 = 48 вертексов максимум. Откуда там 200?)HeadZerg
27.04.2016 22:58«Коп» состоит из 11 кубиков, так как из-за особенностей текстурирования для каждого цвета нужна отдельная геометрия. Из-за плоского затенения (не знаю как правильно перевести flat shading) полигоны не делят между собой вершины, поэтому в кубе 6*4 вершин. 10 полигонов мы никогда не сможем увидеть, поэтому их можно удалить. В сумме получается (11*6 — 10)*4 = 224 вершины. 10 таких человечков плюс остальные объекты плюс интерфейс вполне может к 5000 вершин подобраться.
shell100500
27.04.2016 23:08На самом деле у копа там не так все печально и кубиков все-таки 6, а вершин 84.
5к это скорей порядок, чем реальное значение. В основном, в кадре до 2-3к вершин.Leopotam
28.04.2016 00:34А зачем вообще было использовать текстуры, почему не задействовать цвета вершин? Тем более в блендере можно мазать их прямо кисточкой.
shell100500
28.04.2016 00:43Увы я не знаком с данным приемом, не могли бы вы подсказать где об этом можно почитать? Это поддерживается unity? Также мне нужна возможность менять цвета объектов (например, одна машина красная, другая розовая), как быть в этом случае?
Leopotam
28.04.2016 00:57В блендере VertexPaint и красить любым цветом, потом экспортировать в fbx и в юнити. В юнити не помню стандартных шейдеров, умеющих показывать цвет из вертексов, но шейдер получается довольно простой (без поддержки освещения, это уже нужно прикручивать самостоятельно):
Shader "Custom/UnlitVertexColor" { Properties { } SubShader { Tags { "RenderType" = "Opaque" } LOD 100 Pass { Tags { "LightMode" = "ForwardBase" } CGPROGRAM #include "UnityCG.cginc" #pragma vertex vert #pragma fragment frag #pragma fragmentoption ARB_precision_hint_fastest struct v2f { float4 pos : SV_POSITION; fixed4 color : TEXCOORD0; }; v2f vert (appdata_full v) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.color = v.color; return o; } fixed4 frag (v2f i) : SV_Target { return i.color; } ENDCG } } Fallback Off }
нужна возможность менять цвета объектов
Сделать несколько префабов с разными мешами — они будут сбатчены в 1дк при использовании одного материала (цвета будут зашиты в вертексы).shell100500
28.04.2016 01:21Сделать несколько префабов с разными мешами
Если я правильно понял, это означает что на каждую вариацию цвета будет отдельная модель (блендер файл)? Но в таком случае это тяжелей поддерживать (например, саму геометрию модели нужно будет менять в нескольких файлах).Leopotam
28.04.2016 01:28Это значит, что будет N-файлов машинок, из которых наделать несколько префабов и спаунить уже из списка.менять цвет через renderer.material — нельзя в принципе, как и вообще трогать renderer.material — это приводит к дублированию материала и разрыву батчинга.
это тяжелей поддерживать
Это легко поддерживать, мешей машинок думаю будет не более десятка. Погуглить как работать с инстанцированием префабов из Resources + как сделать pooling объектов. Как вариант, посмотреть здесь и здесь.manto
28.04.2016 12:39— Таки можно использовать Renderer.sharedMaterial вместо Renderer.material.
— Можно и без использования окрашивания вершин обойтись, есть Renderer.material.SetColor (хотя изменяет цвет у всех 3D моделей, которым назначен данный материал).
— Но само текстурирование в стиле Hyden интересно тем, что uv-развертку можно ужать до минимума, уложив, например, в 32x32 пикселя и менее, а значит можно создать атлас текстур, и используя offset и tiling уместить в текстуру 512x512, получив аж 256 вариантов раскраски, как минимум, одного персонажа. В идеале одного такого атласа с набором большого количества цветов хватит на всю игру, при должном расположении островов uv-развертки различных моделей.Leopotam
28.04.2016 13:42— Таки можно использовать Renderer.sharedMaterial вместо Renderer.material.
— Можно и без использования окрашивания вершин обойтись, есть Renderer.material.SetColor (хотя изменяет цвет у всех 3D моделей, которым назначен данный материал).
Левой пишу, правой зачеркиваю — противоречащие абзацы. Топикстартеру нужна кастомизация отдельных инстансов, а не всех разом. Если потрогать material хотя бы раз — он скопируется из sharedMaterial и станет новым, те выпадет из батча. Это к сожалению неуправляемо и трогать это свойство крайне не рекомендуется.
Но само текстурирование в стиле Hyden интересно тем, что uv-развертку можно ужать до минимума
Оно не интересно абсолютно, ибо можно в данном случае повторить через цвета вертексов и оно не поломает батчинг в случае произвольной раскраски мешей любыми цветами радуги. Единственный полезный случай как раз — кастомизация путем подмены сета цветов на другой в разных картах.
HeadZerg
28.04.2016 08:39Скорее всего, 84 вершины — это то, что показывает blender. После экспорта их, почти всегда, будет больше. Например, для куба с жесткими гранями blender показывает 8 вершин, а unity — 24.
FrozmatGames
27.04.2016 23:39Из-за плоского затенения полигоны не делят между собой вершины
Откуда вы такое взяли?) В статье написано:
В игре только 1 направленный (directional) источник света и нет теней.
Это обычный свет и стандартные шейдеры. Геометрию на полигоны они не разбивают.Leopotam
28.04.2016 00:59Имелось ввиду — вертексы продублированы из-за необходимости получить острые ребра между гранями (по-разному направленные нормали вертексов).
BIanF
28.04.2016 03:53А что насчёт реплея геймплея?
shell100500
28.04.2016 10:49Увы на данный момент его нет, если вы имеете в виду replayability. Один из вариантов — ввести новых персонажей с уникальным поведением.
BIanF
28.04.2016 12:40Я про кнопочку «повторить последние 10 секунд игры»
shell100500
28.04.2016 12:51Простите, я не понял о чем идет речь. Вы предлагаете добавить возможность просматривать / расшаривать последние 10 сек игры? На данный момент есть возможность расшаривать видео после прохождения уровня, также предварительно редактировать его (можно оставить только последние 10 сек).
BIanF
28.04.2016 12:52Ну вот я и спрашиваю, как вы это сделали =)
shell100500
28.04.2016 12:54Это не мой велосипед, это я интегрировал один из сервисов unity — Everyplay.
Kirpa
05.05.2016 18:52Только сейчас почему-то прочитал статью. Релиз уже свершился? Спасибо за интересное чтение и полезные ссылки.
Leopotam
Спасибо за хорошее настроение, давно так не смеялся, до слез просто.