Я не особо люблю теорию, поэтому мне все проще изучать и объяснять на практике. Чтобы научиться делать игры - нужно делать игры! А для этого нужно просто начать! Поэтому без лишних слов давайте начнём, а все вопросы будем решать по мере их поступления! Не забудьте только подписаться, если эта тема вам интересна! :)
Какую игру будем делать?
Сразу оговоримся будем делать для начала что-нибудь очень простое! А еще для нашей с вами первой игры нам понадобится так называемый "референс" - отсылка, пример для подражания - не для того, чтобы его точно скопировать, а просто чтобы примерно понимать, что мы делаем. Когда есть понимание, то все становится намного проще)
Предлагаю сделать незатейливую выживалку, в которую можно было бы играть одним пальцем на телефоне или одной мышкой на компьютере, чтобы в неё мог сыграть кто угодно. В качестве референса предлагаю любую игру с видом сверху, например Don't Starve - это один из ранних представителей жанра "выживалок":
Но игру будем делать трехмерную, потому-что "почему бы и нет" и потому-что анимации персонажа гораздо проще сделать в 3D, когда ты не художник. :)
Где будем делать?
Будем делать на движке Unity. Если вам хочется не только смотреть, но и делать все вместе со мной, то скачиваем Unity с их сайта https://unity.com/ru/download - точнее Unity Hub - специальный менеджер проектов, в котором создаем проект и скачиваем уже сам редактор. На сайте все подробно объясняется, так что не будем здесь расписывать все нюансы установки.
У меня сейчас версия 2021.3.29f1 LTS, но поначалу версия не имеет значения. Любая LTS-версия (поддерживаемая в данный момент) подойдёт.
Создаём пустой 3D-проект и давайте уже чего-нибудь программировать! Назову проект пока условно ZenSurvival - выживание на Дзене :)
Начинаем разбираться в коде
В движке Unity используется язык программирования C# (Си Шарп) - в нём то мы и будем разбираться и творить нашу игродельскую магию! Для написания кода нам потребуется Visual Studio, которая устанавливается вместе с Unity.
Если вы впервые устанавливаете Visual Studio, то после установки Unity нужно будет сначала запустить VS отдельно и завершить его установку, выбрав там специальное дополнение для работы с Unity.
Важный нюанс: убедитесь, что в настройках Unity наша Visual Studio выбрана, как внешний редактор кода в меню Unity: Edit - Preferences - External Tools - а там выбрать External Script Editor - Visual Studio. Если этого не сделать, то редактор не сможет выводить нужные нам подсказки связанные с Unity.
Создадим наш первый скрипт (программу), щелкнув на пустом окне проекта правой кнопкой мыши и выбрав Create -> C# Script. Назовем его просто Game. Двойным щелчком он откроется в редакторе Visual Studio и наконец-то мы можем приступать к написанию нашего первого кода! Но для начала давайте посмотрим что там у нас открылось:
"Как много непонятных слов!" - Такая же реакция была и у меня! А ведь мы еще ничего даже не написали! Но не пугайтесь - при должном объяснении на примерах все станет сразу понятно.
Пока нас интересует только основное окно, в котором написан код. Все строки пронумерованы, что очень удобно. А в первой строчке написано using, что с английского переводится как "использование" и по смыслу так и есть - с помощью этой команды к нашей программе подключаются библиотеки с готовыми функциями, которые мы сможем использовать для наших нужд и не изобретать велосипед с нуля. :)
using UnityEngine;
Конкретно тут в коде на 3 строчке по умолчанию подключается библиотека движка - UnityEngine, которая даёт нам возможность использовать кучу полезных функций класса MonoBehaviour путём наследования. Получается, что наша программа Game (а в языке С# программа, называется class) наследует весь богатый жизненный опыт программы MonoBehaviour и уже по умолчанию много всего умеет, о чём мы поначалу даже не догадываемся)
public class Game : MonoBehaviour
{
}
Еще две библиотеки нам тоже выдали по умолчанию. Хотя они пока и не нужны, но обязательно пригодятся в дальнейшем, как и два прописанных по умолчанию метода Start() и Update(), которые поначалу привычнее называть "функциями". То, что это методы нам говорят () - две скобки после их названия.
Перед методами прописаны комментарии (всё, что написано после двух слэшей // является комментарием и программой игнорируется, но очень полезно для нас). Эти стандартные комментарии можно сразу удалить, как и метод Start, чтоб не мозолил глаза - он нам пока не понадобится.
Фигурными скобками { } разграничивается начало и конец программы (класса) или функции (метода), а точка с запятой означает конец строки.
Эти два метода (Start и Update) нам достались в наследство от MonoBehaviour. Start, что логично, выполняется в самом начале при включении программы, а Update выполняется каждый кадр в процессе работы программы. Что такое public и void разберём чуть позже.
Начинаем программировать
Ну всё, научившись основам "грамматики" языка C# начинаем наконец творить!
Во-первых в игре должен быть главный герой, за чьё благополучие мы будем переживать. Самый главный и очевидный показатель для него это здоровье, поэтому в теле нашего класса Game так и запишем:
Переводя с программного на русский, это означает, что мы объявляем целочисленную (int = integer) переменную по имени health, которая имеет значение по умолчанию 100. В C# переменная тоже называется непривычно - поле. А перед типом переменной напишем public, что позволит нам её увидеть в окне инспектора в редакторе Unity.
А что за цифра 100? Какие единицы измерения? Тугрики или попугаи? Это мы решаем сами - как задумаем так и будем считать. Для простоты предлагаю считать, что это проценты.
Получается, что мы одной строчкой кода ввели понятие "здоровье главного героя" из которого вытекает логичная механика проигрыша в виде смерти главного героя при здоровьи равном нулю. Но для верности, чтобы избежать ошибок, учтём и ситуацию, когда здоровье вдруг стало меньше нуля.
Давайте для удобства создадим свой метод GameOver(), который будет нам пока просто сообщать, что мы проиграли. А проверять мы состояние нашего здоровья будем с помощью метода Update(), который в каждом кадре игры будет выполнять все действия, которые мы в нём пропишем. Так и запишем - "если здоровье меньше или равно нулю, то игра окончена":
Сразу оговорюсь, что есть всякие правила и премудрости как правильно писать код, но сейчас мы не будем этим забивать голову - она итак пухнет от количества новой информации.
Поэтому просто придумываем правила игры и разбираемся, как записать их в виде кода на языке C#.
Итак, получается, если здоровье главного героя иссякло, то программа начинает нам где-то 60 раз в секунду (каждый кадр) заявлять, что мы проиграли. Информативно, но не практично! Я и с первого раза понимаю, что мне говорят. Ну на худой конец с третьего! Поэтому давайте добавим условие "если герой жив и его здоровье иссякло, то он проиграл и больше не жив". Как то вот так, не совсем по-русски, надо научиться мыслить, чтобы программа сделала то, что нам нужно. Добавим переменную isAlive, которая будет хранить в себе информацию жив главный герой или нет:
Тип переменных bool может иметь только два значения true или false (истина или ложь), поэтому "булевая" переменная isAlive может нам сообщить только "жив" или "не жив".
Совместим проверку здоровья с проверкой живости: "Если главный герой жив и его здоровье меньше или равно нулю, то мы проиграли", ну а если не жив, то ничего не делаем, да и вообще перестаём интересоваться его здоровьем. Волшебный знак двойного "и" && служит как раз для того, чтоб если первое условие не сложилось, то второе нечего и проверять.
Всё, теперь, если здоровье закончилось, программа напишет нам один раз, что мы проиграли, а в переменной isAlive запишет для себя, что персонаж "не жив", что нечего тратить время на проверку его здоровья и со скоростью света заваливать игрока сообщениями о том, что он проиграл.
Но что будет причиной ухудшения здоровья героя? Поскольку мы делаем игру про выживание, то мне кажется одним из главных насущных вопросов должен стать вопрос: "Что бы такого съесть, чтобы похудеть?!". Всё как в жизни - полный реализм. Поэтому давайте добавим в игру параметр сытости и для простоты назовём его food (еда).
Сытость тоже будем измерять в процентах и целых числах для простоты. Если упростить механизм наступления голода, то можно просто взять за правило, что голод наступает со временем. Ну, а сытость со временем соответственно тратится.
Мы могли бы просто уменьшать сытость в методе Update каждый кадр по чуть-чуть, но что если у кого-то игра будет работать в 30 кадров в секунду, а у кого-то в 300? Какая дискриминация людей с мощными компьютерами и телефонами получается! Поэтому давайте считать не кадры, а секунды и тут нам поможет полезная функция из библиотеки UnityEngine доставшаяся нам без смс и регистрации по умолчанию. А заодно добавим переменные interval и hungerTime, которые будут отвечать за время через, которое главный герой станет голоднее на 1%.
Опять какие-то новые слова! Функция Time.deltaTime выдаёт нам время, которое прошло между кадрами игры. Если эти значения суммировать каждый кадр, то мы получим единицу ровно, когда пройдёт одна секунда. Точнее неровно! Эти интервалы времени представляют из себя доли секунд, а значит дробные числа, которые в языке C# имеют тип float. Поэтому нам понадобится переменная interval типа float, чтобы её изменять каждый кадр на какое-то дробное значение. С целочисленными переменными типа int такое проделать не получится.
Теперь если мы будем запускать наш новый метод Hunger() в методе Update() каждый кадр, то новый метод будет отсчитывать время равное hungerTime и если (if) сытость главного героя еще не на нуле, то вычитать из неё единицу с помощью короткой и удобной записи food--, а иначе (else), если в животе главного героя образовалась полнейшая пустота, то через те же промежутки времени будем уменьшать здоровье на 1 с помощью такой же нехитрой конструкции health--. Ну и конечно каждый раз обнулять переменную интервал, чтобы начать отсчет интервала заново.
В методе Update давайте немного подкорректируем условие и сделаем, чтобы проверка на голод и на проигрыш делалась только, если главный герой жив. Ну и метод голода вызывался только, если здоровье главного героя больше нуля. В противном же случае вызываем метод GameOver() и записываем в переменную isAlive, то что герой уже "не жив" в виде значения false. Теперь наша программа перестаёт беспокоится и по поводу его голода, и по поводу его здоровья.
Вот как вся наша программа или можно сказать скрипт (или даже можно поумничать и сказать "класс") выглядит в целом:
Теперь обязательно нужно сохранить наш скрипт! И вообще сохранять его в любой непонятной ситуации и по любому поводу! Чтобы все это заработало и мы насладились результатами наших трудов, нам нужно прикрепить этот скрипт Game на любой игровой объект на сцене нашего пока пустого проекта.
Переходим обратно в Unity и уже в окне Иерархии щёлкаем правой кнопкой мыши - выбираем создать пустой объект (Create Empty).
Теперь перетаскиваем наш скрипт прямо на этот пустой объект в панели Иерархии. И если никаких ошибок в скрипте не обнаружится, то он на нём успешно повиснет. Объект для удобства можно переименовать. Например, назовём его также Game. Если кликнуть на него в окне иерархии и тем самым выбрать, то в окне инспектора справа мы увидим его компоненты, среди которых будет и наш скрипт Game, в котором мы увидим наши публичные поля health, food, isAlive и hungerTime.
Теперь жмём кнопку Play и если никаких логических ошибок нами не допущено, то все должно заработать. Увидеть мы это сможем прямо в инспекторе, т.к. показатель еды начнёт убывать. Правда довольно медленно и для проверки всех наших механик придётся ждать долго. Хорошо, что мы вынесли hungerTime в отдельную переменную и сделали её публичной. Теперь мы сможем менять её значение прямо в инспекторе и даже в режиме игры. Поэтому давайте впишем туда 0.1, чтобы ускорить все в 10 раз. Вуаля! Сытость стремительно ушла в ноль, а за ней и здоровье, галочка возле isAlive пропала, а в консоли появилось сообщение "Game Over!". Нашего главного героя визуально еще даже не существует, а он уже умудрился сдохнуть от голода! Каков талант!
Возможно первая статья по разработке нашей "выживалки" получилась длинновата, но зато сколько всего интересного мы узнали! Чтобы поскорее увидеть продолжение поставьте лайк и подпишитесь на канал, а в комментариях обязательно напишите что было непонятно объяснено или ненаглядно показано. А еще жду от вас предложений, что делать в нашей игре дальше. Какую механику, визуальную составляющую или звуки добавить?
А вот и продолжение:
А вот тут, кстати, я рассказываю как всему этому учился я сам: