using System.Collections.Generic; using Unity.VisualScripting; using UnityEngine; using UnityEngine.UI; public class RadarView : MonoBehaviour { [SerializeField] private RadarOrigin origin; [SerializeField] private GameObject prefab; [SerializeField] private float radarRadius = 100f; [SerializeField] private bool showDebugGizmos = true; [Header("Scan Settings")] [SerializeField] private float scanWidth = 30f; // Ширина луча сканирования (градусы) [Header("Fade Settings")] [SerializeField] private float iconFadeOutSpeed = 1f; // Скорость затухания иконок // Храним активные иконки private List activeIcons = new List(); // Храним время последнего обнаружения для каждого объекта private Dictionary lastDetectionTime = new Dictionary(); // Задержка между обнаружениями одного и того же объекта (чтобы не спавнить пачками) [SerializeField] private float detectionCooldown = 2f; private DangersModel dangersModel; private RectTransform radarRectTransform; [SerializeField] private List radarSlides; [SerializeField] private float anglePerSecond = 180; private float currentScanAngle = 0; [SerializeField] private Image image; public void Initialize(RadarOrigin radarOrigin, DangersModel model) { origin = radarOrigin; dangersModel = model; radarRectTransform = GetComponent(); if (radarRectTransform == null) { Debug.LogWarning("RadarView должен быть на объекте с RectTransform!"); } if (dangersModel != null) { dangersModel.OnUpdate += OnDangersModelUpdated; } } private void OnDangersModelUpdated(DangersModel model) { // Не создаем иконки заранее, только обновляем списки } private void Update() { if (origin == null || dangersModel == null) return; // Обновляем угол сканирования currentScanAngle = (currentScanAngle + anglePerSecond * Time.deltaTime) % 360; // Обновляем спрайт сканирования int slideIndex = (int)(currentScanAngle / (360 / radarSlides.Count)); image.sprite = radarSlides[slideIndex]; // Проверяем все живые объекты на обнаружение CheckForDetections(); // Обновляем существующие иконки (уменьшаем таймеры жизни) UpdateExistingIcons(); } private void CheckForDetections() { // Проверяем корабли foreach (var ship in dangersModel.AliveShips) { if (ship != null && ship.HealthComponent.Alive) { CheckAndCreateIcon(ship.transform); } } // Проверяем снаряды foreach (var projectile in dangersModel.Projectiles) { if (projectile != null) { CheckAndCreateIcon(projectile.transform); } } } private void CheckAndCreateIcon(Transform target) { if (target == null) return; // Проверяем, находится ли цель в зоне сканирования if (IsInScanZone(target)) { // Проверяем, не создавали ли мы недавно иконку для этого объекта if (!lastDetectionTime.ContainsKey(target) || Time.time - lastDetectionTime[target] >= detectionCooldown) { // Создаем новую иконку CreateIcon(target); lastDetectionTime[target] = Time.time; } } } private bool IsInScanZone(Transform target) { if (origin == null || target == null) return false; // Получаем угол до цели Vector3 directionToTarget = target.position - origin.transform.position; float angleToTarget = Vector3.SignedAngle(origin.transform.forward, directionToTarget, Vector3.up); // Проверяем дистанцию float distance = directionToTarget.magnitude; if (distance > origin.Radius) return false; // Проверяем угол float halfWidth = scanWidth / 2; float normalizedScanAngle = NormalizeAngle(currentScanAngle); float normalizedTargetAngle = NormalizeAngle(angleToTarget); float angleDifference = Mathf.DeltaAngle(normalizedScanAngle, normalizedTargetAngle); return Mathf.Abs(angleDifference) <= halfWidth; } private void CreateIcon(Transform target) { if (prefab == null || target == null) return; GameObject iconObject = Instantiate(prefab, transform); RadarIcon icon = iconObject.GetComponent(); if (icon == null) { icon = iconObject.AddComponent(); } // Получаем позицию для фиксации Vector3 radarData = origin.TransformOnRadar(target); Vector2 fixedPosition = new Vector2(radarData.x, radarData.y) * radarRadius; float fixedRotation = -radarData.z; // Определяем цвет в зависимости от типа цели Color iconColor = Color.white; if (target.GetComponent() != null) { iconColor = Color.red; } else if (target.GetComponent() != null) { iconColor = Color.yellow; } icon.Initialize(fixedPosition, fixedRotation, iconFadeOutSpeed, iconColor); activeIcons.Add(icon); } private void UpdateExistingIcons() { // Обновляем все иконки и удаляем уничтоженные for (int i = activeIcons.Count - 1; i >= 0; i--) { if (activeIcons[i] == null || activeIcons[i].IsDestroyed) { activeIcons.RemoveAt(i); } else { activeIcons[i].UpdateLife(); } } } private float NormalizeAngle(float angle) { angle = angle % 360; if (angle > 180) angle -= 360; if (angle < -180) angle += 360; return angle; } private void OnDestroy() { if (dangersModel != null) { dangersModel.OnUpdate -= OnDangersModelUpdated; } // Уничтожаем все иконки foreach (var icon in activeIcons) { if (icon != null) Destroy(icon.gameObject); } activeIcons.Clear(); lastDetectionTime.Clear(); } private void OnDrawGizmos() { if (!showDebugGizmos || origin == null) return; Gizmos.color = Color.white; Gizmos.DrawWireSphere(origin.transform.position, origin.Radius); // Визуализируем зону сканирования if (Application.isPlaying) { Vector3 center = origin.transform.position; Vector3 forward = origin.transform.forward; float halfWidth = scanWidth / 2; float minAngle = currentScanAngle - halfWidth; float maxAngle = currentScanAngle + halfWidth; Vector3 leftBoundary = Quaternion.Euler(0, minAngle, 0) * forward; Vector3 rightBoundary = Quaternion.Euler(0, maxAngle, 0) * forward; Gizmos.color = new Color(1, 1, 0, 0.5f); Gizmos.DrawRay(center, leftBoundary * origin.Radius); Gizmos.DrawRay(center, rightBoundary * origin.Radius); } } } // Компонент для отдельной иконки на радаре public class RadarIcon : MonoBehaviour { private RectTransform rectTransform; private Image image; private float fadeOutSpeed = 1f; private float currentAlpha = 1f; private bool isDestroyed = false; public bool IsDestroyed => isDestroyed; public void Initialize(Vector2 position, float rotation, float fadeSpeed, Color color) { rectTransform = GetComponent(); if (rectTransform == null) { rectTransform = gameObject.AddComponent(); } image = GetComponent(); if (image == null) { image = gameObject.AddComponent(); } // Устанавливаем позицию и поворот rectTransform.anchoredPosition = position; rectTransform.localRotation = Quaternion.Euler(0, 0, rotation); // Настраиваем цвет fadeOutSpeed = fadeSpeed; color.a = 1f; image.color = color; // Настраиваем размер иконки //rectTransform.sizeDelta = new Vector2(20, 20); currentAlpha = 1f; isDestroyed = false; } public void UpdateLife() { if (isDestroyed) return; // Уменьшаем прозрачность currentAlpha -= fadeOutSpeed * Time.deltaTime; if (currentAlpha <= 0) { // Иконка полностью исчезла - уничтожаем isDestroyed = true; Destroy(gameObject); return; } // Обновляем цвет Color color = image.color; color.a = currentAlpha; image.color = color; //// Плавно уменьшаем размер //float scale = Mathf.Lerp(1f, 0f, 1f - currentAlpha); //rectTransform.localScale = Vector3.one * scale; } public void SetColor(Color color) { if (image != null) { color.a = currentAlpha; image.color = color; } } }