01 сентября 2020

🏓 Создаем 2D-игру на Unity: инструкция для новичка

Full stack web developer: Laravel, Symfony, MySQL/PostgreSQL, Nginx, MongoDB.
Показываем, как создать простую 2D-игру на Unity на примере игры в пинг-понг, C#-код прилагается. Пошаговое руководство для абсолютных новичков, дающее практическую основу для изучения Unity.
🏓 Создаем 2D-игру на Unity: инструкция для новичка

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

Примечание
Если вы хотите получить более систематическое образование в области разработки игр, мы рекомендуем рассмотреть факультет разработки игр онлайн-университета GeekBrains.

Двумерные игры сравнительно просты: для них не требуется сложных 3D-моделей, программный код по сравнению с 3D-проектами выглядит понятнее. Такие игры популярны как на десктопах, так и на мобильных устройствах. Unity также позволяет разрабатывать игры и для браузеров.

За последние годы вышло много популярных двумерных игр:

  • Battletoads продолжение серии игр в жанре beat ’em up, файтинг;
  • Spiritfarer игра в жанре «ферма», менеджмент;
  • Carrion игра в жанре «ужасы», метроидвания;
  • Creaks игра в жанре «квест», головоломка;
  • Streets of Rage 4 игра в жанре beat ’em up, файтинг.

Программная реализация 2D-игр проще не только из-за отсутствия третьего измерения: на самой сцене меньше объектов, вместо трехмерных моделей плоские спрайты, вместо скелетной анимации покадровая. А еще 2D-игры проще портировать на другие платформы – легче найти новую аудиторию.

Особенности создания 2D-игр на Unity

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

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

  • Ресурсили Asset – основной строительный блок для любого проекта Unity. Это может быть изображение, трехмерная модель, звук. Чтобы не путать с префабами или игровыми объектами, рекомендуется размещать ресурсы в отдельной папке Assets.
  • Игровой Объектили GameObject. Если ресурс используется в сцене, то он становится игровым объектом. Например, у нас есть изображение противника – это ресурс. Когда же мы создадим на сцене 20 противников, то получим 20 игровых объектов.
  • Компоненты влияют на поведение и отображение игровых объектов.
  • Префаб – способ хранения игровых объектов, оптимизированный для многократного использования и клонирования с разными настройками. При изменении префаба изменяются все его копии.
  • Скрипт – исходный текст программы на языке C#. Могут прикрепляться к игровым объектам или префабам.

Пошаговый процесс создания 2D-игры на Unity

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

В первую очередь создадим новый проект и откроем его настройки (Edit → Project Settings). Во вкладке Editor установим параметр Default Behaviour Mode в значение 2D

Настройка проекта
Настройка проекта
Детальная настройка проекта
Детальная настройка проекта

Следующим шагом сохраним текущую активную сцену, назвав ее, например, Scene1. Теперь создадим основные игровые объекты: ракетку, мяч и менеджер игры, в котором будет храниться основная логика игры.

1. Создаем пустой объект, переименовываем в GameManager.

Создаем пустой объект
Создаем пустой объект

2. Создаем C#-скрипт с названием GameManager. Ассоциируем скрипт с объектом GameManager, перетащив мышкой скрипт на объект.

Создаем C# скрипт
Создаем C# скрипт

3. Создаем квадратный спрайт, называем его Pad (Assets → Create → Sprites → Square). Аналогично создаем круглый спрайт Ball (Assets → Create → Sprites → Circle). Масштабируем спрайт Pad со следующими параметрами x:0.5, y:2.5, z:1.

Создаем спрайты
Создаем спрайты

4. Создаем префабы для Pad и Ball, после чего добавляем к ним компонент Box Collider 2D (включаем параметр Is Trigger) и компонент Rigidbody 2D (выставляем параметр Body Type в значение Kinematic).

Добавляем .компонент Box Collider 2D
Добавляем .компонент Box Collider 2D
Настраиваем.компонент Box Collider 2D
Настраиваем.компонент Box Collider 2D
Добавляем компонент Rigidbody 2D
Добавляем компонент Rigidbody 2D
Масштабируем спрайты
Масштабируем спрайты

5. Создаем C#-скрипты Ball и Pad. Ассоциируем их с префабами.

6. Заполняем скрипты следующим кодом.

GameManager.cs
        using System.Collections;
using System.Collections.Generic;
using System.Security.Cryptography;
using UnityEngine;
public class GameManager : MonoBehaviour
{
    // Публичная переменная для ракетки, будет ассоциироваться с префабом Pad
    public Pad pad;

    // Публичная переменная для ракетки, будет ассоциироваться с префабом Pad
    public Ball ball;

    // Публичная переменная со значением нижнего левого угла экрана
    public static Vector2 bottomLeft;

    // Публичная переменная со значением верхнего правого угла экрана
    public static Vector2 topRight;

    void Start()
    {
        // получаем значение нижнего левого угла экрана
        bottomLeft = Camera.main.ScreenToWorldPoint(new Vector2(0,0));

        // получаем значение верхнего правого угла
        topRight = Camera.main.ScreenToWorldPoint(new Vector2(Screen.width, Screen.height));

        // создаем мяч
        Instantiate(ball);

        // создаем две ракетки
        Pad padVar1 = Instantiate (pad) as Pad;        
        Pad padVar2 = Instantiate (pad) as Pad;
        padVar1.Init (true);   // Инициализируем левую ракетку
        padVar2.Init (false); // Инициализируем правую ракетку
    }
}
    
Ball.cs
        using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using UnityEngine;

public class Ball: MonoBehaviour
{
    [SerializeField]
    float speed;

    float radius;
    Vector2 direction;

    void Start()
    {
        direction = Vector2.one.normalized;
        radius = transform.localScale.x / 2;
    }

    void Update()
    {
        // Обрабатываем движение мяча
        transform.Translate(direction * speed * Time.deltaTime);

        // Обрабатываем столкновения с краями экрана
        if(transform.position.y < GameManager.bottomLeft.y + radius && direction.y < 0)
        {
            direction.y = -direction.y;
        }

        if (transform.position.y > GameManager.topRight.y - radius && direction.y > 0)
        {
            direction.y = -direction.y;
        }

        // Обрабатываем выигрыш игрока
        if (transform.position.x < GameManager.bottomLeft.x + radius && direction.x < 0)
        {
            UnityEngine.Debug.Log("Right win");
        }

        if (transform.position.x > GameManager.topRight.x - radius && direction.x > 0)
        {
            UnityEngine.Debug.Log("Left win");
        }

    }

    // Обрабатываем столкновение ракетки и мяча
    void OnTriggerEnter2D(Collider2D other)
    {
        if(other.tag == "Pad")
        {
            bool isRight = other.GetComponent<Pad>().isRightPad;
            if (isRight == true && direction.x > 0)
            {
                direction.x = -direction.x;
            }

            if (isRight == false && direction.x < 0)
            {
                direction.x = -direction.x;
            }
        }
    }
}
    
Pad.cs
        using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Security.Cryptography;
using System.Threading;
using UnityEngine;

public class Pad : MonoBehaviour
{
    [SerializeField]
    float speed;

    float height;
    string inpt;
    public bool isRightPad;

    // Вызывается в начале игры, перед первым обновлением кадра
    void Start()
    {
        height = transform.localScale.y;
    }

    public void Init(bool isRight)
    {
        isRightPad = isRight;
        Vector2 pos = Vector2.zero;

        // Обрабатываем изменение позиции ракетки. Для левой и правой ракетки - отдельно.
        if (isRight)
        {
            pos = new Vector2(GameManager.topRight.x, 0);
            pos -= Vector2.right * transform.localScale.x;
            inpt = "PadRight";
        } else {
            pos = new Vector2(GameManager.bottomLeft.x, 0);
            pos += Vector2.right * transform.localScale.x;
            inpt = "PadLeft";
        }

        transform.position = pos;
        transform.name = inpt;
    }

    void Update()
    {
        // Блокируем выход ракетки за края экрана, обрабатываем изменение позиции на экране
        float move = UnityEngine.Input.GetAxis(inpt) * Time.deltaTime * speed;

        if(transform.position.y < GameManager.bottomLeft.y + height/2 && move < 0)
        {
            move = 0;
        }

        if (transform.position.y > GameManager.topRight.y - height / 2 && move > 0)
        {
            move = 0;
        }

        transform.Translate(move * Vector2.up);
    }
}
    

6. Добавляем к префабу Ball и Pad теги с аналогичными именами. Выделив префабы, в инспекторе мы можем видеть выпадающий список тегов. Там же расположены и кнопки для добавления и редактирования тегов.

7. В настройках камеры выставляем параметр Projection в значение Orthographic, а параметр Clear Flag в значение Solid Color.

Настройка камеры
Настройка камеры

8. Настраиваем кнопки, как показано на следующих скриншотах (Edit → Project Settings → Input Manager).

Настройка ввода, основное
Настройка ввода, основное
Настройка ввода, первый игрок
Настройка ввода, первый игрок
Настройка ввода, второй игрок
Настройка ввода, второй игрок

Вот и всё, игра готова!

Пинг-понг, итоговый результат
Пинг-понг, итоговый результат

Билд для платформы Windows

Исходный код

Дополнительные туториалы

1. Официальный туториал от Unity, где детально рассмотрен процесс создания roguelike RPG.

2. Youtube-канал Brackeys, где можно найти серию видеоуроков по созданию 2D-платформера.

3. Youtube-канал N3K EN содержит множество уроков как по отдельным компонентам Unity, так и полноценные серии уроков по созданию игр с нуля.

Больше полезной информации вы найдете на наших телеграм-каналах «Библиотека программиста» и «Книги для программистов».
***

Хочу научиться программировать с нуля, но не знаю, с чего начать. Что делать?

Можно учиться самостоятельно (долго) или пойти на курсы с преподавателями (быстро). Плюс нужно учитывать, что джунов много, конкуренция выше и работодатели повышают порог вхождения при найме на работу. Чтобы получить актуальные знания, мы в proglib.academy запустили курсы:

  • Основы программирования на Python.
  • Профессия Python-разработчик.
  • Алгоритмы и структуры данных.
  • Математика для Data Science.
  • Профессия Data Science.
  • Frontend Basic: принцип работы современного веба.
  • Профессия Фронтенд-разработчик.
  • Обработка естественного языка. Полный курс.

На подходе еще больше 10 курсов для взрослых и детей.

МЕРОПРИЯТИЯ

Комментарии

ВАКАНСИИ

Добавить вакансию
PHP Developer
от 200000 RUB до 270000 RUB
DevOps
Санкт-Петербург, от 150000 RUB до 400000 RUB
Golang разработчик (middle)
от 230000 RUB до 300000 RUB

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ