120608

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

Показываем, как создать простую 2D-игру на Unity на примере игры в пинг-понг, C#-код прилагается. Пошаговое руководство для абсолютных новичков, дающее практическую основу для изучения 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# скрипт

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
Добавляем компонент 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 курсов для взрослых и детей.

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

denis.litvinets
16 октября 2024

🎮⚙️ Неправильные настройки импорта в Unity убивают вашу игру

Неправильно настроенные текстуры и звуки могут незаметно убить производител...
denis.litvinets
15 апреля 2025

🎮⛰️ Дорожная карта Unity-разработчика 2025: с нуля до джуна

Подробная и актуальная на 2025-й год дорожная карта со всеми знаниями, необ...
denis.litvinets
06 февраля 2025

🎮💉 Инъекция зависимостей в Unity для самых маленьких

Ваш проект Unity утопает в зависимостях, где изменение одного класса ломает...