Разработка игр стратегий unity. Добавляем фон в сцену

  • 27.03.2020

Как создать игру в Unity

Казуальные игры в жанре match 3 (три в ряд) - одни из самых популярных на рынке. Многие играют в Candy Crush, Bejeweled и прочие. У этих игр простая цель: перемещать мозаичные элементы до тех пор, пока три одинаковых элемента не окажутся рядом. Когда это происходит, совпавшие элементы исчезают, а на их месте появляются другие. Игрок при этом набирает баллы.

В этом руководстве будут освещаться следующее:

  • Создание доски, заполненной мозаичными элементами
  • Выбор и отмена выбора мозаичных элементов
  • Идентификация соседних элементов с помощью raycasts
  • Замена элементов
  • Поиск совпадающих трех и более элементов с помощью raycasts
  • Заполнение пустых элементов
  • Ведение счета и подсчет движений

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

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

Share this article:

Related Articles

В первой главе вы найдете основную информацию по загрузке и установке Unity и подготовке первой сцены нашей игры.

Настройка среды в Unity

Начнем с самого простого: загрузки и настройки Unity .

Загрузите последнюю версию с официального сайта или торрента и запустите установочный файл.

Для редактирования кода в Unity (4.0.1 и выше) служит редактор MonoDevelop. Если вы работаете в Windows, вы можете (и я вам советую) использовать альтернативый редактор Visual Studio 2013 Desktop (C#) для Windows , после чего в настройках Unity измените редактор по умолчанию на Visual Studio.

Полезно знать: невозможно использовать отладчик Visual Studio 2013 Express с Unity. Вы должны иметь Pro версию Visual Studio и купить UnityVS плагин. С версией Express, вы будете иметь лучший редактор кода, но отсутствие отладчика сведет на нет все его достоинства.

Mac OS X

Заметка о папке Resources : если вы уже работали с Unity, вы знает, что Resources – полезная и уникальная папка. Она позволяет загрузить в скрипт объект или файл (с помощью статичного класса Resources). Она понадобится нам в самом конце (в главе, посвященной меню). Проще говоря, пока мы не будем ее добавлять.

Наша первая игровая сцена

Панель Hierarchy (Иерархия) содержит все объекты, которые доступны в сцене. Это то, чем вы манипулируете, когда начинаете игру с помощью кнопки "Play".

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


Как вы можете видеть здесь, у нас здесь 3 потомка для объекта Level .

В Unity можно создать пустой объект и использовать его в качестве "папки" для других игровых объектов. Это упростит структуру вашей сцены.

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

Заполнение сцены

По умолчанию, новая сцена создается с объектом Main Camera (Главная камера). Перетащите ее на сцену.

Для начала создайте эти пустые объекты:

Scripts Мы добавим наши скрипты сюда. Мы используем этот объект, чтобы прикрепить сценарии, которые не связаны с объектом – например, скрипт гейм-менеджера. Render Здесь будет наша камера и источники света. Level

В Level создайте 3 пустых объекта:

  • 0 - Background
  • 1 - Middleground
  • 2 - Foreground

Сохраните сцену в папке Scenes . Назовите ее как угодно, например Stage1 . Вот, что у нас получилось:

Совет: по умолчанию игровой объект привязан к положению родителя. Это приводит к интересному побочному эффекту при использовании объекта камеры: если камера является дочерним объектом, она автоматически будет отслеживать положение родителя. Если же она является корневым объектом сцены или находится внутри пустого игрового объекта, она всегда показывает один и тот же вид. Однако если вы поместите камеру в движущийся игровой объект, она будет следовать за его передвижениями в пределах сцены. В данном случае нам нужна фиксированная камера, поэтому мы помещаем ее в пустой объект Render . Но запомните это свойство объекта камеры, оно может вам пригодиться. Мы подробно остановимся на этой теме в главе "Паралаксный скроллинг".

Мы только что создали базовую структуру нашей игры. На следующем этапе мы начнем делать забавные вещи: добавим на сцену фон и кое-что еще!

Добавляем фон в сцену

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


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

Создайте в Unity новый игровой объект Sprite на сцене.

Что такое спрайт?

По сути, спрайт – это 2D-изображение, используемое в видео-игре. В данном случае это объект Unity для создания 2D-игр.

Добавляем текстуру спрайта

Unity может автоматически установить фон для вашего спрайта. Если ничего такого не произошло, или если вы хотите изменить текстуру, перейдите на вкладку инспектора и выберите background : (фон)


Вы должны нажать на маленький круглый значок справа от поля ввода, чтобы появилось Select Sprite (Выбрать спрайт) в Инспекторе

Мой спрайт не появляется в диалоговом окне! Убедитесь, что вы находитесь в вкдадке Assets диалогового окна "Select Sprite" (Выбрать спрайт). Если вы видите диалоговое окно пустым, - не пугайтечсь. Дело в том, что для некоторых установок Unity, даже со свежим новым 2D проектом изображения импортируются как "Текстура", а не "Спрайт". Чтобы это исправить, необходимо выбрать изображение на панели "Проект", и в "Инспекторе", изменить свойство "Текстура Type" имущество "Sprite":

Итак, мы создали простой спрайт отображающий облака на небе. Давайте внесем изменения в сцену. В панели Hierarchy (Иерархия) выберите New Sprite . Переименуйте его в Background1 или что-то такое, что легко запомнить. Переименуйте его в Background1 или что-то такое, что легко запомнить. Затем переместите объект в нужное место: Level -> 0 - Background . Измените координаты на (0, 0, 0) .


Создайте копию фона и поместите его в (20, 0, 0) . Это должно отлично подойти к первой части.

Tip : Вы можете создать копию объекта с помощью клавиш cmd + D в OS X или ctrl + D Windows .

Слои со спрайтами

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

В Unity мы можем изменить "Z" наших элементов, что позволит нам работать со слоями. Это то, что мы делали в этом руководстве перед обновлением до Unity 5, но нам понравилась идея идея использовать слои со спрайтами. У вашего компонента Sprite Renderer есть поле с именем Sorting Layer с дефолтным значением. Если щелкнуть на нем, то вы увидите:

Давайте добавим несколько слоев под наши нужды (используйте кнопку +):

Добавьте фоновый слой к вашему спрайту фона:

Настройка Order in Layer - это способ ограничить подслои. Спрайты с меньшим номером оказываются перед спрайтами с большими числами.

Слой Default нельзя удалить, так как это слой, используемый 3D-элементами. Вы можете иметь 3D-объекты в 2D игре, в частности, частицы рассматриваются как 3D-объекты Unity, так что они будут рендериться на этом слое.

Добавление элементов фона

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


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

Получение двух спрайтов из одного изображения

Выполняйте следующие действия:

  1. Импортируйте изображения в папку "Текстуры"
  2. Выберите спрайт Platform и перейдите к панели Инспектор
  3. Измените "Sprite Mode" на "Multiple"
  4. Нажмите на кнопку Sprite Editor (Редактор спрайта)

В новом окне (Sprite Editor) вы можете рисовать прямоугольники вокруг каждой платформы, чтобы разрезать текстуру на более мелкие части:


Кнопка Slice в левом верхнем углу позволит вам быстро и автоматически проделать эту утомительную работу:

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

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


Добавим их в сцену. Для этого мы будем выполнять те же действия что и для фона: создадим новый спрайт и выберим platform1 . Потом повторим эти действия для platform2 . Поместите их в объект 1 - Middleground . Убедитесь, что их позиция по оси Z равна нулю.


Prefabs (Префабы)


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


Заметка о кнопках "Prefab": При последующей модификации игрового объекта, вы можете использовать кнопку "Apply", чтобы применить эти изменения к Prefab , или кнопку "Revert", чтобы отменить все изменения игрового объекта в свойстваъ Prefab . Кнопка "Select" переместит выбранные свойства в ассет Prefab в окне проекта (они будут выделены).

Создание префабов с объектами-платформами упростит их повторное использование. Просто перетащите Prefab на сцену, чтобы добавить копию. Попробуйте добавить другую платформу таким же образом.

Теперь вы можете добавить больше платформ, меняющих свои координаты, размеры и плоскости (вы можете поместить их на заднем или переднем плане, просто установите координату Z для платформы на 0).

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

Слои

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

При переключении из 2D режима в 3D, в окне "Scene" (Сцена) вы будете четко видеть слои:


Кликнув на игровом объекте Main Camera , вы увидите, что флажок Projection установлен на Orthographic . Эта настройка позволяет камере визуализировать 2D игру без учета трехмерных свойств объектов. Имейте в виду, что даже если вы работаете с 2D объектами, Unity по-прежнему использует свой 3D движок для визуализации сцены. Рисунок выше это наглядно демонстрирует.

В следующем уроке:

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

Здравствуйте уважаемые читатели данной статьи. Сегодня я решил поговорить о ОО-проектировании в сфере геймдева, используя движок Unity. Сразу скажу, что данная статься является объективным видением использования паттернов и в особенности, их реализация. Кто-то может говорить о том, что в моих далее приведенных примерах лучше использовать тот или иной паттерн и возможно вы будете правы, но моя задача как минимум поверхностно пройтись по этой достаточно сложной теме. Я рассчитываю сделать небольшой цикл статей про разные паттерны и их примерное использование, чтобы архитектура вашего проекта стала гибкой и расширяемой.
Также хочу отметить, что данный цикл будет требовать определенных знаний в области ОО-программирования и базового ознакомления с понятием "паттерн".
Вроде всё оговорил, можем начинать!

Паттерн "Стратегия". Его использование.

Данный паттерн рассчитан для добавления новых поведений объектам, имеющим один базовый класс, либо интерфейс.
Например, у нас есть объект "Enemy" и объект "Player" . Наша задача - добавить новые виды врагов и NPC в нашу небольшую игру. Предположим, мы уже имеем базовую реализацию двух ранее написанных классов, но мы хотим и в перспективе добавлять всё новые типы врагов и NPC, а реализовывать отдельные классы для каждого типа - вещь затратная, как в плане времени, так в плане и "кодинга".
Если мыслить более абстрактно, мы можем выделить для класса "Enemy" , "Player" и "NPC" один абстрактный класс, который будет иметь общие хар-ки и методы. Назовём этот класс "Human". Я рассматриваю задачу на конкретных примерах и поэтому названия буду давать соответствующие.
Что мы имеем? Предположим такую реализацию класса "Human" :

Using UnityEngine; using Game.Behaviours; namespace Game.Entity { public abstract class Human: MonoBehaviour { protected readonly byte maxHealth = 100; protected string humanName; protected byte currentHealth; } }

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

Примечание: В целом, набор параметров можно было бы хранить двумя другими способами. 1. Хранить внутри инкапсулированной структуры. 2. Хранить внутри ScriptableObject.

Теперь перейдём к реализации класса "Player" :

Using UnityEngine; using Game.Behaviours; namespace Game.Entity { public class Player: Human { public struct PlayerInfo { public Vector3 position; public PlayerInfo(Vector3 position) { this.position = position; } } private Vector3 direction; private PlayerInfo playerInfo; private void Start() { playerInfo = new PlayerInfo(transform.position); } private void Update() { if (Input.GetKey(KeyCode.Space)) Movement(); } private void Movement() { //Здесь мы будем двигать нашего игрока (для примера) } } }

Поясню некоторые моменты:

  1. Все изменяемые параметры игрока я храню внутри структуры (чтобы в будущем можно было бы, например, сохранять эти данные в JSON формате. (сохранение игровых данных, проще говоря).
  2. Для примера, я решил сделать передвижение игрока, поэтому добавил некоторые элементы, которые будут для нас важны в будущем.

А теперь реализация класса "Enemy" :

Using UnityEngine; namespace Game.Entity { public class Enemy: Human { private void Start() { } private void Update() { } } }

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

Public interface WalkBehaviour { float SetMoveSpeed(float speed); void Move(Vector3 direction); }

Классический пример интерфейса для движения объекта. Далее, класс-поведение:

Using UnityEngine; namespace Game.Behaviours { public class HumanWalkBehaviour: WalkBehaviour { private Transform humanTransform; private float speed; public HumanWalkBehaviour(Transform humanTransform, float speed) { this.humanTransform = humanTransform; this.speed = speed; } public float SetMoveSpeed(float speed) => this.speed = speed; public void Move(Vector3 direction) { humanTransform.position += direction * speed; //Проверка. Как и подобает. Debug.LogFormat("Transform: {0}, Direction: {1}, Speed: {2}", humanTransform, direction, speed); } } }

Как видите, все необходимые данные инициализируются в конструкторе класса, а при этом, само поведение не будет занимать места среди компонентов игровых объектов, так как существует он только внутри классов, которыми он будет создан.
Теперь немного преобразуем класс "Human" :

Using UnityEngine; using Game.Behaviours; namespace Game.Entity { public abstract class Human: MonoBehaviour { protected readonly byte maxHealth = 100; protected string humanName; protected byte currentHealth; private WalkBehaviour walkBehaviour; public void SetWalkBehaviour(WalkBehaviour behaviour) => walkBehaviour = behaviour; public void PerformWalkSetSpeed(float speed) => walkBehaviour.SetMoveSpeed(speed); public void PerformWalkMove(Vector3 direction) => walkBehaviour.Move(direction); } }

Если вы обратите внимание, то увидите, что благодаря методу "SetWalkBehaviour" вы имеете возможность добавить новое поведение, а конкретно поведение, реализуемое на базе интерфейса "WalkBehaviour". Достоинство этого способа заключается в том, что экзмепляр, который хранит у нас "WalkBehavoir" никак не привязан к классу "HumanWalkBehaviour". Что это значит? То, что вы можете делать несколько классов-поведений, которые связаны одной задачей, но при этом имеют разную реализацию. Конкретнее в примере далее.
Теперь преобразуем класс "Player" :

Using UnityEngine; using Game.Behaviours; namespace Game.Entity { public class Player: Human { public struct PlayerInfo { public Vector3 position; public PlayerInfo(Vector3 position) { this.position = position; } } private Vector3 direction; private PlayerInfo playerInfo; private void Start() { playerInfo = new PlayerInfo(transform.position); SetWalkBehaviour(new HumanWalkBehaviour(transform, .25f)); } private void Update() { if (Input.GetKey(KeyCode.Space)) Movement(); } private void Movement() { PerformWalkMove(direction); } } }

Как вы можете видеть, класс "Player" создаёт новое для себя поведение, а также передаёт некоторые входные параметры, необходимые для данного поведения. Сам вызов поведения реализован в методе "PerformWalk" и вызывается внутри класса "Player" . Если протестировать, игрок будет двигаться в том направлении, которое ему укажут (переменная "direction" в окне "Inspector").
Таким образом, мы применили паттерн "Стратегия". Мы создали один общий класс для многих похожих друг на друга классов, которые имеют общие параметры, но при этом могут иметь или не иметь разные поведения.
Таким способом, вы можете добавлять совершенно разные поведения и устанавливать их для разных классов. Например для класса "Enemy" я создам новое поведение.
Вот новое поведение для класса "Enemy" :
using UnityEngine;

Namespace Game.Behaviours { public class EnemyNotWalkBehaviour: WalkBehaviour { public void Move(Vector3 direction) => Debug.Log("This enemy can not walk! He can only kill!"); public float SetMoveSpeed(float speed) => 0; } }

И его использование в классе "Enemy" :

Using UnityEngine; using Game.Behaviours; namespace Game.Entity { public class Enemy: Human { private void Start() { SetWalkBehaviour(new EnemyNotWalkBehaviour()); PerformWalkMove(Vector3.zero); } } }

Хм. Кажется, этот враг не может ходить... Верно, мы ведь избавили его от этой нелёгкой работы.
Как видите, я использую те же методы, что и в классе "Player" , только теперь добавляю новое поведение. Однако, я могу добавить это поведение и игроку. Но стоит ли?

В следующей статье я хочу рассказать сразу о двух паттернах, а именно "Одиночка" и "Декоратор".

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