translatedTitles.Article
Ke
Kerioth
12/09/25

Unity. Урок 5

Привет!

Меня зовут Аристарх, я веду занятия по разработке на Unity и буду вашим ментором.

При возникновении вопросов по Unity, пишите мне в чате в ветке Unity с тегом @Kerioth.

Вступайте в чат Практик•ON для прохождения интенсива


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

Приступим к созданию врага.


Основа врага

Для начала сделаем простую основу врага.

Создайте капсулу (3D Object > Capsule), назовите её Enemy. Можете поменять материал в Mesh Renderer, либо убрать визуальную часть и поместить выбранную вами модель внутрь объекта.

Добавьте компоненты Rigidbody (отметьте Freeze Rotation для осей X и Z) и Damagable.

Проверьте, что всё работает как надо, попробуйте выстрелить во врага достаточное число раз для того, чтобы он уничтожился.

Теперь попробуем добавить отображение здоровья врага.

Добавьте во внутрь врага Canvas (ПКМ Enemy > UI > Canvas) укажите в Render Mode > World Space и настройте его так, чтобы он был небольшого размера.

Я обычно устанавливаю размер (Scale) в 0.01 по каждой оси, а ширину (Width) и высоту (Height) в несколько сотен, чтобы не пришлось сильно менять размер UI элементов.

Поднимите холст немного вверх по оси Y, чтобы он оказался над головой врага.

А так же разверните его на 180 градусов по оси Y, таким образом он будет показываться в сторону, куда направлен "взгляд" врага.

Добавьте в холст слайдер (ПКМ Canvas > UI > Slider). Это будет шкала здоровья врага.

Поменяйте размер слайдера, чтобы он был виден над головой врага.

Удалите ручку слайдера (Slider > Handle Slide Area > Handle).

Сделайте более выраженные по цвету задний фон слайдера (Slider > Background) и заполняющую его часть (Slider > Fill Area > Fill), например более темный цвет для заднего фона, более яркий для заполняющей части.

Так же, стоит изменить размер Fill Area, чтобы она закрывала ту часть, которая осталась после удаления Handle.

В настройках слайдера отключите Interactable, поставьте для Transition и Navigation - None.

После всех манипуляций у вас должно получиться что-то вроде этого:


Давайте пропишем логику для отображения здоровья.

Но для начала изменим Damagable так, чтобы можно было получать информацию об изменении здоровья

C#using System;
using UnityEngine;

public class Damagable : MonoBehaviour
{
    public float health = 100f; // значение здоровья
    public event Action<float> OnHealthChange; // событие на изменение здоровья

    // Метод изменения здоровья
    public void ChangeHealth(float amount)
    {
        health += amount; // меняем здоровье
        OnHealthChange?.Invoke(health); // вызываем событие
        
        // проверка на смерть
        if (health <= 0f)
        {
            Die();
        }
    }
    // Метод на нанесения урона перепишем как отрицательное изменение здоровья
    public void TakeDamage(float amount) => ChangeHealth(-amount);

    private void Die()
    {
        // удаляем объект
        Destroy(gameObject); 
    }
}

Теперь можно создать отдельный класс, который будет подписываться на событие изменения здоровья и выводить его на шкалу здоровья.


Создайте скрипт HealthUI:

C#using UnityEngine;
using UnityEngine.UI;

public class HealthUI : MonoBehaviour
{
    public Damagable damagable;
    public Slider healthSlider;
    
    private void Start()
    {
        // возьмем начальное здоровье
        float maxHealth = damagable.health;
        // установим как максимальное значение на слайдере
        healthSlider.maxValue = maxHealth;
        // подпишемся на изменение здоровья
        damagable.OnHealthChange += SetHealth;
        // установим на слайдере максимальное значение
        SetHealth(maxHealth);
    }

    private void SetHealth(float amount)
    {
        // устанавливаем значение слайдера
        healthSlider.value = amount;
    }
}

Данный скрипт можно повесить на врага или сам слайдер.

Главное заполните поля HealthUI в редакторе для Damagable (у меня он находится на самом враге) и HealthSlider (это созданный нами слайдер).

Проверьте как слайдер меняется при нанесении урона врагу.


Навигация врага

Давайте добавим врагу возможность ходить. Мы сделаем это через систему навигации NavMesh.

Логика простая:

  • Создадим уровень
  • Настроим область перемещения врагов
  • Зададим врагу цель – игрока

Таким образом враг будет двигаться на игрока, по доступной ему области.


Я создал небольшой уровень со стенами и препятствиями, но вы всегда можете настроить свой уровень.

Главное убедитесь что он находится внутри отдельного объекта в иерархии.

Добавьте на уровень компонент NavMash Surface

В настройках можно добавить различные типы агентов для разных типов врагов и разные типы областей, но сейчас мы будем использовать агента по умолчанию (Humanoid) и область по умолчанию (Walkable)

Настройте Collect Objects на Current Object Hierarchy, чтобы настройки затрагивали только объект уровня, а не все объекты на сцене.

После этого нажмите Bake и на сцене можно будет увидеть, как выглядит область перемещения агентов.

Добавьте на объект врага компонент Nav Mesh Agent

В нем содержатся настройки перемещения по области навигации (NavMesh Surface)

Пока что достаточно поставить Stopping Distance = 2 или 3, чтобы враг не подходил к игроку вплотную


Создадим скрипт для логики врага EnemyController:

C#using UnityEngine;
using UnityEngine.AI;

public class EnemyController : MonoBehaviour
{
    private NavMeshAgent agent; // агент для перемещения
    private Transform player; // игрок для цели перемещения

    void Start()
    {
        // найдем на сцене PlayerController и возьмем его трансформ
        player = FindAnyObjectByType<PlayerController>().transform;
        // компонент агента находится на том же объекте
        agent = GetComponent<NavMeshAgent>();
    }

    void Update()
    {
        HandleMovement();
    }

    void HandleMovement()
    {
        // назначаем целью агента позицию игрока
        agent.SetDestination(player.position);
    }
}

Перемещение не требует много кода, всё что нужно реализуется посредством системы NavMesh, а все настройки перемещения находятся в Nav Mesh Agent

Добавьте EnemyController на объект врага и проверьте его работу

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


Здоровье игрока

Можно было бы просто повесить на игрока Damagable, но логика здоровья игрока всё таки отличается от логики здоровья других объектов, при этом имеет что-то схожее – у игрока тоже есть числовой показатель здоровья, который может меняться. Но при этом нам не нужно уничтожать объект игрока и сама игра должна как-то реагировать на его смерть.

Решением будет создать новый класс для здоровья игрока, который будет наследоваться от Damagable.

Таким образом сохраним уже написанный функционал без необходимости дублировать код и допишем новый.


Поменяйте параметры у метода Die в скрипте Damagable:

C#protected virtual void Die()
{
    // удаляем объект
    Destroy(gameObject); 
}

protected позволит использовать этот метод наследникам (сюда так же подойдет public, если вас не смущает использование метода мгновенной смерти как публичного), а virtual позволит переопределить этот метод наследниками. В нашем случае игрок при смерти не будет удаляться.


Создайте скрипт PlayerHealth:

C#using System;
// наследуется от Damagable и может использовать всю доступную логику Damagable
public class PlayerHealth : Damagable
{
    public event Action OnPlayerDeath; // событие смерти игрока
    
    // метод проверки, мертв ли игрок (если health меньше либо равно 0)
    public bool IsDead() => health <= 0; 
    
    // метод лечения, просто изменение значения здоровья
    public void Heal(float amount)
    {
        ChangeHealth(amount);
    }

    // переопределенный метод смерти
    protected override void Die()
    {
        health = 0;
        OnPlayerDeath?.Invoke();
    }
}

Поместите скрипт на объект игрока.


Теперь остается настроить визуал для отображение здоровья. Это можно сделать по аналогии со здоровьем врага, только поместить слайдер на холcт с другими элементами UI, вроде прицела и счетчика патронов.

Создайте слайдер и настройте его аналогично шкале здоровья врага.

Добавьте компонент HealthUI и укажите в нем игрока и слайдер.

Проверьте что шкала здоровья отображается как надо и принимает максимальное значение на старте.

А чтобы проверить, как она меняется, нужно научить врага наносить урон.


Урон по игроку

Изменим скрипт EnemyController, добавив туда логику для атаки игрока

C#using UnityEngine;
using UnityEngine.AI;

public class EnemyController : MonoBehaviour
{
    [Header("Attack Settings")]
    public int attackDamage = 10; // урон
    public float attackDistance = 2f; // дистанция атаки
    public float attackCooldown = 1f; // время между ударами
    private float nextAttackTime; // время следующей атаки
    
    private NavMeshAgent agent;
    private Transform player;
    private PlayerHealth playerHealth; // здоровье игрока

    void Start()
    {
        player = FindAnyObjectByType<PlayerController>().transform;
        // получаем здоровье игрока как конмпонент с объекта игрока
        playerHealth = player.GetComponent<PlayerHealth>(); 
        
        agent = GetComponent<NavMeshAgent>();
    }

    void Update()
    {
        // если игрок мертв, ничего не делаем
        if (playerHealth.IsDead()) return;
        
        // обрабатываем перемещение
        HandleMovement();
        // обрабатываем атаку
        HandleAttack();
    }

    void HandleMovement()
    {
        agent.SetDestination(player.position);
    }

    void HandleAttack()
    {
        // вычисляем дистанцию до игрока
        float distance = Vector3.Distance(transform.position, player.position);
        // если дистанция и время позволяют атаковать
        if (distance <= attackDistance && Time.time >= nextAttackTime)
        {
            // атакуем
            Attack(); 
            // устанавливаем время следующей атаки
            nextAttackTime = Time.time + attackCooldown; 
        }
    }

    void Attack()
    {
        // наносим урон здоровью игрока
        playerHealth.TakeDamage(attackDamage);
    }
}

Теперь враг будет не только следовать за игроком, но и наносить урон при достижении нужной дистанции.

Проверьте как меняется шкала здоровья игрока при нанесении урона и скорректируйте настройки врага при необходимости.


Подбираемый предмет

Давайте добавим аптечку, которую можно подобрать чтобы восстановить здоровье


Добавьте на сцену любой 3D объект или модель. Добавьте компонент коллайдера (например Sphere Collider), если его нет и пометьте его как триггер (Is Trigger)

Создайте скрипт HealthPickup, в котором будет логика восстановления здоровья игрока:

C#using UnityEngine;

public class HealthPickup : MonoBehaviour
{
    [Header("Settings")]
    public float healAmount = 25f;   // сколько здоровья будет восстановлено
    public float rotationSpeed = 50f;  // необязательная анимация вращения

    private void Update()
    {
        Rotate();
    }

    private void Rotate()
    {
        // поворачиваем объект на месте вокруг своей оси
        transform.Rotate(Vector3.up * (rotationSpeed * Time.deltaTime));
    }

    // при входе в триггер
    private void OnTriggerEnter(Collider other)
    {
        // проверяем, есть ли у объекта компонент здоровья
        if (other.TryGetComponent(out PlayerHealth health))
        {
            // восстанавливаем здоровье
            health.Heal(healAmount);

            // уничтожаем объект после подбора
            Destroy(gameObject);
        }
    }
}

Добавьте скрипт на подбираемый объект и запустите игру.

Проверьте, что при пересечении игрока и объекта, объект удаляется, а здоровье восстанавливается

Что ещё можно добавить?

Мы рассмотрели простую логику для врагов, предметов и их влияние на здоровье игрока.

Возможно для вашей игры требуются другие виды врагов или их поведение, попробуйте это сделать с имеющимися знаниями.


Так же подумайте над тем чтобы добавить:

  • Преследование игрока врагом только при достижении определенной дистанции
  • Патрулирование – перемещение врага между несколькими точками
  • Атаку для врага стрельбой в игрока
  • Разные виды агентов для различных видов врагов
  • Другие области для перемещения, например непроходимые
  • Возможность для врагов перепрыгивать препятствия или пропасти (в этом поможет Nav Mesh Link)
  • Разные виды подбираемых предметов, например с патронами или новым оружием
  • Выпадение предметов из врагов или ящиков при их уничтожении


Как всегда, все возникающие вопросы можете задать в чате или комментариях.

В следующем уроке мы разнообразим врагов и сделаем геймплей интереснее.


hub.comments