В прошлом уроке Игра Flappy Bird на Unity 2D. Часть 2 мы добавили в игру птицу и научили ее летать. Теперь введем в игру препятствия, чтобы в нее было интересно играть.
Содержание
Перемещение камеры
Сейчас камера никогда не перемещается, а это значит, что наша птица рано или поздно исчезнет. Решим эту проблему, создав новый скрипт, который заставляет камеру все время следовать за птицей.
Выберите Main Camera в Иерархии и нажните на Add Component&rarrow;New Script, назовите его CameraFollow и выберите в качестве языка CSharp. Мы также переместим его в нашу папку Scripts, а затем откроем его:
Нам не нужна функция Start, поэтому удалим ее. Давайте добавим публичную переменную Transform для указания направления следования:
Теперь мы будем просто использовать функцию Update чтобы всегда устанавливать X-позицию (положение по горизонтали) камеры в X-позицию target:
Наш скрипт готов. Однако, считается хорошей практикой начать движение камеры после того, как все в сцене обновлено. для этого мы переименуем функцию Update в LateUpdate:
Сохраните скрипт, после чего взгляните на вкладку Инспектор и перетащите игровой объект bird_0 из Иерархии в слот скрипта Target:
Если вы сейчас нажмете на кнопку Play, то вы увидите, что камера автоматически следует за птицей
Создаем препятствия в Unity
Рисуем препятствия
Сейчас наша игра не вызывает затруднений в прохождении. Изменим это, добавив различные препятствия. Давайте нарисуем одно (сохраните его в папке Assets/Sprites):
Мы будем использовать следующий импорт настроек:
Перетащите изображение из Project Area в Иерархию для того, чтобы создать из него игровой объект:
Мы будем позиционировать его в X=3, Y=-5:
Вот как это выглядит на сцене:
Физика препятствий
Препятствие должно быть частью физического мира, чтобы птица могла столкнуться с ним, поэтому давайте выберем Add Component→Physics 2D→Box Collider 2D в Инспекторе:
Мы уже говорили о том, как препятствия могут оказаться внутри земли и о том, что мы не хотим, чтобы они сталкивались, так что давайте сделаем их частью нашего слоя WeirdPhysics:
Так как мы отключили столкновения в промежутке между нашим слоем WeirdPhysics, земля никогда не столкнется с препятствием. Птица все еще может столкнуться с землей и с препятствием, поскольку она расположена на другом слое (по умолчанию one).
И так, некоторые из препятствий также должны двигаться вверх и вниз. Всем объектам, которые должны двигаться, нужно Rigidbody (жесткое тело), так что выберите Add Component→Physics 2D→Rigidbody 2D в инспекторе снова. Мы не хотим, чтобы это происходило под действием силы тяжести, поэтому давайте установим Gravity Scale в 0 . Мы также не хотим, чтобы происходило вращение, поэтому снова включим Fixed Angle:
Если нажать на Play и сделать так, чтобы птица налетела на препятствие, то мы увидим как уровень перезагружается:
Перемещение препятствий
Ладно поэтому некоторые из препятствий, должны двигаться вверх и вниз. Такое поведение может быть реализован с помощью сценария снова. Выделим Add Component→New Script, назовем новый скрипт Obstacle, переместим его в папку Scripts и откроем для редактирования. Есть много различных способов сделать препятствие двигаться вверх и вниз все время. Как обычно, мы будем использовать самый простой способ.
Начнем с того, что добавим переменную speed а затем настроим свойство velocity так, чтобы препятствия двигались вверх с заданной скоростью speed:
Наша хитрость заключается в том, чтобы использовать Unity-функцию InvokeRepeating , чтобы изменять скорость каждые несколько секунд:
Функция Switch просто изменяет скорость Rigidbody. Затем мы используем InvokeRepeating чтобы сказать Unity вызывать эту функцию каждые несколько секунд. Мы также добавили переменную switchTime, чтобы определить время вызова параметра Switch.
Давайте сохраним сценарий и установим для препятствия Speed в 1 :
Если мы не хотим, чтобы препятствия перемещались, то установите Speed в 0 или отключите скрипт.
Если нажать сейчас на кнопку Play то мы сможем увидеть как наше препятствие движется вверх и вниз:
Увеличиваем число препятствий
Щелкните правой кнопкой мыши на препятствие в Иерархии, выберите Duplicate и переместите препятствие немного дальше вправо:
Мы также будем дублировать препятствие и устанавливать его свойство Scale.Y в -1 :
Теперь это выглядит верно, если позиционировать все кверх-ногами:
Мы можем добавить сколько угодно препятствий и с таким количеством различных свойств speed и switchTime:
Мы только что узнали, как невероятно просто сделать игру Flappy Bird в Unity. Попробуйте проявить творческую жилку и сделать игру более трудной и интересной.
Идея игры следующая: птица должна лететь вперед не сталкиваясь с препятствиями. Такие простые игры, как эта, идеально подходит для учебника. Все, что нам потребуется — 40 строк кода и несколько спрайтов.
В нашем учебнике используется Unity 5.0.0f4. Игра будет работать и в более новых версиях Unity, работа в старых версиях не гарантируется. Бесплатная версия Unity 5 теперь поставляется со всеми функциями движка, поэтому мы рекомендуем именно ее.
Настройка проекта
Запустите Unity и выберите New Project (Новый проект):
Назовите его flappybird, выберите где он будет храниться, например, на C:, выберите 2D и нажмите на Create Project (Создать проект):
Если мы выберем Main Camera (Основная камера) в Hierarchy (Иерархия) то мы можем сделать Background Color (Фоновый цвет) светло-голубым (R=198, G=208, B=230) под цвет неба и задать Size (Размер) как показано на рисунке ниже:
Как сделать фон в Unity 2D
Начнем мы с того, что нарисуем небо на фоне:
Щелкните правой кнопкой мыши на изображении выше, выберите Save As. , перейдите в папку Assets и сохраните картинку в новой папке Sprites. После сохранения, мы можем выбрать фон в Project Area:
Измените Import Settings в Инспекторе:
Величина Pixels Per Unit , равная 16 , означает, что 16х16 пикселей поместится в одном объекте в игровом пространстве. Мы будем использовать это значение для всех наших текстур, потому что размер птицы — 16х16 пикселей и это должно быть одним объектом в нашей игре.
Давайте добавим фон в игровой мир, перетащив его из Project Area на Main Camera в Иерархии:
Так фон становится потомком для Main Camera:
Теперь, когда фон является потомком камеры, он всегда будет идти следом за камерой. Таким образом, игрок всегда будет видеть фон.
Мы могли бы поместить несколько фонов рядом друг с другом так, чтобы при перемещении камеры в горизонтальном направлении все равно был бы виден фон, но сделать его потомком камеры намного проще.
Обратимся к вкладке Инспектор и установим позицию фона Y=-1 так, чтобы он как раз подходил к игре и в дальнейшем:
Если нажать Play, то мы увидим небо на заднем фоне:
Существует еще одна необходимая корректировка. В ближайшее время мы добавим птицу и некоторые препятствия, так что давайте убедимся, что фон на самом деле нарисовал позади всего остального. В Unity используются свойства SpriteRenderer: Sorting Layer (Сортировка Слоя) и Order in Layer (Порядок в Слое), чтобы решить какой слой игры должны находиться впереди, а какие за ними.
Мы установим Order in Layer в -1 , так что все остальное будет нарисовано перед небом:
Чем больше величина, тем вероятней, что слой будет находиться на переднем плане. При уменьшении величины вероятность нахождения слоя на заднем плане возрастает.
Ландшафт
Давайте также нарисуем какой-нибудь ландшафт на земле. Мы сделаем его очень широким, чтобы хватило места для наших препятствий:
Щелкните правой кнопкой мыши на изображение, выберите Save As. (Сохранить как. ) и сохраните изображение выше в проекте в папке Assets/Sprites. Выберем землю в Project Area, а затем назначим те же Import Settings (Настройки Импорта), что мы использовали ранее:
После этого мы перетащим его из Project Area в Hierarchy (Иерархия) снова (обратите внимание: на этот раз мы не будем делать его потомком камеры):
Взгляните на вкладку Inspector (Инспектор) и спозиционируйте нашу землю в X=16 , а Y=-6 : так она будет находится ниже фона, а большая часть области окажется в правой части экрана:
На этот раз мы зададим Order in Layer (Порядок в Слое) значение, равное 1 , чтобы слой всегда находился перед птицей и препятствиями:
Ground Physics
Почва должна быть частью игрового мира. Сейчас это просто изображение в игровом мире, только визуальный эффект и больше ничего. Мы хотим, чтобы земля была как стена, с которой птица может столкнуться, поэтому давайте выберем Add Component→Physics 2D→Box Collider 2D в Инспекторе:
По идее, на этом можно было бы закончить, но осталось еще одно маленькое «но».. В дальнейшем мы добавим препятствия в нашу игру (как зеленые трубы в оригинальной игре Flappy Bird) и эти препятствия будут перемещаться вверх и вниз. Препятствия и земля будут частью игрового мира и, в соответствии с законами физики, не может быть двух объектов в одной точке (или, в нашем случае, двух Коллайдеров).
Есть несколько способов обойти эту проблему. Как обычно, мы выберем самый простой путь, создав новый слой, который мы будем использовать для земли и препятствий. После этого мы сообщим Unity игнорировать столкновения в/между этим слоем.
Мы можем создать новый слой, выбрав Add Layer (Добавить Слой) в Инспекторе:
После этого мы добавим один User Layer (Пользовательский Слой), давайте назовем его WeirdPhysics:
Теперь мы можем выбрать землю в Иерархии, а затем снова назначить слой WeirdPhysics:
Затем мы выберем Edit→Project Settings→Physics 2D в верхнем меню и отключим столкновения WeirdPhysics в Layer Collision Matrix, которая определяет как будет себя вести система обнаружения столкновений, привязанных к слоям:
В Unity редко приходится делать что-то такое, но наша игра как раз является исключением.
Теперь земля никогда не столкнется с каким-либо препятствием. Кроме того, если мы нажмем на кнопку Play, то увидим не только небо, но и землю:
На этом первая часть нашего урока подошла к концу. В следующем уроке Игра Flappy Bird на Unity 2D. Часть 2 мы поработаем над нашим пернатым героем.
Введение
Не так давно была популярной такая мобильная игра, как Flappy Bird. Причем по разным причинам из магазинов она пропала также неожиданно, как и появилась. Однако, учитывая ее популярность, сразу образовалось множество клонов. Не столь качественных, конечно. Но почему их было много? Все из-за того, что сама игра делается довольно просто и быстро.
И в этой небольшой статье мы, разумеется, рассмотрим, как же сделать такую игрушку, как Flappy Bird. Все ресурсы (спрайты, шрифты) принадлежат непосредственно их авторам.
Ресурсы
Все, что нам понадобится из ресурсов игры – это несколько спрайтов и шрифт, как в оригинале.
Спрайты найдены на просторах интернета.
Подготовка игровых объектов
Сначала сделаем префаб игрока, то есть птичку. Для этого создаем на сцене пустой объект с именем Bird. Внутрь него помещаем объект-спрайт с именем Body и в свойство Sprite компонента Sprite Renderer помещаем спрайт нашей птички (из папки Sprites).
Теперь на саму птичку (объект Bird) прикрепляем компонент Circle Collider 2D и задаем его радиусу значение 0.45.
Также необходимо прикрепить компонент Rigidbody 2D. Здесь, пожалуйста, не перепутайте. Нам необходим именно 2D компонент, а не обычный Rigidbody. Ему задаем значение гравитации (Gravity Scale) равным 2.45 и запрещаем передвижение по оси X, чтобы наша птичка неожиданно не улетела куда-то в сторону.
Теперь создаем C# скрипт c именем BirdHelper и тоже прикрепляем его к птичке (объект Bird).
После всего этого перетягиваем объект Bird из окна Hierarchy в окошко Project, создав таким путем префаб птички. То есть в итоге на префабе Bird должно быть четыре компонента: Transform, Circle Collider 2D, Rigidbody 2D и скрипт Bird Helper.
С главным героем пока что покончили.
Приступим теперь к единственным препятствиям в игре – трубам. Мы с Вами поступим очень хитро. Так как в процессе игры каждая преграда – это пара труб (одна сверху, другая – снизу), их длину можно было бы регулировать и кодом в момент создания. Ведь если нижняя труба короткая, то верхняя – длинная. Но мы пойдем более простым путем. Наша преграда будет сразу состоять из двух длинных труб, и мы просто-напросто будем их ставить выше или ниже. Как на картинке ниже, где светлая рамка – границы дисплея.
Что ж, дабы заделать префаб преграды, создаем на сцене пустой объект с именем Pipes и помещаем внутрь него два объекта-спрайта с именами TopPipe и BottomPipe. В Каждому из них в свойство Sprite компонента Sprite Renderer перетаскиваем спрайт Pipe (из папки Sprites).
Объекту BottomPipe ставим положение по оси Y -4.5 (отрицательное).
С объектом TopPipe проделываем аналогичные манипуляции, но позиция по оси Y будет 4.5, и еще необходимо повернуть его на 180 градусов вокруг оси Z.
Почти готово. Осталось только настроить коллайдеры и прикрепить скрипт. Начнем с коллайдеров. Прикрепим на объект Pipes компонент Box Collider 2D. А лучше сразу три.
Первый настроим таким образом, как на картинке. Просто немного подкорректируем размер и зададим позиции по оси X значение -4.5. Как вы, думаю, уже догадались — это будет коллайдер для нижней трубы.
Следующий Box Collider 2D настроим аналогично предыдущему, только позиция по оси X будет 4.5.
Теперь последний коллайдер. Он, на самом деле, будет триггером, и с помощью него мы сможем отследить, когда же игрок преодолел текущее препятствие. Вот такие настройки должны быть у этого коллайдера (уже триггера).
И под конец создаем скрипт с именем PipesHelper и прикрепляем его на объект Pipes.
Теперь перетягиваем объект Pipes из окна Hierarchy в окно Project, создав таким путем префаб для препятствий.
Остался только фон. Создаем пустой объект с именем Background. Помещаем в него два объекта-спрайта с именами Part1 и Part2 и в свойство Sprite компонента Sprite Renderer помещаем спрайт Background.
Объекту Part1 задаем размеры X: 2.6, Y: 2.6. С объектом Part2 выполняем такие же действия, но еще сдвигаем его вправо на 7.2 юнитов по оси X.
После этого в объект Background помещаем еще два объекта-спрайта с именами Ground1 и Ground2. Им назначаем спрайты Ground из папки Sprites.
Вот так должны выглядеть настройки объектов Ground1 и Ground2.
Напоследок необходимо добавить коллайдер для земли и создать анимацию движения фона.
Прикрепляем компонент Box Collider 2D на объект Background.
Для того, чтобы создать анимацию, выделяем Background в окне Hierarchy и в окошке Animation нажимаем кнопку Create. Назовем ее BackgroundFloating.
Вся задача данной анимации – передвижение фона влево, чтобы создать эффект, как будто игрок на самом деле летит вправо. После того, как анимация будет создана, на объект Background автоматически прикрепится компонент Animator и будет создан Animator Controller. Нам осталось только перейти в окно Animator и установить значение скорости анимации 0.2.
Под конец создаем скрипт GameHelper и цепляем его на игровую камеру. На этом, пожалуй, все игровые приготовления завершены.
Непосредственно сам процесс разработки
Начнем, я так думаю, из скрипта главного персонажа. То есть BirdHelper’a. Реализация полета птички, как в оригинале, довольно проста. Под действием силы гравитации она будет постоянно падать, а при нажатии клавиши, допустим, Space, мы применим к ней силу по направлению вверх, используя метод AddForce на компоненте Rigidbody2D.
public class BirdHelper : MonoBehaviour
public float force;
private new Rigidbody2D rigidbody;
if ( Input .GetKeyDown( KeyCode .Space))
rigidbody.AddForce( Vector2 .up * (force — rigidbody.velocity.y), ForceMode2D .Impulse);
С помощью метода MoveRotation мы совершаем поворот птицы в зависимости он величины и знака значения текущего ее ускорения. Поле force у нас открытое и, разумеется, отобразится в окне Inspector. Напишем там 8. Это будет сила “прыжка”.
Со скриптом главного героя почти всё. Вернемся к нему немножко позже.
Перейдем к трубам. Их задача — двигаться на игрока, ведь сам по себе он лишь летает по вертикальной оси в процессе игры и так и не сдвинется по горизонтальной. Движение препятствий можно реализовать статическим методом MoveTowards структуры Vector3.
public class PipesHelper : MonoBehaviour
private float speed;
Vector2 position = transform.position;
position.y = Random .Range(-1.5F, 2.5F);
transform.position = Vector2 .MoveTowards(transform.position, transform.position — transform.right, speed * Time .deltaTime);
При появлении препятствие будет выбирать случайную позицию по оси Y, но такую, чтобы не было видно конца верхней или нижней трубы. Затем каждый кадр будет двигаться влево. Для этого, задавая конечную точку движения, мы вычитаем из текущей позиции препятствия вектор, направленный вправо от него. Также через 6 секунд объект Pipes будет уничтожен, чтобы не нагружать устройство, так как он свою задачу уже выполнил.
К полю speed, представляющему скорость движения препятствия, мы применили атрибут SerializeField, чтобы оно было отображено в Inspector’e, ведь мы его закрыли.
Перед тем, как перейти к скрипту GameHelper, добавим на игровую сцену объект-текст с именем ScoreText и настраиваем, как на рисунке ниже.
Это будет текст для отображения количества очков игрока.
Еще давайте добавим объект-кнопку с именем RestartButton. Она будет появляться, когда игрок проиграет, то есть при столкновении с каким-либо препятствием. В свойство SourceImage компонента Image нашей кнопки перетащите спрайт Button из папки Sprites. Вот настройки кнопки.
А вот настройки текста внутри кнопки RestartButton.
Непосредственно объект кнопки нужно деактивировать. Иерархия объектов на сцене будет выглядеть следующим образом.
Какая же задача скрипта GameHelper? Он будет отвечать за генерацию новых препятствий, подсчет очков и их отображение. А еще за перезапуск уровня. Давайте глянем код.
public class GameHelper : MonoBehaviour
private Text scoreText;
private GameObject pipes;
public Button restartButton;
public int score;
pipes = Resources .Load GameObject >( "Pipes" );
scoreText.text = "Score: " + score;
Instantiate(pipes, position, Quaternion .identity);
yield return new WaitForSeconds (2.0F);
public void Restart()
Application .LoadLevel( Application .loadedLevel);
Time .timeScale = 1.0F;
В первую очередь не забудьте подключить пространства имен UnityEngine.UI для работы с элементами пользовательского интерфейса и System.Collections, ведь там находится необходимый нам интерфейс IEnumerator , который мы будем использовать для карутины.
Метод Reset отвечает за перезапуск уровня при нажатии соответствующей кнопки. Значит, настройки компонента Button объекта ResetButton необходимо немножко подправить. Так как сцена у нас не тяжелая, то мы можем позволить себе просто перезагружать ее полностью. Но, так как свойство timeScale статическое, его значение стоит снова установить в 1.0, чтобы при перезагрузке сцены время шло своим чередом.
Метод GeneratePipes является карутиной и его задача – бесконечным циклом создавать препятствия с задержкой между каждыми в 2.0 секунды. Запускается карутина методом StartCaroutine при старте игры. А в методе Awake подгружается из папки Resources модель препятствия.
В поля scoreText и restartButton, которые будут отображены в окне Inspector, необходимо перетащить соответствующие объекты текста очков и кнопки рестарта игры.
Каждый кадр в свойство text объекта scoreText будет передавать текущее количество очков, которое хранится в поле score.
Почти закончили. Осталось немного дополнить скрипт BirdHelper.
public class BirdHelper : MonoBehaviour
public float force;
private new Rigidbody2D rigidbody;
gameHelper = Camera .main.GetComponent GameHelper >();
if ( Input .GetKeyDown( KeyCode .Space))
rigidbody.AddForce( Vector2 .up * (force — rigidbody.velocity.y), ForceMode2D .Impulse);
void OnCollisionEnter2D ( Collision2D collision)
Time .timeScale = 0.0F;
void OnTriggerExit2D ( Collider2D other)
Метод OnTriggerExit2D сработает в момент выхода игрока из триггера, который находится внутри препятствия и добавит нам одно очко.
OnCollisionEnter2D будет вызван при столкновении с любым коллайдером, а это значит, что игрок проиграл. Время остановится и активируется кнопка перезапуска игры.
На этом, пожалуй, всё. Скачивайте проект, изучайте, дополняйте, переделывайте либо просто удаляйте.
Спасибо большое всем за внимание. Удачи в начинаниях и творческих успехов!