Перенаправление отдава в Unity2D

Устали от хаотичных отскоков? Узнайте, как элегантно перенаправить отвод врага к нужной цели в Unity2D через Rigidbody2D и коллайдеры!

В играх с 2‑D физикой часто возникает задача: при столкновении игрока с врагом враг отталкивается, но вместо того, чтобы улететь в произвольном направлении, нужно «перенаправить» отвод так, чтобы он летел к определённому объекту (например, к союзному персонажу, к точке‑отклюку или к специальному «отводному» триггеру). В статье рассмотрим, как реализовать такой механизм в Unity2D, используя Rigidbody2D, Collider2D и несколько простых скриптов.

Содержание

  1. Теоретические основы отталкивания в Unity2D
  2. Подготовка сцены и компонентов
  3. Скрипт перенаправления отдава
  4. Почему важен параметр Drag
  5. Перенаправление к нескольким целям
  6. Отладка и типичные ошибки
  7. Оптимизация и производительность

Теоретические основы отталкивания в Unity2D

В Unity отталкивание (knock‑back) реализуется при помощи:

  • Физического тела Rigidbody2D – отвечает за движение, массу и сопротивление.
  • Коллайдеров (Collider2D) – определяют момент столкновения.
  • Метода Rigidbody2D.AddForce – добавляет к телу мгновенный импульс.

Если добавить силу в произвольном направлении, объект начнёт лететь до тех пор, пока не будет приторможен силами сопротивления (Drag) и гравитацией.

Подготовка сцены и компонентов

Для примера создадим три объекта:

  1. Player – объект, который получает удар.
  2. Enemy – враг, который будет отталкиваться.
  3. RedirectTarget – любой объект (может быть «мяч», «твердая стена», «союзный персонаж»), к которому нужно направить отвод.

Для каждого из них:

  • Добавьте Rigidbody2D. У Enemy оставьте Body Type = Dynamic, у Player – Kinematic (если игрок управляется без физики).
  • Установите Collider2D (например, BoxCollider2D или CircleCollider2D).
  • У Enemy в инспекторе задайте Drag = 1 (об этом подробнее ниже).

Скрипт перенаправления отдава

Создаём скрипт EnemyKnockbackRedirect.cs и привязываем его к объекту Enemy:


using UnityEngine;
/// <summary>
/// Отталкивает врага от точки удара и перенаправляет его полёт к целевому объекту.
/// </summary>
[RequireComponent(typeof(Rigidbody2D))]
public class EnemyKnockbackRedirect : MonoBehaviour
{
 private Rigidbody2D _rb;

 // Ссылка на объект, к которому нужно «перенаправить» отвод
 [Tooltip("Объект, к которому будет направлен отвод врага")]
 public Transform redirectTarget;

 // Сила отскока (можно менять в инспекторе)
 [Tooltip("Модуль силы отталкивания")]
 public float knockbackForce = 10f;

 // Параметр, определяющий, насколько быстро враг «засыпает» после отскока
 [Tooltip("Сопротивление движению (Drag)")]
 public float dragAfterKnockback = 1f;

 private void Awake
 {
 _rb = GetComponent<Rigidbody2D>;
 }

 /// <summary>
 /// Вызывается, когда вражеский коллайдер соприкасается с другим коллайдером.
 /// Мы проверяем, что столкновение произошло с игроком, а затем
 /// рассчитываем вектор к redirectTarget.
 /// </summary>
 private void OnCollisionEnter2D(Collision2D collision)
 {
 // Проверяем, что удар пришёл от игрока (можно также использовать тег)
 if (collision.gameObject.CompareTag("Player"))
 {
 ApplyRedirectedKnockback(collision.GetContact(0).point);
 }
 }

 /// <summary>
 /// Применяет отталкивание, направленное к redirectTarget.
 /// </summary>
 /// <param name="hitPoint">Точка удара (может быть использована для расчёта направления)</param>
 private void ApplyRedirectedKnockback(Vector2 hitPoint)
 {
 if (redirectTarget == null)
 {
 Debug.LogWarning("Redirect target not set! Performing normal knockback.");
 // Обычное отталкивание от игрока
 Vector2 dir = (transform.position ౼ hitPoint).normalized;
 _rb.AddForce(dir * knockbackForce, ForceMode2D.Impulse);
 return;
 }

 // 1. Считаем вектор от точки удара к целевому объекту
 Vector2 toTarget = (redirectTarget.position ‒ (Vector3)hitPoint).normalized;

 // 2. Добавляем импульс в этом направлении
 _rb;AddForce(toTarget * knockbackForce, ForceMode2D.Impulse);

 // 3. Устанавливаем Drag, чтобы враг не «улетал» бесконечно
 _rb.drag = dragAfterKnockback;
 }

 // Если нужно сбросить Drag после определённого времени, можно добавить корутину:
 private void ResetDragAfterDelay(float delay)
 {
 StartCoroutine(ResetDragCoroutine(delay));
 }


 private System.Collections.IEnumerator ResetDragCoroutine(float delay)
 {
 yield return new WaitForSeconds(delay);
 _rb.drag = 0f; // Возврат к обычному сопротивлению
 }
}

Ключевые моменты скрипта:

  • Мы получаем позицию redirectTarget и считаем вектор от точки удара к ней.
  • С помощью Rigidbody2D.AddForce с режимом Impulse создаём мгновенный импульс.
  • Устанавливаем drag в значение dragAfterKnockback (по умолчанию 1), чтобы враг не летел бесконечно.

Почему важен параметр Drag

В оригинальном ответе на Stack Overflow (см. c ‒ Unity2D Сделать отталкивание врагов при ударе) отмечалось, что без ненулевого Drag враг будет «бесконечно улетать» после отскока. Причина проста:

  • Drag – это коэффициент сопротивления, который замедляет движение тела каждый кадр.
  • Если Drag = 0, то после применения импульса сила будет действовать без потери энергии, и объект будет двигаться с постоянной скоростью.
  • Небольшое значение (например, 0.5‑1) достаточно, чтобы «потушить» движение через 0.5‑1 секунду, не делая отскок слишком «мягким».

Перенаправление к нескольким целям

Иногда требуется, чтобы в зависимости от контекста отвод направлялся к разным целям (например, к ближайшему союзнику или к «отводному» объекту, который меняет позицию). Для этого достаточно расширить скрипт:


public Transform[] possibleTargets; // массив потенциальных целей
private Transform ChooseTarget
{
 if (possibleTargets == null || possibleTargets.Length == 0) return null;

 // Выбираем ближайший к текущей позиции врага
 Transform best = null;
 float bestDist = Mathf.Infinity;
 foreach (var t in possibleTargets)
 {
 float d = Vector2.Distance(transform.position, t.position);
 if (d < bestDist)
 {
 bestDist = d;
 best = t;
 }
 }
 return best;
}

// В ApplyRedirectedKnockback заменяем redirectTarget на результат ChooseTarget

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

Отладка и типичные ошибки

При реализации часто встречаются следующие проблемы:

  • Враг проходит сквозь другие коллайдеры – проверьте, что у всех объектов Is Trigger выключен, иначе физика не будет отрабатывать столкновения.
  • Отскок слишком сильный или слишком слабый – отрегулируйте knockbackForce и dragAfterKnockback. Хороший диапазон силы – 5‑15, в зависимости от массы врага.
  • Цель не задана – скрипт выдаст предупреждение в консоли и выполнит обычный отскок. Убедитесь, что ссылка на redirectTarget установлена в инспекторе.
  • Враг «залипает» в точке назначения – если цель статична, добавьте небольшую горизонтальную/вертикальную составляющую к вектору toTarget (например, toTarget += Vector2.right * 0.1f;).

Оптимизация и производительность

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

  • Избегайте вызова FindObjectOfType в Update. Ссылки на цели храните в переменных.
  • Если отвод происходит редко, можно отключать Rigidbody2D у врагов в состоянии покоя (rb.simulated = false) и включать только в момент столкновения.
  • Используйте LayerMask и Physics2D.Raycast для предсказания столкновений, если требуется более сложная логика (например, отскок от стен).

Перенаправление отдава врага с себя на другой объект – достаточно распространённый, но в то же время гибкий приём. Ключевые шаги:

  1. Настроить физические компоненты (Rigidbody2D, Collider2D).
  2. Установить ненулевой Drag, чтобы отскок «затухал».
  3. В скрипте рассчитать вектор к желаемой цели и применить импульс.
  4. Обработать несколько целей и добавить отладочный вывод.
  5. Оптимизировать работу при большом количестве врагов.

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

Дата публикации: