using System; using System.Collections.Generic; using System.Linq; using Managers; using UnityEngine; public class OffScreenIndicatorManager : MonoBehaviour { public GameObject[] Targets; public GameObject IndicatorPrefab; public Color DefaultColor = Color.yellow; private MeshRenderer _meshRenderer; private Camera _camera; private Dictionary _targetIndicators = new(); // Start is called before the first frame update void Start() { _camera = Camera.main; foreach (var target in Targets) AddTarget(target); } // Update is called once per frame void Update() { if (_camera == null) { if (Camera.main != null) _camera = Camera.main; return; } bool targetRemoved = false; foreach (KeyValuePair entry in _targetIndicators) { var target = entry.Key; var indicator = entry.Value; if (target != null) { UpdateTarget(target, indicator); } else { targetRemoved = true; } } if (!targetRemoved) return; GameObject[] targets = _targetIndicators.Keys.ToArray(); for (int i = 0; i < targets.Count(); ++i) { if (targets[i] == null) { Destroy(_targetIndicators[targets[i]]); _targetIndicators.Remove(targets[i]); } } } public void AddTarget(GameObject target) { AddTarget(target, DefaultColor); } public void AddTarget(GameObject target, Color color) { var indicator = Instantiate(IndicatorPrefab); _meshRenderer = indicator.GetComponentInChildren(); indicator.SetActive(false); indicator.transform.SetParent(_camera.transform); _targetIndicators.Add(target, indicator); MaterialPropertyBlock materialPropertyBlock = new(); materialPropertyBlock.SetColor("_BaseColor", color); _meshRenderer.SetPropertyBlock(materialPropertyBlock); } private void UpdateTarget(GameObject target, GameObject indicator) { if (MatchManager.G.matchState != MatchState.Match || target == null || !target.activeInHierarchy) { indicator.SetActive(false); return; } var screenPos = _camera.WorldToViewportPoint(target.transform.position); bool isOffScreen = screenPos.x <= 0 || screenPos.x >= 1 || screenPos.y <= 0 || screenPos.y >= 1; if (!isOffScreen) { indicator.SetActive(false); return; } indicator.SetActive(true); float margin_x = 0.05f; float margin_y = 0.05f; screenPos.x = Mathf.Clamp(screenPos.x, margin_x, 1 - margin_x); screenPos.y = Mathf.Clamp(screenPos.y, margin_y, 1 - margin_y); screenPos.z = Mathf.Clamp(screenPos.z, 0, 1); var worldPosition = _camera.ViewportToWorldPoint(screenPos); var cameraDirection = _camera.transform.position - worldPosition; indicator.transform.position = worldPosition; var virtualIndicatorPosition = worldPosition + _camera.transform.localPosition.z * cameraDirection; Vector3 direction = target.transform.position - virtualIndicatorPosition; indicator.transform.rotation = Quaternion.FromToRotation(new Vector3(0, 0, 1), direction); } }