Как я игрульку создал: ч. 1 Предыстория и идея, ч. 2 Игрок и сборщик уровней

Часть 1. Предыстория и идея

Здравствуй, Хабр!

Я хочу поделиться опытом в создании одной гиперказуалки. Сам я, правда, ещё совсем зелен и юн на этой тернистой тропе игроделания, но может кому-то станет интересно и он прочтёт цикл этих статей. Самого кода здесь не будет, а если и будет, то в очень мизерных количествах, в связи с чем вряд-ли эти статьи будут интересны людям, которые хотят применить главную основу программирования «Ctrl+C/Ctrl+V» да и так как я сам новичок то заядлым «про» тут тоже будет скучновато, а плюсом на них может наложиться дебафф «BloodFromTheEyes» от порой наинеумнейших решений наиглупейших проблем. Но всё же опыт есть опыт, так что расскажу что есть.

Пожалуй, начну с рассказа как я до такого докатился.

Предыстория

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

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

Потом мне потихоньку надоело безвылазно сидеть в кубическом мире и я отдалился от него. Как раз в то время в школе началась информатика. Не то чтобы учительница была хорошо сведущая (перепутать блок питания и видеокарту это сильно), но основы Паскаля она заложить смогла. Это показало мне, что оказывается я могу довольно легко писать программы и игры в том числе, если выучу язык программирования, ведь это быстро и просто! (как же я ошибался…)

Так я захотел создать игру и немного почитав начал выбирать движок. Естественно мой взор пал на Unity. Могу выделить в нем такие плюсы как:

  1. Простота, ведь и само меню и основной язык C# являются довольно лёгкими в обучении

  2. Огромное количество статей, тем на форумах и видеоуроков по практически любой проблеме. (К примеру у UE видеоуроков на русском заметно меньше)

  3. Я есть школота. Школота есть сила. Сила есть единство. Единство есть Unity!

Так, спустя немного времени, я получал 5 несколько четвертей подряд за созданную наспех игру. Сама игра было максимально простой. В игре были небольшие квадратные комнаты и коридоры, соединявшие этим комнаты. Игровое поле представляло собой массив этих комнат, соединённых коридорами (размер поля 16×16 комнат). В некоторых комнатах были монстры. Монстры были разные. Некоторые маленькие и слабые, но буквально прижимали к стенке количеством и затыкивали до смерти. Некоторые сносили почти всю полосу здоровья 1-м ударом, но были медленными и не могли покинуть свою комнату. Цель этой игры — собрать 5 ключей для открытия нужной комнаты и загнать туда 1-го монстра, который движется быстрее игрока и наносит 99 урона (максимальное здоровье — 100). Игрок мог умирать сколько хочет. Возрождался он в 1-й из 4х комнат, куда не могут зайти монстры в разных углах локации. Сама игра не была оснащена даже кнопкой выхода или настройками, но для школьных учителей информатики и этого хватало.

Кому интересно как «Это» выглядело, то вот:

b4f358950c3d939208e186f3acaafcaa.png

Как я придумал идею

Так, я окончил школу и поступил в вуз. За это время я начинал по меньшей мере ещё 3 проекта и строил грандиозные планы по ААА проектам, которые взорвут сеть. Однако я всё же звёзд с неба не хватаю и принял решение выпустить игру на подобии прошлых гигантов, таких как Crossy Road и Geometry Dash. У первого я взял идею простоты, а у второго идею графики со светящимися в темноте цветами. Правда, по ходу создания друг подметил что больше всего игра напоминает другую классику — Doodle Jump.

b43b91109544b52c7574e0c197eec035.png

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

Суть игры, как писалось выше, максимально простая. Нужно допрыгать так высоко вверх, как только возможно, но по мере увеличения высоты будут появляться новые виды и комбинации платформ. Чтобы игроку было не скучно, можно будет менять образы (Стандартный — «Ниндзя воды». Его можно заметить на изображении) и темы самой карты. Новые образы и темы можно будет получить с помощью «Рандомайзера», заплатив 100 монет, которые вы собрали на карте или получили, выполняя миссии.

Сами образы делятся на:

  • Стандартные. Их больше всего и шанс их выпадения около 74%

  • Редкие. Их поменьше. Шанс получить такой будет не больше 20%

  • Эпические. Красивые образы, шанс получить которые будет 5%

  • Легендарные. Название само говорит за себя. Шанс выпадения такого равен 1%.

  • Особые. Это образы событий или тематические. Шанс получить такой в рандомайзере всего 0,01%.

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

Часть 2. Игрок и сборщик уровней

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

Дисклеймер: данная статья не является гайдом и не рекомендуется к прочтению профи из-за опасность улететь на своём кресле в стратосферу.

Игрок

Для начала разберёмся, что мы вообще хотим от игрока:

  1. Должен прыгать и падать

  2. Должен плавно перемещаться по горизонтальным поверхностям

Для простой реализации физики в Unity представлены компоненты Rigidbody и Character Controller. Первый представляет собой объект физического движка, а второй это скрипт, управляющий объектом. Изначально я начал делать персонажа с помощью Rigidbody, но уже на начальных этапах столкнулся с проблемой, когда персонаж сам по себе входил с стены, липнул ко всему подряд и в некоторых случаях перемещался рывками, поэтому было принято решение избавится от Rigidbody в пользу Character Controller.

Реализовывал гравитацию и передвижение я в двух методах:

В GamingGravity () я присваивал переменной gravityForse значение -50f, если персонаж не касается земли и -1f, если касается. Этого хватило, чтобы обеспечить довольно быстрое падение и не дать объекту самому оторваться от земли. Для проверки соприкосновения с землёй была использована одна из функций самого Caracter Controller: [chController].isGrounded возвращает true, если объект касается земли и false, если нет. Данные значения были умножены на Time.deltatime.

В методе CharacterMove () было реализовано всё остальное передвижение персонажа. В самом начале метода переменная типа Vector3 обнулялась [moveVector] = Vector3.zero, чтобы не переносить значения из прошлой работы метода.

Далее персонажу даётся возможность прыгать, если игрок касается земли и зажата левая кнопка мыши (На android работает как нажатие). Для самого прыжка присваиваем gravityForse значение 20f.

Следующим шагом даём игроку возможность перемещаться влево и право:

Если позиция мыши по оси х меньше начальной позиции, то переменной типа float присваиваем значение: [move] = ((startMausPos — Input.mousePosition.x) / 30f) * sensitivity, где sensitivity это переменная чувствительности, а 30f было подобрано испытательным методом, чтобы при sensitivity = 1 игрок перемещался с нужной скоростью. Далее нужно сделать проверку на максимальную скорость: Если наша скорость меньше максимальной, то присваиваем её отрицательное значение переменной [moveVector].x, в обратном случае присваиваем отрицательное значение максимальной скорости.

Если же позиция мыши по оси х меньше начальной позиции, то при присвоении значения переменной [move] вычитаем из текущей позиции начальную, а переменной [moveVector].x присваиваем не отрицательные, а исходные значения [move] и максимальной скорости.

Финальным этапом присваиваем [moveVector].y значение gravityForse.

Оба метода (GamingGravity () и CharacterMove ()) вызываем в Update и радуемся передвижению нашего персонажа.

Генерация уровней

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

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

В начале скрипта располагается структура Lvls, которая содержит:

public GameObject thisLvl;  — сам префаб уровня,
public GameObject[] impossibleLvl;  — все префабы, которые нельзя поместить после этого уровня,
public GameObject posseblelvl;  — префаб, который точно можно поместить. Он нужен для подстраховки.

Чтобы уровни были более разнообразными, я разделил их на 2 типа и 2 сложности: Y5 (его высота равняется 5) и Y10 (его высота равняется 10) и 1 и 2 сложность. Для каждого типа каждой сложности я создал массивы структуры Lvls. Так же добавил переменную сложности difficultyFactor с изначальным значением 0.

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

Теперь о самом процессе подбора уровня.

Когда игрок подобрался к сборщику менее чем на 20 единиц, то скрипт начинает определять какой уровень создать. Так как уровней типа Y5 меньше и они добавляют сложности игре, шанс их появления 30%, а остальные 70% остаются для Y10. С помощью рандома создаётся переменная и случайным образом выбирается 1 из типов. после выбора переменной отвечающей за сложность (у меня это difficultyFactor) прибавляется значение 1. Предположим выбран тип Y10.

Переносимся в метод SpawningY10(), где тут же отфутболиваемся в другой метод передав ему параметр 10 (высота уровня в Y10) для определения какой именно уровень выбрать. Этот дополнительный метод вызывается во всех методах спавна и принимает на вход нужную высоту. Он нужен для удобства чтения скрипта и даёт возможность выбирать к примеру из различных образов карты. Внутри этого метода выбираем какой сложности сгенерировать уровень. Если рандомная переменная (от 0 до 100) больше difficultyFactor, то сложность 1, если меньше, то 2. Выбранный префаб возвращается в метод SpawningY10(), После чего идёт проверка, есть ли этот новый уровень в массиве impossibleLvl от текущего. Если есть, то повторяем вызов метода для выбора уровня. Если же скрипт совершил более 50 повторений и всё ещё выбирает невозможные уровни, то генерируем объект для подстраховки, posseblelvl.

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

Послесловие

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

Всех благодарю за то, что уделили пару минут своего времени этой статье. До скорого!

© Habrahabr.ru