- Добавлено свойство spawnerNameFilter (по умолчанию 'Enemy') в EntitySpawnerZone, чтобы зоны переопределения применялись только к спавнерам врагов и не заменяли префабы в CoinSpawner (спавнерах кристаллов) или других спавнерах предметов. - Реализована точная математическая проверка IsPointInside для BoxCollider2D, что делает ее независимой от Z-координаты и тиков физического движка Unity. - Исправлено поведение в EntitySpawner: теперь при поиске зоны проверяется zone.MatchesSpawner.
114 lines
4.5 KiB
C#
114 lines
4.5 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
|
||
namespace RR.Game2D.Behaviour
|
||
{
|
||
[RequireComponent(typeof(Collider2D))]
|
||
[DisallowMultipleComponent]
|
||
public sealed class EntitySpawnerZone : MonoBehaviour
|
||
{
|
||
[Header("Override Prefabs")]
|
||
[Tooltip("Список префабов, которые будут спавниться вместо оригинальных префабов спавнера внутри этой зоны.")]
|
||
[SerializeField] private EntitySpawner.SpawnPrefabEntry[] prefabs;
|
||
|
||
[Header("Targeting")]
|
||
[Tooltip("Фильтр по имени GameObject спавнера. Если не пусто, зона будет переопределять только те спавнеры, чье имя содержит этот текст (без учета регистра).")]
|
||
[SerializeField] private string spawnerNameFilter = "Enemy";
|
||
|
||
private Collider2D _collider;
|
||
|
||
// Статический список всех активных зон на сцене для быстрого поиска
|
||
public static readonly List<EntitySpawnerZone> ActiveZones = new();
|
||
|
||
public EntitySpawner.SpawnPrefabEntry[] Prefabs => prefabs;
|
||
|
||
private void Awake()
|
||
{
|
||
_collider = GetComponent<Collider2D>();
|
||
// Убеждаемся, что коллайдер является триггером, чтобы не вызывать физических коллизий с персонажами
|
||
if (_collider != null)
|
||
{
|
||
_collider.isTrigger = true;
|
||
}
|
||
}
|
||
|
||
private void OnEnable()
|
||
{
|
||
ActiveZones.Add(this);
|
||
}
|
||
|
||
private void OnDisable()
|
||
{
|
||
ActiveZones.Remove(this);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Проверяет, подходит ли данный спавнер под критерии фильтрации зоны.
|
||
/// </summary>
|
||
public bool MatchesSpawner(EntitySpawner spawner)
|
||
{
|
||
if (spawner == null)
|
||
return false;
|
||
|
||
if (string.IsNullOrEmpty(spawnerNameFilter))
|
||
return true;
|
||
|
||
return spawner.gameObject.name.IndexOf(spawnerNameFilter, StringComparison.OrdinalIgnoreCase) >= 0;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Проверяет, находится ли мировая точка внутри коллайдера зоны.
|
||
/// </summary>
|
||
public bool IsPointInside(Vector3 point)
|
||
{
|
||
if (_collider == null)
|
||
{
|
||
_collider = GetComponent<Collider2D>();
|
||
}
|
||
|
||
if (_collider == null)
|
||
return false;
|
||
|
||
// Если это BoxCollider2D, выполняем точную математическую проверку в локальных координатах.
|
||
// Это работает надежно в любой момент (даже до первого физического обновления) и не зависит от Z-координаты.
|
||
if (_collider is BoxCollider2D box)
|
||
{
|
||
Vector3 localPoint = transform.InverseTransformPoint(point);
|
||
return Mathf.Abs(localPoint.x - box.offset.x) <= box.size.x * 0.5f &&
|
||
Mathf.Abs(localPoint.y - box.offset.y) <= box.size.y * 0.5f;
|
||
}
|
||
|
||
return _collider.OverlapPoint(point);
|
||
}
|
||
|
||
private void OnDrawGizmos()
|
||
{
|
||
if (_collider == null)
|
||
{
|
||
_collider = GetComponent<Collider2D>();
|
||
}
|
||
|
||
if (_collider == null) return;
|
||
|
||
// Рисуем оранжевую рамку зоны замены в редакторе
|
||
Gizmos.color = new Color(1f, 0.5f, 0f, 0.2f);
|
||
|
||
if (_collider is BoxCollider2D box)
|
||
{
|
||
Vector3 position = transform.TransformPoint(box.offset);
|
||
Vector3 size = Vector3.Scale(box.size, transform.lossyScale);
|
||
|
||
Gizmos.DrawCube(position, size);
|
||
Gizmos.color = new Color(1f, 0.5f, 0f, 0.8f);
|
||
Gizmos.DrawWireCube(position, size);
|
||
}
|
||
else
|
||
{
|
||
// Для других типов коллайдеров рисуем сферу в центре объекта
|
||
Gizmos.DrawSphere(transform.position, 0.5f);
|
||
}
|
||
}
|
||
}
|
||
}
|