using UnityEngine; using System.Collections.Generic; public class ProjectileWarningView : MonoBehaviour { [SerializeField] private ProjectileWarning warningPrefab; [SerializeField] private Transform uiCircleCenter; // Центр круга UI [SerializeField] private float warningRadius = 200f; // Радиус круга на UI [SerializeField] private float maxWarningDistance = 1000f; // Максимальная дистанция для отображения private GameObject player; private Dictionary activeWarnings = new Dictionary(); public void Initialize() { G.DangersModel.OnProjectileSpawned += OnProjectileSpawned; player = G.PlayerStateModel.PlayerComponent.gameObject; } private void OnProjectileSpawned(AbstractProjectile projectile) { if (projectile == null) return; if (projectile.Owner == player) return; // Создаем предупреждение для снаряда ProjectileWarning warning = Instantiate(warningPrefab, transform); activeWarnings.Add(projectile, warning); // Устанавливаем начальную дистанцию float distance = Vector3.Distance(projectile.transform.position, CamerasManager.VR_Camera.transform.position); warning.SetDistance(distance); } private void OnDestroy() { if (G.DangersModel != null) { G.DangersModel.OnProjectileSpawned -= OnProjectileSpawned; } } void LateUpdate() { Vector3 cameraPosition = CamerasManager.VR_Camera.transform.position; Vector3 cameraDirection = CamerasManager.VR_Camera.transform.forward; List projectilesToRemove = new List(); foreach (var warningPair in activeWarnings) { AbstractProjectile projectile = warningPair.Key; ProjectileWarning warning = warningPair.Value; // Проверяем, существует ли еще снаряд if (projectile == null) { projectilesToRemove.Add(projectile); Destroy(warning.gameObject); continue; } // Обновляем дистанцию float distance = Vector3.Distance(projectile.transform.position, cameraPosition); // Вычисляем направление от игрока к снаряду Vector3 directionToProjectile = (projectile.transform.position - G.PlayerStateModel.PlayerComponent.transform.position).normalized; // Проверяем, находится ли снаряд позади игрока (dot < 0) float dotProduct = Vector3.Dot(Vector3.forward, directionToProjectile); bool isBehindPlayer = dotProduct < 0; // Показываем предупреждение только если снаряд впереди и в пределах дистанции if (!isBehindPlayer && distance <= maxWarningDistance) { warning.gameObject.SetActive(true); warning.SetDistance(Mathf.RoundToInt(distance)); // Рассчитываем угол и позицию на UI float angle = CalculateAngle(projectile.transform.position, cameraPosition, cameraDirection); Vector2 uiPosition = GetUIPositionFromAngle(angle); warning.GetComponent().anchoredPosition = uiPosition; warning.SetAngle(angle); } else { // Снаряд позади или слишком далеко - скрываем предупреждение warning.gameObject.SetActive(false); } } // Удаляем уничтоженные снаряды foreach (var projectile in projectilesToRemove) { activeWarnings.Remove(projectile); } } float CalculateAngle(Vector3 projectilePosition, Vector3 cameraPosition, Vector3 cameraDirection) { Vector3 dirToProjectile = projectilePosition - cameraPosition; dirToProjectile.y = 0; // Игнорируем вертикальную составляющую cameraDirection.y = 0; return Vector3.SignedAngle(dirToProjectile, cameraDirection, Vector3.up); } Vector2 GetUIPositionFromAngle(float angle) { float radians = -angle * Mathf.Deg2Rad; // Вычисляем позицию на круге float x = Mathf.Sin(radians) * warningRadius; float y = Mathf.Cos(radians) * warningRadius; return new Vector2(x, y); } }