Урок 6 из 8 · 25 мин · обновлено

Интерфейс: счёт, жизни и кнопки

Как устроены Canvas и элементы интерфейса в Unity, как вывести счёт текстом TextMeshPro, привязать элементы к краям экрана и связать скрипты между собой через менеджер игры.

Canvas: холст интерфейса

Счёт, жизни, кнопки и меню в Unity рисуются на Canvas — холсте поверх игры. Создайте текст: в Hierarchy правая кнопка → UI → Text - TextMeshPro. Unity сама создаст Canvas и поместит текст внутрь. Если появится окно импорта ресурсов TextMeshPro, нажмите Import TMP Essentials.

Сразу настройте у Canvas компонент Canvas Scaler: режим Scale With Screen Size и эталонное разрешение 1920 × 1080. Тогда интерфейс будет пропорционально увеличиваться и уменьшаться на разных экранах.

Создайте два текста: ScoreText и LivesText.

Якоря

Положение элемента интерфейса задаётся в компоненте Rect Transform, а отсчитывается от якорей (Anchors). Нажмите на квадрат в левом верхнем углу Rect Transform и выберите вариант «левый верхний угол». Удерживайте Alt, чтобы элемент сразу туда переехал.

Теперь счёт всегда в левом верхнем углу: и на ноутбуке, и на широком мониторе, и на телефоне.

Русский текст. Стандартный шрифт TextMeshPro не содержит кириллицы, и вместо русских букв появятся квадраты. Добавьте в проект шрифт с кириллицей, например бесплатный Roboto или PT Sans. Нажмите на файл шрифта правой кнопкой → Create → TextMeshPro → Font Asset и выберите получившийся ассет в поле Font Asset у текстов.

GameManager: менеджер игры

Пока счёт хранится в игроке. Но о монетах должен знать и интерфейс, а позже — экран победы. Удобнее держать состояние игры в одном месте. Создайте пустой объект (Create Empty), назовите GameManager и повесьте на него скрипт:

C#
using TMPro;
using UnityEngine;

public class GameManager : MonoBehaviour
{
    [SerializeField] private TMP_Text scoreText;
    [SerializeField] private TMP_Text livesText;
    [SerializeField] private int coinsToWin = 5;

    private int coins;
    private int lives = 3;

    void Start()
    {
        UpdateUI();
    }

    public void AddCoin()
    {
        coins++;
        UpdateUI();
    }

    public void LoseLife()
    {
        lives--;
        UpdateUI();
    }

    private void UpdateUI()
    {
        scoreText.text = $"Монеты: {coins} из {coinsToWin}";
        livesText.text = $"Жизни: {lives}";
    }
}
  • TMP_Text — тип текста TextMeshPro, для него подключаем using TMPro;;
  • $"Монеты: {coins} из {coinsToWin}" — строка с подстановкой: значения переменных встают на место фигурных скобок;
  • методы AddCoin и LoseLife публичные (public): их будут вызывать другие скрипты.

Перетащите ScoreText и LivesText из Hierarchy в одноимённые поля GameManager в Inspector.

Игрок сообщает менеджеру

В PlayerController уберите свои счётчики coins и lives и добавьте ссылку на менеджер:

C#
[SerializeField] private GameManager gameManager;

void OnTriggerEnter(Collider other)
{
    if (other.CompareTag("Coin"))
    {
        Destroy(other.gameObject);
        gameManager.AddCoin();
    }
}

void OnCollisionEnter(Collision collision)
{
    if (collision.gameObject.CompareTag("Hazard"))
    {
        gameManager.LoseLife();
    }
}

Перетащите объект GameManager в поле Game Manager у игрока. Нажмите Play: счёт и жизни меняются на экране.

Кнопки работают так же: UI → Button - TextMeshPro, а в Inspector в событии On Click нажмите плюс, перетащите объект со скриптом и выберите публичный метод. Кнопки понадобятся в следующем уроке для перезапуска игры.

Попробуйте сами. Добавьте таймер: на уровень даётся 60 секунд, в правом верхнем углу видно «Время: 42». Когда время вышло, выведите в Console «Время вышло».

Решение

Создайте текст TimerText, привяжите его якорем к правому верхнему углу. В GameManager добавьте:

C#
[SerializeField] private TMP_Text timerText;
[SerializeField] private float timeLeft = 60f;

private bool timeOver;

void Update()
{
    if (timeOver)
    {
        return;
    }

    timeLeft -= Time.deltaTime;
    timerText.text = $"Время: {Mathf.CeilToInt(timeLeft)}";

    if (timeLeft <= 0)
    {
        timeOver = true;
        Debug.Log("Время вышло");
    }
}

Time.deltaTime каждый кадр вычитает прошедшие секунды. Mathf.CeilToInt округляет вверх, чтобы на экране было «1», пока не прошла последняя секунда. Флаг timeOver не даёт сообщению повторяться каждый кадр.

Мини-тест: проверьте себя

Закрепите урок: несколько вопросов, разбор ответов — в конце.

  1. Где должны находиться текст счёта и кнопки, чтобы Unity нарисовала их поверх игры?

  2. Счёт в левом верхнем углу на широком мониторе уехал в середину экрана. Что настроено неправильно?

  3. Зачем выносить подсчёт монет и жизней в отдельный скрипт GameManager?

  4. Что нужно, чтобы кнопка могла вызвать метод Restart() из скрипта?