06 января, 2018

Пилим игру на Unity (Clicker, Idle, Incremental)

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

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

Далее мы делаем самый простой кликер, 1 кнопка, 1 поле для текста. Нажали кнопку, денежка прибавилась. Гифка:




Забираем персональную версию Unity отсюда:
https://store.unity.com/download?ref=personal

При установке отмечаем WebGL модуль мне он нужен. Создаем новый проект.

 Что у нас тут и где:
Hierarchy слева - Список игровых объектов.
Scene в центре - Редактор, где мы игровые объекты располагаем.
Game в центре - вкладочка рядом с scene. Там мы можем потестировать игру, когда будет что тестировать.
3 кнопки чуть повыше - запуск игры для теста в game режиме.
Inspector - справа, в нем мы настраиваем конкретные объекты.
Внизу Project - это по факту папка с файлами игры на компьютере. Картиночки всякие и прочие штуки добавляются именно сюда.
Также внизу вкладка Console - туда высыпаются ошибки, когда мы что-то делаем неправильно.


Платформа

Дальше сразу же меняем платформу для релиза:

Разрешение игры

Тыкаем на вкладку Game, дальше на free aspect (или что там выбрано будет) и дальше на плюсик.
Добавляем разрешение 800х600.В одном из гайдов было сделано. Знаю что оно окей для веб игр и имеет ровные циферки с нулями на конце, чтобы не путаться. Если надо будет - переделаем.
Теперь меняем разрешение у всего проекта, а не только у тестового окошка.
Лезем в Edit -> Project Settings -> Player
В инспекторе в вкладочке webgl меняем стандартную ширину:
С настройками вроде все. Можно начинать игрой заниматься.

Canvas & Camera

Добавляем canvas - пространство на котором будут располагаться все элементы игры. Тыкаем правой кнопкой куда-нибудь в раздел иерархии. Дальше выбираем UI -> Canvas.
Теперь настаиваем в нем камеру. Делается это в инспекторе в компоненте Canvas. Меняем Screen Space с overlay на Camera.
И теперь перетягиваем объект Main Camera из иерархии в испектор в графу Render Camera. Вот это перетягивание на самом деле довольно часто используется в юнити, приходится привыкать.

Сетка

Теперь чиним сетку на сцене. Сейчас она выглядит примерно так:
Я гуглил и сетка в юнити не настраивается никак. Каждая клеточка - это 1 игровой юнит. При этом если масштабировать сцену то сетка будет меняться при изменении масштаба в 10 раз. Но суть в том что нас игровые юниты пока не интересуют и никто не мешает подстроить камеру так, чтобы от сетки с этими юнитами появилась какая-то польза. Раз у нас вся игра 800х600 пикселей, почему бы не подстроить камеру так, чтобы наш канвас не занимал 8 на 6 клеточек.

Открываем объект Main Camera и в инспекторе меняем Size с 5 на 3. Этот параметр отвечает за то, сколько этих самых клеточек находится в половине вертикального размера игры. То есть от центра до верхней границы камеры. В нашем случае это половина от 6 клеточек, то есть размер должен быть 3. Подробнее об этом можно почитать тут, в разделе Orthograpic Size:
http://www.third-helix.com/2012/02/05/making-2d-games-with-unity.html
В итоге становится норм сетка:

Значок камеры

Наверно неплохо что он есть и показывает что камера настроена на наш канвас, но по итогу он только мешает. Так что выключим его. Делается это в разделе Gizmos. Там тыкаем на значок камеры, не на галочки.

Текстовое поле

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

Размеры

Так вот. В инспекторе для нашего текстового поля есть компонент Rect Transform. Rect - сокращение от rectangle - прямоугольник. В нем есть куча страшных цифр.
Если не вникать в детали - там есть набор пресетов. Они выставляются для якорей, если зажать шифт, то сразу же выставляется еще и опорная точка, а если альт, то еще и позиция. Остается подогнать размеры.
Так что кому лень вникать - скипайте до следуюего заголовка.

Нам нужно запомнить что по стандарту 0, 0 для юнити - левый нижний угол.
Дальше берется якорь (anchors), потом точка опоры (pivot) и уже от нее считается позиция(Pos) и от нее считаются размеры(Width, Height).

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

Что такое якорь - это две пары значений, которые могут быть точка или даже прямоугольником за которые цепляется наш элемент и относительно которых он будет меняться, если поменяется размер родительского элемента в том числе окна игры. Бывают от 0 до 1. 0, 0 - левый нижний угол, 1, 1 - правый верхний.
Вот эти гифки из мануала по юнити прекрасно все объясняют:
Что такое Pivot - опорная точка. Вокруг этой точки происходит вращение и масштабирование. Это пара значений, тоже от 0 до 1. Довольно тяжело словами описывать чем она должна быть. Но в самом простом случае, чтобы не было херни, должно быть тоже самое, что и в значениях якоря.

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

Widht, Height - размеры объекта. Тоже считается в пикселях.

Вообще все неплохо описано в самом мануале по юнити вот тут:
https://docs.unity3d.com/Manual/UIBasicLayout.html

Шрифт

Изначально текст почти не видно, потому что он мелкий и он черный на темно-сером фоне. Чиним это. Меняем стиль текста на Bold, Размер текста на 30, выравниваем текст по центру и меняем его цвет на белый. Потом может чего получше придумаем, а пока и так сойдет.
Получается примерно так:

Переименование

В том же компоненте текст в инспекторе есть поле ввода, где можно что-то написать. С точки зрения игры совершенно не важно что там написано. Это просто подсказка для нас, которая отображается на сцене. Поэтому запишем туда Currency.
В иерархии тоже есть надпись Text, которая не зависит от того что мы переименовали выше, но с точки зрения игры уже важно что там будет написано, потому что именно по этому имени мы будем обращаться к этому полю из скриптов. Назовем ее CurrencyDisp, для того чтобы было понятно, что эта переменная отображает количество валюты (а не сама является валютой).
По итогу я ее переименовал в GoldDisplay. 

Кнопка

Время добавить кнопку, при нажатии на которую мы будем получать немного денег. :)
Тыкаем правой на канвас в иерархии -> UI -> Button.
Сделал ее пожирнее и оставил в центре.
Чтобы переименовать кнопку, нам нужно в иерархии зайти в кнопку и поменять текстовое поле внутри ее.
Шрифт чуть больше и переименуем. Пока в Click Me, потом может чего поумнее придумаем.

Скрипты

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

Внизу тыкаем правой кнопкой и создаем папку Scripts:
Создаем пару файлов. Один для скрипта где будет функция нажатия кнопки, второй для гейм менеджера, который будет следить за глобальным состоянием игры.
Должно получиться как-то так:

GameManager

Теперь можно открывать GameManager скрипт. У меня он открывается в Visual Studio 2017.

Изначально все выглядит примерно вот так:
Сверху подключаются системные библиотеки и библиотека юнити движка.
Дальше внутри класса в фигурных скобках мы все и будем делать.
Там уже есть 2 функции. Первая - Start(). вызывается разово при старте игры и мы ее трогать пока не будем. Вторая - Update(). Вызывается каждый игровой цикл.

Перменные

Сначала определим несколько переменных.
public UnityEngine.UI.Text GoldDisplay; - эта переменная имеет тип - текстовый объект из юнити и называется GoldDisplay. Соответствует вот этому полю:
public float gold = 0; - изначальное количество денег. Тип - дробное число, могло быть и целочисленным, но все равно потом менять придется.
public float goldPerClick = 1; - количество денег добавляемое за каждый клик. Тоже дробное число.

Обновляем текст

В функцию Update мы добавим обновление текстового поля. Без этого количество денег будет меняться, но мы не увидим разницу в игре. Для этого добавляем строчку:
GoldDisplay.text = "Gold: " + gold;

В итоге каждый игровой цикл будет перерисовываться надпись в кавычках и текущее количество денег.

Выглядеть будет примерно так:

Game manager в Unity

Создаем пустой объект в иерархии и называем его GameManager.
В инспекторе к этому объекту добавляем наш скрипт GameManager:
В добавленном компоненте нам нужно определить переменную Gold Display. Можно перетащить GoldDisplay из иерархии или тыкнуть на кружочек справа и выбрать нужный объект из списка:

С менеджером пока что все.

Click Me

Открываем второй скрипт. В нем мы сделаем только одну фукнцию, поэтому стандартые Start и Update удаляем.

Нам нужно, чтобы при нажатии на кнопку поменялось количество денег gold на величину goldPerClick, но обе эти переменные находятся в скрипте GameManager, поэтому нам нужно его подгрузить. Делается это вот такой строкой:
    public GameManager gm;
Тут GameManager - название файла. При этом большие и маленькие буквы важны. А gm - название переменной которое мы будем использовать в нашей функции.

Теперь создаем саму функцию. Назовем ее clicked()
И в ней прибавим к количеству нашего золота, количество золота за клик.
        gm.gold += gm.goldPerClick;
Запись функции довольно странная, но придется привыкать, это тоже самое что и:
        gm.gold = gm.gold + gm.goldPerClick;
Только короче.

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

Click Me в Unity

В иерархии выбираем нашу кнопку. К ней добавляем наш скрипт:
В новом компоненте определяем объект для переменной gm. Из списка его выбрать не получилось, поэтому перетягиваем наш GameManager из иерархии на переменную.
Теперь в компоненте Buttton (Script) внизу добавляем функцию, которая будет срабатывать при нажатии на кнопку.
Добавляем наш объект кнопку, потому что именно к ней привязан скрипт который мы написали:
 И теперь в списке функций находим нужную нам clicked()

Тестируем

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

Сохранение сцены

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

Заключение

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

16 комментариев:

  1. можете написать как обрабатывать большие числа, например когда накопится 100000000000000, юнити начнёт неправильно с ними работать.

    ОтветитьУдалить
    Ответы
    1. у меня такая жа фигня в моей игре

      Удалить
    2. Другие переменные возьми

      Удалить
    3. добавь тип не 1000 а 1к, и т.д.

      Удалить
  2. а если я хочу на пк Кликкер мне не надо что то по другому делать?

    ОтветитьУдалить
    Ответы
    1. нет, просто поменять платформу

      Удалить
  3. Как вывести картинку при достижении числа, тип 10 раз кликнул и включилась картинка?

    ОтветитьУдалить
    Ответы
    1. Я тоже новечек, но могу поделиться своим методом. Не знаю насколько он правильный, но все же:

      Сначала добавлю переменные
      public Image kartinka //чтобы можно было в юнити указать какую картинку использовать. По моему еще библиотеку нужно будет дополнительную подключать для работы с UI

      public int clickValue // имеющееся в данный момент значение кликов
      public int AllClicks = 10;// значение всех кликов. Как ты и хотел поставлю 10,но в инспекторе потом сменить можно спокойно.

      Теперь создаем void Click() {
      clickValue +=1
      }

      А теперь делаем проверку:
      if(clickValue >= AllClicks) {
      kartinka.SetActive(true) ;
      }
      //SetActive позволяет включать и выключать объект на сцене (true - вкл, false- откл)
      //В проверке мы пишем, если у нас кол-во больше или равно указанному в AllClicks, в данному случаи 10,то выполнить следующее - включить картинку, А иначе - выключить ее.
      В синтаксисе мог сделать ошибку, писал с телефона)

      else() {
      kartinka.SetActive(false);
      }

      Удалить
  4. ЛЯТЬ МЯТЬ у меня на моменте когда мы перетаскиваем GameManager в ClickMe У меня шлаг баун и не добавляется HELP!!!

    ОтветитьУдалить
  5. Зачем мы меняем в канвасе с overlay на camera? Ведь если я добавлю меню, которое буду вытягивать, например, снизу, то сквозь него будет наживаться кнопка. Я переделал на оверлэй, только вот теперь у меня не реагирует на клики кнопка вообще, из-за того, что камеру не могу привязать к канвасу, блин, помогите, пожалуйста)

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

      Удалить
  6. как сохранить прогресс например было 20 кликов, и что бы при заходе следующий раз было 20 кликов

    ОтветитьУдалить
  7. Как сделать магазин улучшений в кликере?

    ОтветитьУдалить
  8. Что делать если колчество денег не показывается?

    ОтветитьУдалить