Space-Smash-Out/Assets/Scripts/UI/OffScreenIndicatorManager.cs

121 lines
2.9 KiB
C#

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<GameObject, GameObject> _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<GameObject, GameObject> 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<MeshRenderer>();
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);
}
}