ITVDN logo
Видеокурсы по
программированию

Доступ более чем к 7700 видеоурокам от $19.99

Подписка
ITVDN logo
Видеокурсы по
программированию

Доступ более чем к 7700 видеоурокам от $19.99

Подписка

Введение

Не так давно была популярной такая мобильная игра, как Flappy Bird.  Причем по разным причинам из магазинов она пропала также неожиданно, как и появилась. Однако, учитывая ее популярность, сразу образовалось множество клонов. Не столь качественных, конечно. Но почему их было много? Все из-за того, что сама игра делается довольно просто и быстро.


И в этой небольшой статье мы, разумеется, рассмотрим, как же сделать такую игрушку, как Flappy Bird. Все ресурсы (спрайты, шрифты) принадлежат непосредственно их авторам.

Ресурсы

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

Ресурсы

Спрайты найдены на просторах интернета.

Шрифт был скачан по ссылке: http://www.dafont.com/04b-19.font?text=Flappy+Number

Подготовка игровых объектов

Сначала сделаем префаб игрока, то есть птичку. Для этого создаем на сцене пустой объект с именем 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.

using UnityEngine;

public class BirdHelper : MonoBehaviour

{

    public float force;

    private new Rigidbody2D rigidbody;

    void Awake()

    {

        rigidbody = GetComponent<Rigidbody2D>();

    }

    void Update()

    {

        if (Input.GetKeyDown(KeyCode.Space))

            rigidbody.AddForce(Vector2.up * (force - rigidbody.velocity.y), ForceMode2D.Impulse);

        rigidbody.MoveRotation(rigidbody.velocity.y * 2.0F);

    }

}

С помощью метода MoveRotation мы совершаем поворот птицы в зависимости он величины и знака значения текущего ее ускорения. Поле force у нас открытое и, разумеется, отобразится в окне Inspector. Напишем там 8. Это будет сила “прыжка”.

Со скриптом главного героя почти всё. Вернемся к нему немножко позже.

Перейдем к трубам. Их задача - двигаться на игрока, ведь сам по себе он лишь летает по вертикальной оси в процессе игры и так и не сдвинется по горизонтальной. Движение препятствий можно реализовать статическим методом MoveTowards структуры Vector3.

using UnityEngine;

public class PipesHelper : MonoBehaviour

{

    [SerializeField]

    private float speed;

      

    void Start()

    {

        Vector2 position = transform.position;

        position.y = Random.Range(-1.5F, 2.5F);

        transform.position = position;

        Destroy(gameObject, 6.0F);

    }

       void Update()

       {

        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? Он будет отвечать за генерацию новых препятствий, подсчет очков и их отображение. А еще за перезапуск уровня. Давайте глянем код.

using System.Collections;

using UnityEngine;

using UnityEngine.UI;

public class GameHelper : MonoBehaviour

{

    [SerializeField]

    private Text scoreText;

    private GameObject pipes;

    public Button restartButton;

    [HideInInspector]

    public int score;

    void Awake()

    {

        pipes = Resources.Load<GameObject>("Pipes");

    }

    void Start()

    {

        StartCoroutine(GeneratePipes());

    }

    void Update()

    {

        scoreText.text = "Score: " + score;

    }

    IEnumerator GeneratePipes()

    {

        Vector2 position;

        while(true)

        {

            position = transform.position;

            position.x += 6.0F;

            Instantiate(pipes, position, Quaternion.identity);

            yield return new WaitForSeconds(2.0F);

        }

        

    }

    public void Restart()

    {

# if UNITY_5_2

        Application.LoadLevel(Application.loadedLevel);

#endif

        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.

using UnityEngine;

public class BirdHelper : MonoBehaviour

{

    public float force;

    private new Rigidbody2D rigidbody;

    private GameHelper;

    void Awake()

    {

        rigidbody = GetComponent<Rigidbody2D>();

        gameHelper = Camera.main.GetComponent<GameHelper>();

    }

    void Update()

    {

        if (Input.GetKeyDown(KeyCode.Space))

            rigidbody.AddForce(Vector2.up * (force - rigidbody.velocity.y), ForceMode2D.Impulse);

        rigidbody.MoveRotation(rigidbody.velocity.y * 2.0F);

    }

    void OnCollisionEnter2D (Collision2D collision)

    {

        gameHelper.restartButton.gameObject.SetActive(true);

        Time.timeScale = 0.0F;

    }

    void OnTriggerExit2D (Collider2D other)

    {

        gameHelper.score++;

    }

}

Метод OnTriggerExit2D сработает в момент выхода игрока из триггера, который находится внутри препятствия и добавит нам одно очко.

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

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

Спасибо большое всем за внимание. Удачи в начинаниях и творческих успехов!

СТАТЬИ ПО СХОЖЕЙ ТЕМАТИКЕ
ВИДЕО КУРСЫ ПО СХОЖЕЙ ТЕМАТИКЕ

КОМЕНТАРИИ И ОБСУЖДЕНИЯ

ОЦЕНИТЕ ДАННЫЙ МАТЕРИАЛ

ПОДПИСКА НА ITVDN ВЫГОДА ДО 29.95$ НА ОБУЧЕНИЕ ПРЕСТИЖНЫМ ПРОФЕССИЯМ!

1 месяц19.99$
подписка

легкий старт в обучении

3 месяца49.99$
подписка

выгода от подписки до9.98$

6 месяцев89.99$
подписка

выгода от подписки до29.95$