В играх с 2‑D физикой часто возникает задача: при столкновении игрока с врагом враг отталкивается, но вместо того, чтобы улететь в произвольном направлении, нужно «перенаправить» отвод так, чтобы он летел к определённому объекту (например, к союзному персонажу, к точке‑отклюку или к специальному «отводному» триггеру). В статье рассмотрим, как реализовать такой механизм в Unity2D, используя Rigidbody2D, Collider2D и несколько простых скриптов.
Содержание
- Теоретические основы отталкивания в Unity2D
- Подготовка сцены и компонентов
- Скрипт перенаправления отдава
- Почему важен параметр
Drag - Перенаправление к нескольким целям
- Отладка и типичные ошибки
- Оптимизация и производительность
Теоретические основы отталкивания в Unity2D
В Unity отталкивание (knock‑back) реализуется при помощи:
- Физического тела
Rigidbody2D– отвечает за движение, массу и сопротивление. - Коллайдеров (
Collider2D) – определяют момент столкновения. - Метода
Rigidbody2D.AddForce– добавляет к телу мгновенный импульс.
Если добавить силу в произвольном направлении, объект начнёт лететь до тех пор, пока не будет приторможен силами сопротивления (Drag) и гравитацией.
Подготовка сцены и компонентов
Для примера создадим три объекта:
- Player – объект, который получает удар.
- Enemy – враг, который будет отталкиваться.
- 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для предсказания столкновений, если требуется более сложная логика (например, отскок от стен).
Перенаправление отдава врага с себя на другой объект – достаточно распространённый, но в то же время гибкий приём. Ключевые шаги:
- Настроить физические компоненты (
Rigidbody2D,Collider2D). - Установить ненулевой
Drag, чтобы отскок «затухал». - В скрипте рассчитать вектор к желаемой цели и применить импульс.
- Обработать несколько целей и добавить отладочный вывод.
- Оптимизировать работу при большом количестве врагов.
Следуя этим рекомендациям, вы сможете создавать более интересные боевые механики, где отвод врагов становится управляемым элементом геймплея, а не случайным полётом по экрану.
Дата публикации: