Мой magnum opus от мира мобильного гейминга

Это статья является перезаливом моей старой статьи о моей большой игре (это лонгрид и здесь очень много воды)

Мой magnum opus от мира мобильного гейминга

Важное предисловие

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

На эту попытку я потратил 9 месяцев и под конец я чувствовал только удовлетворение и желание поделиться с кем-то своим творением. Само творение я делал на голом энтузиазме, никаких денег с неё я зарабатывать не планировал изначально. Но какими бы не были мои побуждения что-то пошло не так.

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

Но 26 сентября, когда я опубликовал статью я ждал. Ждал день, 2 дня, неделю, 2 недели, месяц, 2 месяца но ничего я так и не дождался. Вместо этого я получил невнятный 1 плюс на статью.

Всем опять оказалось плевать. Сейчас я уже смирился, но желание хоть какой-то реакции меня не покидало очень долго. Я тогда впал в депрессию до Нового Года.

Но я отхожу от темы. Так зачем я перезалил свою статью? У меня есть 3 причины:

1. Я не хочу, чтобы мой труд пропал в небытие.

2. Я хочу, чтобы эта статья помогла хоть какому-нибудь начинающему разработчику

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

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

Привет всем! Сегодня, 26 сентября, мой день рождения, а значит для меня это отличный повод выкатить статью о сиквеле моей головоломки. Предупреждаю, что я любитель, а значит ошибок во ВСЕХ аспектах разработки будет не мало (если обнаружили напишите, я с радостью учту). В этой статье я хотел бы рассказать всё (ну или почти всё) про то, как я делал сиквел, как к этому шёл и к чему пришёл.

Чтобы вам не запутаться обозначаю здесь значения терминов, которые есть в статье:

Оригинал — первая часть, игра с подпольным погонялом «техно-демка». О ней можно почитать здесь.

Сиквел — вторая часть серии, игра, о которой идёт речь в этой статье.

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

Кратко о разработке

Начал я работу над игрой в конце января и к концу марта была закончена техническая часть (2 месяца). После я взялся за другую игру и к этой игре вернулся продолжить разработку в середине мая. Закончил я чётко под конец лета и всё это время (3.5 месяца) я наполнял игру контентом. И в итоге мною сиквел был сделан даже быстрее, чем оригинальная игра (6 месяцев против 5.5 месяцев).

Делал я игру на движке unity. Хотелось бы как эти парни сделать свой движок и продвинуться в программировании вперёд, но что-то пошло не так и всё-таки решил сделать игру на стандартном, но проверенном мною инструменте.

Между оригиналом и сиквелом

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

После релиза меня снова мучила идея сиквела. На этот раз не начал, потому что я морально обленился, после релиза совсем размяк. Мне хотелось чего-то нового и интересного. Начались массовые эксперименты.

Последующие 3 месяца любую идею, любой сеттинг, любой концепт я старался воплощать в играх. Я делал несмотря на масштабность амбиций, трудности исполнения, а также иногда несмотря на логику и здравый смысл. В итоге у меня получилось где-то 50 проектов. Они все были разных жанров: от шутеров до стратегий, от платформеров до бродилок.

Так бы и продолжались эксперименты, пока мне не надоело. И мне надоело не делать игры, а недоделанность сделанных мною игр. Я дал себе цель: сделать хоть какую-то игру за неделю до конца. Так и появилась моя вторая игра.

Эта игра очень простая и сложная одновременно. Необходимо резать линии и не резать графы. Смысл игры в том, что каждая порезанная линия делилась на 2, а в её центре появлялся граф. Фишка игры была в том, что вся геометрия была динамичной. Двигались графы, а линии всегда соединяли определённые графы.

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

Идея

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

Оказывается, существует очень похожая игра с почти идентичным названием. И она как выглядит как более удачная вариация моей игры. Узнал я о ней из этого видео.После я узнал, что эта игра эксклюзив телевизоров LG Smart TV. Её создало российское подразделение LG R&D Lab в 2014 году:

Управляется она стрелочками «влево» и «вправо» на пульте. Точно так же, как и в моей игре (2 части экрана). Что уж говорить, угол наклона один и тот же — 30°. Чисто технически, можно сказать, что моя игра является плагиатом этой. Хотя я узнал о ней примерно через 2 месяца после релиза первой игры.

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

Первое демо

Началось всё конечно же с него. Я решил: «Если и решать проблемы, то делать это основательно». И первой жертвой таких изменений стало управление. Я мог бы просто его украсть из похожей игры (смотрите выше). Это именно то управление, которое я изначально хотел, но не знал, как его сделать. А дополнить было бы довольно просто: просто добавь анимации вращения при каждом нажатии. Но это было не для меня. По крайней мере, что бы управление воспринималось так же хорошо, как и в похожей игре, нужно было сделать такую же статичную камеру и явно уменьшить уровни вместе с темпом игры. Но так хотелось экшн, динамику и скорость, поэтому я сделал логичное развитие управления оригинала. Теперь вместо нажатия и вращения на определённый градус было зажатие и градус итогового вращения определялся по его длительности. Выглядело явно лучше, чем в оригинале.

Именно из-за того, что я нормально совладал с управлением, исчез самый главный баг оригинала и теперь можно было делать уровни ГОРАЗДО более нагруженными чем в оригинале без боязни лагов и фризов. И дальше наступила экспериментальная часть.

Графическое демо

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

В тестах выдавал стабильные 60 fps, но на телефоне, даже на моём sony xperia был на отметке в 20 fps и проседал до 10 fps. И я упёрся в потолок производительности. Пришлось идти по другому пути, по пути разрушаемости…

Разрушаемость

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

Теперь разрушаемость базировалась на более простом принципе работы, а именно создавала копию себя, только из физических осколков, а оригинальный объект удалял у себя компоненты SpriteRenderer, Collider2D и, если имелся, отключал Rigidbody2D.

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

Также весомый вклад в оптимизацию дала правильная настройка параметра time fixedstep (она стала равна 0.0(3) или 30 в секунду). Но теперь на высоких скоростях объект пролетал его насквозь, и это точно сказалось на геймдизайне.

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

Система урона

Для меня это самая непонятная часть разработки, которая была переписана в итоге 2 раза. Работа над ней велась постоянно. В итоге вышла крайне замудрённая система, но довольно обширно работающая.

Но сначала стоит оговориться, как здесь работает восприятие урона. Может показаться, что оно работает по принципу «чем сильнее ударился, тем сильнее урон», но это не так. Оно в большинстве случаев работает по принципу «чем дольше соприкосновение — тем больше урон», где место такой важной вещи как «сила удара» заменилось коэффициентом урона, который был вручную настроен каждому объекту наносящий урон в зависимости от ситуации. Такое вышло из-за того, что time fixedstep оказался настолько большим, что создавался мощный баг: игра не успевала обрабатывать Enter2D. И это создавало ситуации типа: на большой скорости врезался — не получил урон. Почему это я не исправил? Даже я этого сказать не могу.

Итак, с чего началась система урона? Со здоровья. У игрока появилось здоровье равное 1 (позже увеличил до 2). Да, это всё ещё мало и при первом сильном касании с ловушкой он умрёт, но хотя бы при маленькой скорости есть вероятность выжить (даже несколько раз). Изменять оригиналу мне не хотелось. «Но что же будет наносить урон по игроку?» — подумал я и придумал основные ловушки.

Основные ловушки

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

Основной и первой была пила. Простая и понятная головоломка. Написана была не особо оптимизированно, в период постпродакшена исправил.

Мой magnum opus от мира мобильного гейминга

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

Мой magnum opus от мира мобильного гейминга

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

Мой magnum opus от мира мобильного гейминга

Взглянув на всю систему урона, я решил её основательно переписать. И в этот раз все возможные вариации урона уместил в один скрипт Damage, а блокам с разрушаемостью сделал похожий метод ActionPhysicsEmulation (под конец для каждого отдельного типа урона был написан свой оптимизированный метод). Также интенсивность урона определялась по интенсивности «силы» объекта (скрипт был только на игроке).

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

Силовое поле (отключает гравитацию, замедляет и медленно убивает)

Мой magnum opus от мира мобильного гейминга

Топот (он убивал игроков, давя их)

Мой magnum opus от мира мобильного гейминга

Радиация (которая потихоньку убавляет здоровье)

Мой magnum opus от мира мобильного гейминга

Ловушка (синий шарик, убивающий при касании, являющийся отсылкой на The World's Hardest Game)

Мой magnum opus от мира мобильного гейминга

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

Дополнительные механики

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

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

Мой magnum opus от мира мобильного гейминга

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

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

Мой magnum opus от мира мобильного гейминга

Как это работает? Сначала по теореме Пифагора вычисляется гипотенуза, которая является коэффициентом вектора и пригодиться для восстановления силы. Дальше вычисляется угол при помощи функции Atan2. После к углу прибавляется offsetAngle и строится новый вектор на основе синуса и косинуса, который умножается на коэффициент и получается изменённое направление без измененной силы.

public Vector2 RotateVector(Vector2 a, float offsetAngle) { float power = Mathf.Sqrt(a.x * a.x + a.y * a.y); float angle = Mathf.Atan2(a.y, a.x) * Mathf.Rad2Deg - 90f + offsetAngle; return Quaternion.Euler(0, 0, angle) * Vector2.up * power; }

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

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

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

Мой magnum opus от мира мобильного гейминга

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

Физическая тематика игры позволила создать батут, который обычно используется для разгона игрока с последующим разрушением стены (хотя это простой BoxCollider2D с PhysicsMaterial, у которого был подкручен параметр bounce для разной силы отскакивания).

А песочность игры позволила создать собственные скрипты для анимации. В основном они двигали объект от точки к точке или вращали объект. Раньше у них было значительно больше функций: возможность анимировано (по точкам) вращать объект, изменять scale (по точкам), более точные метки для начала и конца анимации объекта и т.д. Но в силу того, что это были атавизмами, которые в сумме неистово жрали производительность, мне пришлось их выпилить во имя бога оптимизации. Скрипт анимации используется везде, где нужно показать простую анимацию, ведь как я говорил: «Оригиналу очень нехватало анимаций!».

UI

По сравнению с оригиналом это настоящий шедевр.

Для сравнения, вот оригинал:

Мой magnum opus от мира мобильного гейминга

Вот сиквел:

Мой magnum opus от мира мобильного гейминга

Вот оригинал:

Мой magnum opus от мира мобильного гейминга

Вот сиквел:

Мой magnum opus от мира мобильного гейминга

Вот ориг… думаю понятно. Минимализм в сиквеле я довёл до ума и вместо неуместно раскрашенной кнопки паузы и откровенно мешающего таймера теперь локаничная кое-как заметная кнопочка паузы в левом нижнем углу. В меню всё ещё побеждает сиквел. В отличии от оригинала здесь есть анимации повсюду, а задний фон это 11 случайно мною написанных шейдеров в Shader Graph. По функциональности тоже всё лучше, есть настройка графики, раздельная настройка звука и музыки, консоль, которая позволять менять сохранения — ничего этого в меню оригинала нету.

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

  • Меню проигрыша => Взято из Alto's Adventure, только переживания превратились в насмешки, шутки, ироничные комментарии и т.д.
  • Пауза => Тоже из Alto, только не так функционально, но в стиль вписывается и играть удобнее.
  • Настройки => Частично взяты с Vector2, а именно форма меню и ползунки громкости. Немного взял в общем, а в остальном всё сделал самостоятельно.

Консоль

Сначала стоит оговориться о том, как работают сохранения. Существует две переменных отвечающие за глобальные и локальные сохранения: это числа progress и elevatorsave соответственно. Переменная progress отвечает за сохранения между сценами, а переменная elevatorsave за сохранения внутри сцены. При нажатии кнопки «Старт» или «Заново» игра переносит на сцену progress и спаунит игрока на сохранении под номером elevatorsave.

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

И специально для вас я оставлю список ликвидных команд в ней:

  • discharge — сбрасывает игровой прогресс (и всю остальную информацию тоже)
  • echo vertogpro — команда для предоставления доступа к разработческим командам
  • playerprefs [тип данный (string, int, float)] [имя переменной] [данные] — меняет или создаёт любую переменну. Пример: playerprefs int progress 14
  • next — подтип для упрощённой навигации по уровням, со своими командами: start — сохраняет в начале уровня (next start), end — сохраняет в конце уровня (next end), save — телепортирует на следующее сохранение (next save), level — телепортирует на следующий уровень (next level)

Графика

За год я так и не научился рисовать, поэтому я сделал почти то же самое, что и в оригинале: скачал около 30 текстур-паков к майкрафту, отобрал лучшее из каждого и так получилась основная графика. От оригинала графика отличалась не сильно и это меня бесило, бесило настолько, что я ещё нашёл разные анимированные эффекты (взрывы, огонь и т.п) и накачал разнообразных текстур-паков из asset store. Даже для мобильной игры графика довольно плохая, хотя прогресс всё равно наблюдается. Вот оригинал:

Мой magnum opus от мира мобильного гейминга

А вот сиквел:

Мой magnum opus от мира мобильного гейминга

Сохранения

Если принцип у сохранений простой, то их реализация не очень. Система сохранений состоит из 3 скриптов:

  • ElevatorBase — основа, в которой происходят стартовые команды. В ней по переменной elevatorsave из массива сохранений выбирается активное сохранение.
  • Saving — сохранение, при подаче на него сигнала он создаёт игрока в определённой позиции и, если сигнала нету, то при соприкосновении игрока с триггером сохраняет в elevatorsave свой id.
  • Elevator — то же сохранение, только с анимацией перемещения на уровень. Из дополнительного функционала: возможность в него зайдя попасть на любой уровень и сохранение (в том числе и на прошлые уровни).

Игровой дизайн

Это была настоящая морока. Именно игровой дизайн растянул цикл разработки с 4 до 6 месяцев. Всего в игре 34 уровня: 30 обычных, 3 босса и 1 финальный (уровень). Каждый обычный я делал 2-3 дня, каждого босса 2 недели и финальный уровень делал неделю. Что бы сбалансировать это всё, я выстроил их так: 10 уровней => 1 босс => 10 уровней => 2 босс => 10 уровней => 3 босс => финальный уровень.

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

Мой magnum opus от мира мобильного гейминга

Карта не лучшей рисовки и информативности, но она дала важную информацию для необходимых форм уровней. Изначально в планах было сделать все уровни на карте, но те, которые затемнены я так и не сделал. Кстати, это карта размером в 1000x1000 пикселей, и именно из этой карты получился масштабу: 1 блок = 1 пиксель = размер игрока.

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

Для обычных уровней был свой алгоритм построения:

Задний фон, что бы имел форму и масштаб как на карте

Мой magnum opus от мира мобильного гейминга

Стены наружные (имеют тройную толщину из-за особенной физики)

Мой magnum opus от мира мобильного гейминга

Стены внутренние

Мой magnum opus от мира мобильного гейминга

Сами уровни

Мой magnum opus от мира мобильного гейминга

Лифты, сохранения и аудио триггеры

Мой magnum opus от мира мобильного гейминга

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

1 босс очень прост в поведении: рандомно передвигается по помещению, ждёт 5 секунд и повторяет. Если честно, это плохой пример босса: простой, лагающий и не запоминающийся. И его можно убить только долбясь об него. Но есть защита в виде 4 пил: 3 из них шустро движуться рандомно по помещению и одна защищает босса, когда он движется. После смерти он взрывается (блин, я так взрыв и не починил)

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

  • 2 пилы на расстоянии
  • 2 пилы на расстоянии, при движении защищается пилой
  • 2 ограниченных в длине лазера, при движении защищается пилой
  • 2 лазера, при движении защищается пилой
  • 2 лазера, при движении защищается пилой и 2 пилами на расстоянии

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

3 босс является самым лучшим в качестве среди боссов! Для передвижения он использует raycast'ы. Сначала он рандомно вращается на любой угол, потом среди 12 raycast'ов, запускаемые в разные стороны, выбирает самый длинный и летит на точку raycast'а. На уровне есть объекты, некоторые из которых также разрушаются. И как raycast'ы босса реагируют на объекты? К статичным объектам были добавлены триггеры, которые в 2 раза больше самих объектов, чтобы у raycast'а была точка, перелетя на которую босс не будет висеть в воздухе, не будет в стене, а будет как-бы приклеплён к стене. У босса есть особенная защита: в начале уровня с боссом (каждый босс — это отдельный большой уровень без сторонних головоломок) есть триггеры, и они поставлены так, чтобы был активирован только один. У босса есть 5 заготовок ловушек и каждый триггер оставляет активным только 3-4 ловушки. А также у него была усовершенствованная система областей, которая заключалась в заранее заданных областях для каждой области (в которой игрок может быть) и для каждой ловушки. И во время полёта босс всегда убивает игрока.

Список ловушек:

  • Лазер в центре, который после каждого раза как стартует в полёт босс, начинает смотреть на игрока
  • 2 лазера, которые при помощи функции Lerp двигаются в заданные области (в зависимости от местоположения игрока) и перед движением направляются на игрока (они должны были быть всегда наперёд игрока, но что-то пошло не так)
  • Пила, которая всегда направляется к той же области, где и игрок
  • 2 пилы, которые всегда направляются к левой и правой области от области, где и игрок
  • 4 шара-ловушки, двигающиеся симметрично центра

Аудио и музыка

Музыку я тоже писать не умею, но у меня достаточно музыкального вкуса, чтобы подобрать подходящую музыку. В моём плане для каждого уровня нужно было подобрать по треку. И план я по большей части выполнил: подобрал 25 треков. Все треки искал в asset store. Звуки для остального брал уже на freesound.org или подобных сайтах.

Звук с технической части был сделан по простому принципу: на камере находилось 5 отключённых AudioSource и скрипт AudioBase для управления звуком. В этом скрипте была основная функция SetSound с параметрами громкости, зацикленности, типом (музыка или звук) и самим аудиофайлом. После вызова звук начинал проигрываться и (если не зациклено) включался IEnumerator со временем, равным длине трека и по его истечению он отключал компонент.

Сюжет

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

В игре есть 3 выбора: на первых двух боссах и на уровне 32. Выбор с боссами довольно очевиден: их можно убить или нет путём начала атаки или выхода на следующий уровень соответственно. А на уровне 32 немного посложнее: можно активировать триггер, подразумевающий под собой пробуждение местного сюжетного спасительного якоря (персонаж под именем ИИ). Выбор на первых двух боссах влияет на то, будет ли битва с 3 боссом. Если хоть одного из первых двух боссов убить, битва с третьим боссом будет. Если нет, то нет. Концовок всего 4: хорошая, плохая, нейтральная и секретная. Спойлерить я ничего не буду ибо такой бред надо лицезреть лично.

Но почему же сюжет почти невербален? Полностью невербальным я не смог его сделать из-за концовок. Но в игре текста хватает. Ведь, чтобы объяснить игроку «за лор игры» в игре появились терминалы с записками и в них очень подробно объясняется сценарий игры.

Сценарий

Сценарий в данном случаи является предысторией мира, раскрытая от лиц и персонажей этой игры в виде записок, логов, отчётов, монологов и диалогов: в общем текстом. И это настолько графоманский бред программиста, что даже Глуховский удивился бы (ничего против него не имею, люблю Метро). К сожалению, у меня времени было не так уж и много на создание полноценных npc. Хотя спрайты для них в игре я нашёл:

Мой magnum opus от мира мобильного гейминга

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

Если что, в оригинальной игре нет сюжета и никаких намёков на него. И сейчас мне нет смысла скрывать сюжет (ведь никто полностью не пройдёт игру и не прочтёт все записки). Цели у этой графомании три: добавить обоснованную вариативность действий игрока, объяснить необъяснимые игровые вещи и хоть немного сильнее заинтересовать игрока своей игрой.

Сценарий я писал очень простым методом: сначала за 2-3 недели расписал его в рассказ на 40-50 предложений. Потом для каждой записки я выбрал по предложению, и уже исходя из одного предложения я дописывал к записке по 2-3 предложения, менял их на монологи (или другие формы повествования) и получал готовые сбалансированные записки. В итоге от такого приёма во всех записках суммарно набралось где-то 160 предложений с информацией.

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

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

Кодовая база

Поскольку моя специальность — это программист, то и код был для меня основной задачей. По сравнению с кодовой базой оригинала, кодовая база сиквела возросла в 2-3 раза (даже несмотря на то, что в оригинале есть методы на 900 строк кода, так как я тогда боялся использовать такие связки как циклы и массивы или GetChild() и циклы).

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

public class VelocityRotate : MonoBehaviour { public float rotate = 0f; public bool oneTime = true; private bool active = true; public void OnTriggerEnter2D(Collider2D collision) { if (active == true) { if (oneTime == true) { active = false; } Rigidbody2D rb = collision.GetComponent<Rigidbody2D>(); Vector2 vel = rb.velocity; rb.velocity = RotateVector(vel, rotate); } } public Vector2 RotateVector(Vector2 a, float offsetAngle) { float power = Mathf.Sqrt(a.x * a.x + a.y * a.y); float angle = Mathf.Atan2(a.y, a.x) * Mathf.Rad2Deg - 90f + offsetAngle; return Quaternion.Euler(0, 0, angle) * Vector2.up * power; } }

Вы быстро поняли, за что отвечает этот скрипт? А если его сделать таким:

public class VelocityRotate : MonoBehaviour { //Скрипт для вращения силы физических объектов public float rotate = 0f;//угол вращения public bool oneTime = true;//одноразовость использования private bool active = true;//активность скрипта public void OnTriggerEnter2D(Collider2D collision) { if (active == true) { if (oneTime == true)//проверка на одноразовость { active = false; } //изменение направления объекта Rigidbody2D rb = collision.GetComponent<Rigidbody2D>(); Vector2 vel = rb.velocity; rb.velocity = RotateVector(vel, rotate); } } public Vector2 RotateVector(Vector2 a, float offsetAngle)//метод вращения объекта { float power = Mathf.Sqrt(a.x * a.x + a.y * a.y);//коэффициент силы float angle = Mathf.Atan2(a.y, a.x) * Mathf.Rad2Deg - 90f + offsetAngle; //угол из координат с offset'ом return Quaternion.Euler(0, 0, angle) * Vector2.up * power; //построение вектора из изменённого угла с коэффициентом силы } }

Отсутствие комментариев моя самая первая и по-настоящему самая большая ошибка при разработке игры! Во всей её кодовой базе нету ни одного комментария, поясняющего за что та или иная ветвь кода отвечает. И, возможно, для маленькой инди-игры это и не нужно. Ну во-первых, эту игру я точно не могу назвать маленькой, а во-вторых, я как будущий разработчик чего-либо обязательно должен буду работать в команде и отсутствие такой полезной привычки как комментирование когда-нибудь сыграет со мной злую шутку. Эту ошибку я только сейчас понял: она меня преследовала все мои проекты, связанные с программированием и на этот раз, я это принял во внимание и в следующий раз буду делать комментарии. Так что пишите комментарии. ПИШИТЕ КОММЕНТАРИИ.

Баги и недоработки

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

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

Так какие же я вижу главные недоработки?

  • Однозначно самой главной недоработкой можно считать новый способ управления. Это простой джойстик, который менял направление и силу гравитации в зависимости от своего положения. Он появляется после 2 босса и этому способу управления посвящены 3-4 уровня. И пускай это вносит разнообразие, пускай это добавляет новые ловушки, пускай это обоснованно сюжетно: геймплей был изначально заточен под другой темп и такой эксперимент портит картину на последних 10 уровнях. Конечно же я это понял не сразу же, но когда пришло осознание ошибки я судорожно переделал многие уровни и поменял в них тип управления на привычный. Но новое управление всё же осталось в игре в напоминание моей не дальнозоркости.
  • Ещё одной своей недоработкой я считаю основное управление, а точнее его следствия. Дело в том, что упралением по методу вращения трудно управлять гравитацией, следовательно и объектами то же, из чего следует, что удобство и точность оставляют желать лучшего.
  • В сиквел я изначально добавил очень много спрайтов. И очень много из них я использовал, но это «очень много» всего лишь 60% от всех спрайтов в игре. И если бы я использовал их все, графика получилось бы лучше.

Локализация

Из-за полноценного сценария объем локализируемого текста подрос примерно в 30 раз. А вот методика перевода ни капельки не изменилась: как переводил через Google Translate, так и продолжаю. Только сначала я переводил прямо с русского, а теперь перевожу на английский, исправляю ошибки и уже с него на другие языки. Также количество языков сократилось: если в оригинальной игре было 18 языков, а её страница была переведена на ВСЕ языки, которые google поддерживал, то сиквел был передён лишь на 10 языков: что в игре, что и на странице (и это единственное в чём сиквел уступает оригиналу).

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

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

Релиз

Изначально в моих планах был выложить игру 1 сентября. И я так и сделал: в последний момент оказалось, что у меня 4 бага в концовке (а также она была не переведена), быстро исправил и под вечер выложил игру. К сожалению, проверка затянулась на 7 дней, ведь у меня предложение с чего-то решили проверить вручную. Скорее всего дело в аккаунте, который стал «определённым» и его уже проверяет модерация тщательнее.

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

Мой magnum opus от мира мобильного гейминга

Итог

Удивительно, но именно в день, когда эту статью я выложил, я пробыл в IT уже 3 года! И несмотря на свой 16-летний возраст, именно в этот день, когда мне исполнилось 13 лет, я поставил себе цель: выучить программирование и создать игру мечты. И с того момента в какой-то мере моя мечта исполнилась.

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

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

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

Послесловие

Когда я писал эту статью я искренне верил, что она может кому-то пригодиться. Я горел, горел настолько ярко, что сам не замечал до чего это меня довело. 9 месяцев я провёл как будто в трансе, воплощая мечту детства, которую никто не заметил. Я не чувствую, что это плохо, но я чувствую, что это будто всё было зря.

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

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

Надеюсь было интересно. Всем добра!

P.S: зачем-то я сделал трейлер игры под трек больше подходящий для киберпанка чем для этого

мне сейчас так стыдно делиться этим...
1616
12 комментариев

Комментарий недоступен

6
Ответить

Комментарий недоступен

1
Ответить

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

5
Ответить

Ну, добро пожаловать в мобильный геймдев.

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

Совет на будущее - свою игру надо начинать продвигать в массы и собирать лояльную аудиторию ДО того как ты её выпустишь в стор, а не писать статьи ПОСЛЕ. 

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

P.S. забудь про написание собственного движка и изучай инструментарий Unity/Unreal/Godot или любого друго крупного коммерческого движка.

4
Ответить

собирать лояльную аудиторию ДО того как ты её выпустишь в стор, а не писать статьи ПОСЛЕ. А что делать с чувством "ещё рано что-то показывать, ещё ничего не готово"?

1
Ответить

Спасибо. Твоё сообщение наполнило меня решимостью

Ответить

Честно: лонгрид не прочитал, ограничился вступлением и заключением.

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

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

Ответить