Появление врага при входе игрока в триггер в Unity

Узнайте, как сделать спавн врага в Unity при входе в комнату! Пошаговый гайд для Android: минимум ресурсов, максимум эффекта.

В большинстве 2‑D‑игр встречаются ситуации, когда игроку нужно, чтобы враг появлялся в момент, когда он входит в видимую область, например, за порогом в комнате. В этой статье мы разберём пошагово, как реализовать такой эффект в Unity, используя минимальные ресурсы – идеальный вариант для мобильных проектов на Android.

Планируем: что понадобится

  • Unity 2021+ (или более ранние версии, но будем использовать API 2021)
  • Sprite‑файл врага (или готовый аниматор)
  • Порог как GameObject с BoxCollider2D в режиме Trigger
  • Скрипт EnemySpawner (появление врага)
  • Пул объектов для экономии памяти (опционально)

Шаг 1. Создаём порог

  1. В иерархии проекта создаём пустой объект Threshold.
  2. Добавляем компонент BoxCollider2D и в инспекторе ставим флажок Is Trigger.
  3. Подгоняем размер коллайдера так, чтобы он покрывал точку, где игрок «переходит» порог.
  4. При желании можно добавить визуальный спрайт – это поможет отладить позицию.

Шаг 2. Подготовка врага

Враги могут быть реализованы как префаб с компонентами:

  • Rigidbody2D (если нужен физический движок)
  • Collider2D (обычный коллайдер)
  • Скрипт EnemyAI – отвечает за движение и поведение.

Если вы хотите экономить память, создайте пул врагов (Object Pooling). Это позволяет переиспользовать уже созданные объекты вместо создания новых при каждом появлении.

Пример простого пула

using System.Collections.Generic;
using UnityEngine;

public class EnemyPool : MonoBehaviour
{ public static EnemyPool Instance;

 [SerializeField] private GameObject enemyPrefab;
 [SerializeField] private int poolSize = 10;
 private Queue pool = new Queue;

 private void Awake
 { Instance = this;
 for (int i = 0; i < poolSize; i++)
 {
 var enemy = Instantiate(enemyPrefab);
 enemy.SetActive(false);
 pool.Enqueue(enemy);
 }
 }

 public GameObject GetEnemy
 {
 if (pool.Count == 0)
 {
 var enemy = Instantiate(enemyPrefab);
 enemy.SetActive(false);
 pool.Enqueue(enemy);
 }

 var e = pool.Dequeue;
 e.SetActive(true);
 return e;
 }
 public void ReturnEnemy(GameObject enemy)
 {
 enemy.SetActive(false);
 pool.Enqueue(enemy);
 }
}

Шаг 3. Скрипт спавна врага

Создаём скрипт EnemySpawner и привязываем его к объекту Threshold. Скрипт реагирует на событие OnTriggerEnter2D и запрашивает врага из пула.

using UnityEngine;

public class EnemySpawner : MonoBehaviour
{
 [Header("Параметры спавна")]
 [Tooltip("Скорость появления врага после входа игрока")]
 [SerializeField] private float spawnDelay = 0.5f;

 [Tooltip("Класс игрока, который может спавнить врага")]
 [SerializeField] private string playerTag = "Player";

 private void OnTriggerEnter2D(Collider2D collision)
 {
 if (!collision.CompareTag(playerTag)) return;

 // Отложенный вызов спавна
 Invoke(nameof(SpawnEnemy), spawnDelay);
 }

 private void SpawnEnemy
 {
 // Получаем врага из пула
 GameObject enemy = EnemyPool.Instance.GetEnemy;
 // Устанавливаем позицию врага рядом с порогом
 enemy.transform.position = transform.position + new Vector3(1.5f, 0, 0); // 1.5f = расстояние от порога
 // Если у врага есть скрипт AI, можно инициализировать его
 var ai = enemy.GetComponent<EnemyAI>;
 if (ai != null)
 {
 ai.Initialize(this.transform); // передаём цель (порог)
 }
 }
}

Пояснения к коду

  • Поток Invoke добавляет небольшую задержку, чтобы враг не появился мгновенно (это делает игру более реалистичной).
  • Проверка CompareTag гарантирует, что спавн произойдёт только от игрока.
  • Вызов EnemyPool.Instance.GetEnemy берёт уже существующий объект, если он есть, иначе создаёт новый.

Шаг 4. Настраиваем AI врага

Простой AI может просто следовать за игроком, пока он находится в пределах видимости. Ниже пример скрипта EnemyAI:

using UnityEngine;
public class EnemyAI : MonoBehaviour
{
 [Header("Настройки AI")]
 [SerializeField] private float moveSpeed = 2f;
 [SerializeField] private float visionRange = 5f;

 private Transform target; // может быть игроком или порогом


 public void Initialize(Transform target)
 {
 this.target = target;
 }

 private void Update
 {
 if (target == null) return;

 Vector3 direction = (target.position ⏤ transform.position).normalized;
 float distance = Vector3.Distance(target.position, transform.position);

 // Проходим к цели, если она в пределах видимости
 if (distance < visionRange)
 {
 transform.position += direction * moveSpeed * Time.deltaTime;
 }
 }

 private void OnBecameInvisible
 {
 // Когда враг уходит из камеры, возвращаем его в пул
 EnemyPool.Instance.ReturnEnemy(this.gameObject);
 }
}

Что делать, если игрок скрывается за стеной?

Если в игре есть стены, можно использовать Physics2D.Raycast для проверки прямой видимости. Если луч прерывается стеной, враг будет искать обходной путь (можно реализовать простой A* алгоритм на плоскости, но для Android лучше воспользоваться готовой библиотекой, например, PathfindingPlugin).

Оптимизация для Android

  • Используйте Object Pooling – минимум новых объектов.
  • Ограничьте количество активных врагов: если игрок заходит в несколько порогов подряд, можно держать переменную maxActiveEnemies.
  • Переходим на FixedUpdate для физики, а не Update, чтобы избежать лишних вызовов.
  • Убедитесь, что спрайты используют Tilemap или Sprite Atlas для уменьшения draw calls.

Полный пример сцены

  1. Создайте объект GameController и добавьте к нему скрипт EnemyPool.
  2. В сцене разместите несколько объектов Threshold (по порогу в комнатах).
  3. Привяжите к каждому порогу скрипт EnemySpawner.
  4. Настройте тег Player у вашего персонажа.
  5. Запустите игру – при входе в пороги враги появятся!

Мы показали, как реализовать появление врага в момент входа игрока в заданную область (порог) с минимальными затратами ресурсов. Главное – использовать триггерный коллайдер, пул объектов и простую логику AI. Если понадобится более сложный поиск пути, можно подключить A* библиотеку, но для большинства мобильных проектов базовый подход уже будет достаточным.

Удачного кодирования и приятной игры!