310 lines
10 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<RadarIcon> activeIcons = new List<RadarIcon>();
// Храним время последнего обнаружения для каждого объекта
private Dictionary<Transform, float> lastDetectionTime = new Dictionary<Transform, float>();
// Задержка между обнаружениями одного и того же объекта (чтобы не спавнить пачками)
[SerializeField] private float detectionCooldown = 2f;
private DangersModel dangersModel;
private RectTransform radarRectTransform;
[SerializeField] private List<Sprite> 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<RectTransform>();
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<RadarIcon>();
if (icon == null)
{
icon = iconObject.AddComponent<RadarIcon>();
}
// Получаем позицию для фиксации
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<AbstractProjectile>() != null)
{
iconColor = Color.red;
}
else if (target.GetComponent<EnemyShip>() != 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<RectTransform>();
if (rectTransform == null)
{
rectTransform = gameObject.AddComponent<RectTransform>();
}
image = GetComponent<Image>();
if (image == null)
{
image = gameObject.AddComponent<Image>();
}
// Устанавливаем позицию и поворот
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;
}
}
}