0pacec

На Пикабу
поставил 0 плюсов и 0 минусов
178 рейтинг 10 подписчиков 0 подписок 5 постов 3 в горячем

Опыт разработки первой игры на Unity, часть 5

Или о том, как потерять мотивацию и вернуть ее обратно

Опыт разработки первой игры на Unity, часть 5 Gamedev, Инди, Unity, Разработка, Indiedev, Видео, Длиннопост

Ссылка на часть 1

Ссылка на часть 2

Ссылка на часть 3

Ссылка на часть 4

Предисловие

Появляются иногда комменты, мол, если хочешь показать, что можно сделать игру с нуля, то гораздо лучше сначала сделать — и потом рассказать. Возможно. Но мне кажется, что, увидев именно трудности и то, что с ними можно справиться, эффект будет сильно выше, чем от простого “Я, который не умеет программировать и прочее, представляю вам готовую игру!”. Тут видно, что технические навыки низки, видно, что рисовать я не умею — ну и так далее, — и видно, как справляюсь с этими трудностями. Мне кажется, от такого эффект будет сильнее — наверняка многие сталкивались и будут сталкиваться с такими же проблемами, которые кажутся непреодолимыми)


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


Итак, поехали!

Введение

Хотел сделать классическую покадровую анимацию. Но иногда нужно трезво оценивать свои силы — а рисовать хоть и было бы прикольно, но довольно сложно. У меня есть сроки, хочу в них уложиться. Что же мне делать тогда? Вот был бы способ сделать анимацию, не рисуя по 12 кадров одного и того же персонажа… Ой, что же это? Это птица? Это самолет? Нет — это spain animation! Та-даннн!


Наткнулся на такое видео — начал изучать, что да как

Поизучав еще немного, решил стать павер-металлистом сквозь огонь и пламя… Ой — использовать Dragonbones. Вроде подходит + важный аспект — она бесплатна. В ней есть некоторые проблемы — например, искажение объектов, — но с моим скиллом рисования это не сильно решающий фактор. Так что такой размен посчитал разумным.


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


Еще рассматривал стандартный Unity 2D Animation, но, насколько понял, пока что он несколько недоработан.


С анимацией разобрались — но нужен исходное изображение, которое будем анимировать. Сначала хотел использовать Gimp, но внезапно узнал, что он не для рисования, а для обработки фоток. А для рисования используется некая Krita.


Вообще, есть три варианта:

1) Рисовать самому

2) Найти того, кто нарисует

3) Взять готовые ассеты


Раз определился, что рисую сам — поехали!

И получается плохо! Вообще не понимаю, что тут я делаю. После первой неудачи всерьез задумался над поиском того, кто поможет. И это правильный подход, так и нужно делать. Но потом вспомнил, зачем я вообще все это затеял — развлечься и повеселиться! А что будет интереснее? Нарисовать 5 героев — это сложная и, главное, интересная задачка! Достаточно интересная, чтобы продолжить пытаться сделать самому


Для начала попробовал продолжить решать ее с наскока — результат немного предсказуем:

И я понятия не иммею, что делать дальше! Тут началась чуть-ли не паника: “У меня не получится”, “Слишком сложно” — все вот такое. Помог, как ни странно, сон. С новыми силами вернулся к задаче. Теперь подготовился получше — нашел вот такую серию видео:

https://youtube.com/playlis? list=PL1ypg9-tKyc28he9qTdlsEjjG1KPmd1hs

И, когда смотрю, все вроде как понятно. Но стоит начать делать самому — как полная пустота в голове! В итоге цикл получился таким: Просмотр видео — возвращение к работе — просмотри видео — возвращение — ну и так далее.


Результат за 4 часа выглядит ну очень плохо — такое сильно расстраивает. Но я просто напомнил себе, что невозможно получить хотя бы немного хорошие результаты за 4 часа работы с "нулевого уровня навыка".


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

Но тут случилось нечто непредвиденное.


Помните, однажды говорил, как важно видеть прогресс для сохранения рабочего настроения? Так вот, за 20 или 30 часов работы, 4+ из которых записывал на видео, прогресса я не увидел. Учитывая вкладываемые усилия… Ну, это привело к закономерному результату — я потерял интерес. Причем не только к рисованию этого персонажа, но и к проекту вообще.


Штош, арт я не вытяну — это очевидно. Значит, нужно найти либо спецов, которые помогут, либо использовать готовые решения (asset store, например). Узнал, что нарисовать одного героя (только нарисовать — не анимировать!) стоит ориентировочно 10к. Зато купить готовый набор героев можно примерно за 5к.


Проблема “нарисовать героя” трансформировалась в “найти героев” — и отодвинулась на будущее.


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

Оно живое! Живое!

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

При нажатии валюта тратится на нужную стоимость (что я вообще сказал?) и призывает нужное количество героев. Если валюты не хватает на призыв… Не повезло, что тут сказать. Иди фарми ее.


Но как-то некрасиво выглядит — просто тыкаю по кнопке и циферки меняются. Давайте исправим. Вжух! Немного магии, и…

Тут есть два варианта отображения полученных героев:

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

2) Как сделано в AFK Arena (каждый призванный герой отображается в виде отдельной карточки)


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


Герои получены — но вот незадача! Я же не сделал место, откуда открывается интерфейс с призывом. Давайте это поправим


Помните мою секретную кнопку в выборе уровней? Нет? Не страшно — сейчас напомню. Именно там пока будет открытие окна призыва. Вжух!

Хм, я все еще старательно сохраняю интригу на счет меты, так что давайте сделаем вид, что вы ничего не заметили!

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

Вы это видели? Для доступа к следующему уровню игроку теперь нужно пройти текущий — это все больше похоже на настоящую игру! Щитаю, это шедевр, не иначе.


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

Это птица? Это самолет? Нет — это же код!

О да, я знаю, многие ждали этого!


Начнем с экрана призыва. В новом скрипте все разделено на 2 блока:

1) Показать героев, участвующих в битве

2) Создание самих кнопок


С показом героев ничего сложного:

https://pastebin.com/Bkqvizvn

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


Со спавном кнопок тоже не возникло проблем

https://pastebin.com/5bBDwWb5

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

https://pastebin.com/JVs8zi9h

Видимо, книжек прочитал недостаточно — раз тут какая-то 5 затесалась. На самой же кнопке висит... Барабанная дробь...

https://pastebin.com/drSMNXxp

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


Ну а с покупкой все не так радужно. Я понятия не имею, как избавиться от кучи GetComponent

https://pastebin.com/iCsBDTRC

Итак, призыв готов. Но что с изменением уровней? Как работает «Level complete» и «Пройди предыдущую битву»?


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


У меня есть переменная maxLevel, от которой зависит количество уровней (неожиданно, да?). Используя ее, могу определить, на каком "слое" находится нужная кнопка-уровень — причем не важно, где в массиве она находится. Так что добавляю в кнопку смену цвета (вот это логическая связь, просто нет слов)

https://pastebin.com/nDjZq1Fh

Меняю функцию по включению нужного уровня

https://pastebin.com/wM2BPeXK

И добавляю функцию для «открытия» следующего уровня

https://pastebin.com/kNnG88hC

В итоге все это работает. Пока под вопросом, как резво — но работает!

Маленький шажок навстречу неизвестности

Уф, это было сложно. Браться за что-то с нулевым навыком — то еще испытание, считаю, что потратил очень много времени безо всякого результата. Если бы я видел, что процесс хоть и медленно, но движется, такого удара по мотивации я бы не получил. Из-за этого на игру стало абсолютно все равно — продолжать работать не было никакого желания.


Следование ранее намеченному плану работ и декомпозиция задач на самые минимальные помогло влиться обратно (это когда задачу разбиваешь на более маленькие — у меня все свелось к “а теперь задача — добавить на кнопку текст с количеством призываемых героев”).

Благодаря этому вновь начал ощущать прогресс, да и сам факт выполненных задач влияет очень позитивно - неважно, насколько они мелкие.


А в следующей части будет то, чего всеми силами старался избежать — добавление функционала сверх того, что запланировал изначально. К сожалению, без него получается не игра, а технодемка игры. Что это? Узнаете в следующей части!

P.S Как это тут принято - бот ругается на Stonks

Показать полностью 1 8

Опыт разработки первой игры на Unity, часть 4

Или о том, как я обманываю читателей

Опыт разработки первой игры на Unity, часть 4 Gamedev, Инди, Unity, Indiedev, Видео, Длиннопост

Ссылка на часть 1

Ссылка на часть 2

Ссылка на часть 3

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

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

Поэтому перед повышением уровня нужно сначала сделать выбор участвующих в битве героев

Подготовка

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

Опыт разработки первой игры на Unity, часть 4 Gamedev, Инди, Unity, Indiedev, Видео, Длиннопост

1) Снизу экрана должны отображаться все имеющиеся у игрока герои

2) Сверху - просто расположение выбранных героев на поле битвы

3) Хочу кликнуть по герою - и чтоб он появился сверху

4) Еще хочу уметь перетаскивать героев сверху на разные позиции

Реализация

Пыщь-пыщь - немного магии - и готово.

Упс - что-то пошло не так. Лезем обратно в код - и получаем вот такое чудо:

Дело нехитрое, но как же ужасно выглядит! Давайте договоримся: вы сделаете вид, что не замечаете интерфейс. Под UI / UX выделен отдельный пункт плана работ - и какой же кошмар меня ожидает! Тут можно заметить, что весь экран по центру разделен на две части: с квадратами и - вот неожиданность - другими квадратами (причем снизу кнопки). Итак, что тут:

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

Трудности - куча их!

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

В Unity можно сделать "выключенные" объекты - они как бы есть, но движок их не обрабатывает (соответственно, игрок их не увидит). Был вариант “сделать сюда кучу героев и деактивировать их. А потом в нужный момент просто активировать нужного". Спасибо за генерацию идей - сказал я себе, - и принялся думать дальше. В итоге сейчас просто нужному объекту присваивается спрайт нужного мне героя. Уии, магия!

Осталось немного - нужно уметь менять героев местами перетаскиванием в разные точки. Сделал 1 в 1 как в этом видео:

https://youtu.be/BGr-7GZJNXg

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

Выгорание, ты ли это?

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

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

Как итог - я так и не придумал, как сделать так, чтобы герои менялись местами при перетаскивании одного на другого. Подозреваю, это не просто. Скорее всего, это очень просто. Беда в том, что это важный UX - элемент, без которого игрок будет чувствовать много боли. И оставлять это недоделанным - такое себе…

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

Ох и нагнал я негатива. Да, было не очень комфортно - но гляньте на результат! Настоящая магия!

А как работает, семпай?

А теперь ваша любимая часть! Сердце сего шедевра, его мозг. Путь, по которому движется сей самурай. Движок, бьющийся… Ладно, ладно, прекращаю. Встречайте: то, от чего у программистов появляется непреодолимое желание взять учебник по языку и дать его почитать - код!

Правда, никаких неординарных задач тут нет

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

https://pastebin.com/XHmiYBS4

И... Это все xD

Заключе… Ох, стоп. Это что, продолжение?

Воу, статья еще не кончилась?

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

Для повышения уровня рассматривал несколько вариантов:

1) Герой получает опыт при каждом убийстве противника. Максимально приближенный к “большим” РПГ игровой опыт


2) Герои получают опыт только после победы над каждой волной противников


3) Герои получают опыт только после победы над всеми противниками

Изначально хотел сделать первый вариант, но остановило то, что герои будет увеличивать уровень чуть ли не после каждого убийства. А при повышении восстанавливается здоровье. Они же не убиваемыми получатся! Это можно решить, назначив требованием к level up "получить 9000 опыта", но я хочу игрока награждать почаще. Остальные варианты в своей сути одинаковы.

К чему это я? Остался последний штрих перед повышением уровня - игра должна знать о нашей победе или поражении. Иии… Тут без сюрпризов: добавить UI панели - разместить нужные картинки и текст - вжух-вжух - и готово!

Решил не делать красивую анимацию “перетекания” полученного опыта в героя (чтоб красиво так повышался уровень). Пока просто отображает, сколько опыта герой получил за битву. Чуть не забыл! Выбранные в битву герои сохраняют свои позиции даже после битвы - красота.

Добро пожаловать в школу программирования

И вновь - попытка поехать на велосипеде с помощью костылей и какого-то чуда

Вот. Вот оно - то, с чем я возился больше 10 часов. И я не шучу. В поисках этого решения я перерыл весь интернет. Вы готовы?

if (_battleStarterScript.ActiveEnemies.All(ActiveEnemies =>

ActiveEnemies.GetComponent<Characteristics>().IsDead))

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

А больше ничего интересного и не было. Хотя нет - я понял, что с моим "переключателем сцен" (который пока что просто включает/выключает объекты) нужно что-то делать. Сейчас это что-то жуткое, в котором наделать баги проще простого. Ну вы видели в предыдущей части, что у меня там. А сейчас туда добавился экран выбора героев и экраны победы с поражением.

Штош, на этом все. Хах, ладно. Обещал сделать прокачку герев - будет прокачка героев.

Это же значит, что теперь я не обманываю читателей!

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

И я решил не париться от слова “совсем”. Это решение наверняка плохое, но в дальнейшем я, скорее всего, от него избавлюсь. А пока… Решил использовать struct. Первый struct хранит характеристики на первом уровне. Второй - на сотом. Все значения между ними по задумке будут высчитываться интерполяцией.

Почему не сделать грамотно (например, сделав на устройстве файлик с этими значениями) и подтягивать из него? А все просто - еще не время разбираться в этом (ну и мне лень, чего уж там). Опять же - в дальнейшем struct наверное пропадет, а эти же данные будут подтягиваться из таблиц.

План определен - поехали

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

Делаю колдунство с таблицей - и все вроде как нормально.

Нужно разобраться, что мне вообще делать:

1) Получить список характеристик (как раз struct подготовил)


2) Сделать так, чтобы за битву давали опыт в зависимости от противников


3) Повышать в зависимости от полученного опыта уровень героя


4) И находить соответствующие уровню характеристики

Для первого пункта делаю вот так

https://pastebin.com/0WXvrwma

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

Пункт 2

Внезапно стало легко определить, сколько опыта выдавать героям игрока - просто перемножаем количество опыта за противника на число противников:

https://pastebin.com/vNN7Xwme

По поводу for... Честно - понятия не имею, почему при стандартном i++ у меня остается один активный объект.

Операция "Повышение"

Ой, а я же уже показал. Вычисляется, сколько уровней герой может получить в зависимости от полученного опыта. Затем уровень присваивается, а оставшийся остаток от деления становится "текущим опытом". Интересна тут функция hero.GetComponent<Characteristics>().SetNewLvl(newLevel);, которая приводит нас к...

Свободная касса!

Итак - проблема. Мне известен набор характеристик на первом уровне героя. Известен набор характеристик на 150 уровне героя. А тут, внезапно, понадобилось узнать параметры героя на условном 38 уровне. Как это сделать?

Можно попробовать через for. Это будет чуть проще, чем через if или switch. Но, хоть я тот еще извращенец, к таким подвигам не готов. Зная пограничные значения, можно высчитать то, какие значения будут в любом месте между границами. Не буду томить - мне подсказали вот такую замечательную формулу:

https://pastebin.com/BwdAqqGx

Функцию придумал уже я - и она 100% поменяется. Мне крайне не нравится, что приходится вручную указывать пограничные для значения структуры.


Ах да, думаю, вы уже успели отдохнуть от надругательства над беднягой c#. Не переживайте, подергивающийся глаз от встреченного Stats stat вас не обманул - это именно то, о чем вы подумали:

https://pastebin.com/TAyB4zYN

И так на каждый параметр. Я так и не придумал, как избавится от сравнения (хотя на 100% уверен, что можно).

Зато гляньте, какое чудо получается!

Магия! Это все больше и больше становится похоже на игру! Ну разве не чудо?


Заключение

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


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


И я тут подумал... Вам не кажется, что битва квадратов с кружочками - это совсем не серьезно?

Опыт разработки первой игры на Unity, часть 4 Gamedev, Инди, Unity, Indiedev, Видео, Длиннопост
Показать полностью 2 5

Опыт разработки первой игры на Unity, часть 3

Ссылка на часть 1

Ссылка на часть 2

Ошибка планирования

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


Так что в этой части будет смена уровня вместо прокачки героев.

Взаимоисключающие цели

Вот какая штука. По моей задумке хочу сделать следующее:

1) Сделать выбор следующей битвы так же, как в AFK Arena сделан мистический лабиринт.

Опыт разработки первой игры на Unity, часть 3 Gamedev, Indiedev, Инди, Unity, Видео, Длиннопост

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

2) Сделать выбор следующей битвы максимально простым.


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


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

Грамотно озвученная проблема — часть решения

Хмм, в лабиринте игрок принимает решение… Делает выбор… Планирует маршрут… Стоило только озвучить проблему — тут же пришло в голову решение. Сочетает и простоту восприятия, и предоставляет игроку выбор, игрок ощущает, что он принимает решение. Встречайте!

Опыт разработки первой игры на Unity, часть 3 Gamedev, Indiedev, Инди, Unity, Видео, Длиннопост

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


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

Объекты или UI?

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


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


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

Завершение биты — переход на экран выбора — начало битвы

Есть два варианта:


1) Битва и экран карты— две отдельные сцены.

2) Используется только одна сцена — но при «переходе» активируются / деактивируются соответствующие объекты.


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

Переделка архитектуры объектов на сцене

Суть вот в чем. Будет примерно следующее

https://pastebin.com/rnRLrVMv

При «переходе» на экран карты отключаются все объекты, относящиеся к битве, и включаются объекты, относящиеся к карте. При выборе уровня все происходит ровно наоборот

Но сейчас у меня при старте на сцене куча объектов безо всякого порядка — в основном из-за того, как сделан спавн героев игрока и противника

https://pastebin.com/j3rZkH9Q

Решаю просто: перед вызовом спавна героев создаю два дополнительных объекта — для героев игрока и героев противника соответственно

https://pastebin.com/SJVLNdCf

Какое-то адское извращение, но работает же!


После этого при спавне героев достаточно указать, что соответствующий _goParent… их родитель. Это так мило — теперь у них есть родитель.


Главное, что мне это дало — как только spawner отключается, пропадают все герои и панель с их скиллом. И появляются при его включении.


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

А дальше что?

Ах да — забыл. Теперь архитектура объектов на сцене выглядит вот так

Опыт разработки первой игры на Unity, часть 3 Gamedev, Indiedev, Инди, Unity, Видео, Длиннопост

Дальше нужно добавить саму структуру кнопок / битв. Располагаться они будут как на одной из картинок выше.


Хммм, а как это сделать кодом?

Конечно же, первым делом полез в интернет! Наверняка уже кто-то такое делал) Только ничего подобного не нашел. Хорошо, придется думать.


У меня есть повторяющийся паттерн поведения: 1 — 2 — 1 — 2 и так далее. Значит, мне нужно сделать так, чтобы на экране появлялась комбинация "1 — 2". Хотя стоп — а вдруг я захочу сделать 1 — 2 — 2 — 2 — 1 или что-то такое.


Между делом наткнулся на новую для себя в юнити штуку — Grid. Судя по всему, там как раз можно задать любой шаблон — в том числе такой, какой мне нужен. Но как-то не удалось заставить его работать.


В итоге придумал какую-то абсолютно хитровыдуманную конструкцию. Что-то мне подсказывает, что я перемудрил

Опыт разработки первой игры на Unity, часть 3 Gamedev, Indiedev, Инди, Unity, Видео, Длиннопост

Ну и что я натворил

https://pastebin.com/TU2Hzt7r

Вызывается так


for (int i = 0; i < _maxLvl; i++)
{
SpawnMap(i, i);
}

Ура! Чем нравится это решение: вместо второй «i» могу подставить 1 или 2. Например, мне нужна последовательность 1 — 1 — 2 — 1 — 2 — 2 — 2.


Тогда я делаю что-то типа такого

Вызов ее через for, но вместо _maxLvl будет 1 («SpawnMap(i, 1);»).

Вызов ее через for, но вместо _maxLvl будет 2 («SpawnMap(i, i);»).

Вызов ее через for, но вместо _maxLvl будет 2 («SpawnMap(i, 2);»).


И да, в первом случае for не нужен, но это для меня. Иначе могу запутаться.

Разделение битв и особой сущности

По задумке с одной стороны будут битвы, с другой — будет находиться эта самая сущность. Но тут вообще просто — создаю функцию SetBtnType(int type)

https://pastebin.com/hVY2t8wY

И добавляю ее вызов в SpawnMap() куда-нибудь в конец


SetBtnType(howManyConverter(howMany));
_startPoint += directionY;

Тут я понимаю, что забыл повесить какой-нибудь скрипт на кнопку карты. Так что делаю новый скрипт ClickMapBtn и вешаю на префаб кнопки карты. Щас будет полное извращение, но в этом скрипте у меня будет в том числе храниться индекс этой кнопки 0_о


Конечно, и в самом спавнере еще нужно добавить массив с кнопками.


Перед тем, как сделать переключением «состояния» рабочим, нужно сделать еще одну вещь.

Сброс параметров всего, что отключается

Помните DEBUG_SWITCH, которая просто отключает / включает объекты? Забудем о ней! Теперь на каждом объекте есть скрипт, который не просто отключает объекты, а еще делает что-нибудь с их параметрами, если это нужно.


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


Какие типовые сущности у меня есть? Герои, кнопки их скиллов и кнопки карты.


Сходу наткнулся на интересное архитектурное решение. По какой-то причине контроль ползунков на кнопке использовании скила у меня находится… В скрипте Characteristics. Это гениально, не иначе. Значит, переношу всю эту систему туда, где ей самое место — в скрипт копки. А Characteristics будет туда только передавать актуальные данные.


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


Вот такая штука теперь у меня переключает «активные сцены»

https://pastebin.com/6vVgSvPC

Честно — понятия не имею, как ее сократить. Первый for проходится по всем героям игрока, следующий — по героям противника. Если попытаться сразу обновить цель для героя в первом цикле, то герои игрока не найдут противника, увы.


А всякие WakeUp вообще простейшие

https://pastebin.com/MbEEQMx2

В итоге получилось вот так

При респавне герои начинают с начальными характеристиками.


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

Но это еще не все!

Игра умеет менять состояние "битва / карта", теперь нужно сделать так, чтобы кнопки на карте запускали битву — причем разную в зависимости от кнопки!


Для этого вернусь в скрипт, спавнящий на карте кнопки боев и добавлю что-то типа такого

https://pastebin.com/36AhBd8M

Гляньте, как классно все получается! Номер уровня увеличивается только у кнопок по центру и крайних левых

Подготовка завершена! Теперь, наконец, можно запускать битву, выбрав нужную кнопку на карте.

В бой!

В скрипте смены состояния делаю небольшие перестановки, вынеся кучу текста по разным функциям

https://pastebin.com/Qqu2WJpL

И происходит магия!

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


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

Результат

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

https://pastebin.com/5JL4yLbh

Опыт разработки первой игры на Unity, часть 3 Gamedev, Indiedev, Инди, Unity, Видео, Длиннопост

Уф, теперь можно заняться прокачкой героев. Вернусь, когда с ней закончу!

Показать полностью 5 3

Опыт разработки первой игры на Unity, часть 2

Ссылка на часть 1

Ссылка на часть 3

Часть вторая — в которой я сдаюсь и опускаю руки

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

Заключение

Итак, я сдался. Вся затея пошла прахом. Постоянные ошибки сломили меня, а полное непонимание того, что делаю — добило окончательно

Так, а это что за текст?

Ахах, сдался — как же! Просто скипнул эту часть и перешел к следующему этапу. Благо, пока что это можно сделать относительно безболезненно. К загрузке данных вернусь как-нибудь потом. Итак, поехали дальше!

О возникших проблемах

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

Нашел такое решение. И оно работает!

Смог адаптировать под моих героев и записать в них данные из таблицы! Проблема пришла внезапно. На первом листе у меня находятся, по сути, ссылки на айдишники из других листов. А cvs, насколько понял, не позволяет работать с другими листами! Штош, ищем другие варианты.

Поиски привели к https://docs.google.com/spreadsheets/d/*/expor? format=cs? edit#gid=0, где * это id таблицы, а после gid айди листа.

Ура — unity возвращает адрес нужного листа! Но данные все равно берутся с первого листа, агрррх! Я отчаялся. Почти потерял надежду. И тут, неожиданно, нахожу его: https://docs.google.com/spreadsheets/d/{key}/gviz/t? tqx=out:csv&sheet={sheet_name}. Ура, работает! Берет данные именно с нужного листа. Но вылезли баги — все id обрамлены теперь в « ». Решилось просто — указал, что нужно убирать символ ".

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

Итог вы знаете — пока так и не получилось

Следующий шаг

Удаляем все эксперименты — возвращаемся к самому началу до экспериментов с таблицами.


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

Выдал героям и противникам фейковые характеристики, добавил кнопки для использования скилла. Если мана накопится, то по нажатию герой делает БУМ!


Модернизировал выбор цели героем — теперь есть не только сингл-таргет скилл, но и бьющий несколько противников

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


Для того, чтобы найти ближайшие объекты, просто взял готовое решение по поиску и переделал чутка

Еще и здоровье теперь не просто отнимается, а делает это красиво и постепенно. Циферки это здоровье — они дублируют полоску здоровья с кнопки скилла. Потом от них избавлюсь

Что вообще происходит на видео

Герой с 1000 здоровья это герой 1, его кнопка Skill Hero 1. По нажатию, если мана заполнилась, он бьет одного ближайшего противника


Герой с 2000 здоровья, соответственно, герой 2. По нажатию на его кнопку, он бьет всех противников, которые находятся на дистанции атаки


Естественно, количество кнопок зависит от количества героев — и у каждого свой скилл

Возникшие проблемы

Первая — архитектура. Уже начинается непонятно что — что-то откуда-то вызывает, что-то при этом происходит. В итоге сделал в confluence кучу страниц с названиями объектов и префабов. В них — страницы со скриптами. И в этих страницах описание того, что они делают со ссылками на те скрипты, которые вызывают. Надеюсь, это немного поможет упорядочить то, что у меня там происходит.

Вторая тоже относится к архитектуре, но немного иначе. Например, изначально был скрипт Fight, в котором есть метод Attack(). В начале все было хорошо — а сейчас понадобилось использовать умения. Если все оставить как есть, то Attack() будет вызывать UseSkill(), но это скилл должен вызывать атаку! Поэтому пришлось немного переделывать такие моменты. Теперь Fight() только определяет само состояние атаки и ее возможность, а UseSkill() используется в другом месте

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

Немного важностей

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

private IEnumerator UpdateCurHealth()

{

while (_prevHealth > _curHealth)

{

yield return new WaitForSeconds(.1f);

_prevHealth--;

_healthCount.text = _prevHealth.ToString();

if (_prevHealth <= _curHealth)

{

_prevHealth = _curHealth;

StopCoroutine(UpdateCurHealth());

}

}

}

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

Следующий этап

А дальше у меня прокачка! Научу героев получать опыт и повышать уровень

Показать полностью 2

Первая игра 1

Ссылка на часть 2

Ссылка на часть 3

Знакомство

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

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

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

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

Что

Баттлер. Ибо они популярны и приносят много денег

Зачем

Хочу перенести часть меты из баттлеров в кор-геймплей. Чтобы... а тут уже пока секрет) Предвижу при этом проблемы из разряда "а зачем мне в это играть больше одного раза?". Но есть мысли, как сделать так, чтобы игрокам всё ещё было интересно играть после первой сессии

Референсы

AFK arena. Оттуда беру сами битвы - герои по бокам, есть активируемый супер прием. Оттуда же беру продвижение по миру в виде мистического лабиринта. Ах да, не беру - вдохновляюсь!

И... Ещё одна пока секретная игра) Нужно же мне сохранить интригу для того, чтобы продолжили читать

Основная часть

Чутка о себе: умею выводить "Hello, world". Могу нарисовать прямую линию в пеинте. Слегка знаю, как работать в юнити - что есть скрипты и что часть переменных можно показывать в инспекторе. Итак, знаний достаточно, игра планируется сложной. Пфф, изи, что может пойти не так.


Для начала решил сделать самое главное - боевую систему.

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

Аккуратнее - ниже запись с камеры телефона!

Ии оно работает! Квадраты (герои игрока) умеют подбегать к кругам (героям противника). При достижении нужного расстояния меняют поведение "движение" на поведение "атака". После смерти противника ищут следующую цель - и так до победного.

Считаю, это успех.


Следующее, что собираюсь сделать - каким-то образом взять характеристики героев из таблицы и выдать их героям на экране. Как это сделать? А черт его знает!

Спасибо за внимание, вернусь, когда соображу за перенос данных из таблицы и как-нибудь это сделаю


P.S Врооооде со скрипом втискиваюсь в правила раздела - но модераторам будет виднее)

Показать полностью 2
Отличная работа, все прочитано!