using System.Collections.Generic; using Unity.Entities; using Unity.Mathematics; using Unity.Physics; using Unity.Transforms; using UnityEngine; using UnityEngine.UIElements; namespace Unity.MegaCity.Gameplay { /// /// Manages the player name tags. /// public class PlayerInfoController : MonoBehaviour { [HideInInspector] public string Name; [SerializeField] private PlayerInfoItemSettings m_Settings; [SerializeField] private VisualTreeAsset m_PlayerInfoItem; private VisualElement m_PlayerInfoContainer; private readonly Dictionary NameTags = new(); public static PlayerInfoController Instance; private Transform m_CameraTransform; private Camera m_Camera; private void Awake() { if (Instance == null) { Instance = this; m_Camera = Camera.main; if (m_Camera != null) m_CameraTransform = m_Camera.transform; } else { Destroy(this); } } private void OnEnable() { var root = GetComponent().rootVisualElement; m_PlayerInfoContainer = root.Q("player-name-info-container"); } private void OnDestroy() { Name = string.Empty; } public void CreateNameTag(string playerName, Entity player, float health) { if (NameTags.TryGetValue(player, out var nameTag)) { var label = nameTag.Label; label.text = playerName; } else { var item = m_PlayerInfoItem.Instantiate(); var playerInfo = new PlayerInfoRef(player, health, playerName, item, m_Settings); m_PlayerInfoContainer.Add(item); NameTags.Add(player, playerInfo); } } private void DestroyNameTag(Entity player) { m_PlayerInfoContainer.Remove(NameTags[player].Panel.parent); NameTags.Remove(player); } public void UpdateBadge(Entity player, bool shouldShow) { if (NameTags.TryGetValue(player, out var nameTag)) { nameTag.UpdateBadge(shouldShow); } } public void UpdateNamePosition(Entity player, string playerName, float health, LocalToWorld localToWorld, CollisionWorld collisionWorld) { if (NameTags.ContainsKey(player)) { NameTags[player].SetLife(health); var cameraPosition = m_CameraTransform.position; var distance = math.distancesq(localToWorld.Position, cameraPosition); var placerPosition = localToWorld.Position + GetOffsetByDistance(distance); var screenPosition = m_Camera.WorldToScreenPoint(placerPosition); var rootRay = cameraPosition + m_CameraTransform.forward * m_Settings.RayOffsetFromCamera; if (screenPosition.z < 0 || !NameTags[player].IsVisible(collisionWorld, rootRay, localToWorld.Position)) { NameTags[player].Hide(); } else { // Convert the screen position to panel position screenPosition = RuntimePanelUtils.ScreenToPanel(m_PlayerInfoContainer.panel, new Vector2(screenPosition.x, Screen.height - screenPosition.y)); NameTags[player].UpdateLabel(playerName); NameTags[player].UpdatePosition(screenPosition); NameTags[player].UpdateScale(distance); NameTags[player].Show(); } } } private float3 GetOffsetByDistance(float distance) { var minOffset = m_Settings.MinOffset; var maxOffset = m_Settings.Offset; var minDistance = m_Settings.MinDistanceSq; var maxDistance = m_Settings.MaxDistanceSq; float3 offset; if (distance <= minDistance) { offset = Vector3.up * maxOffset; } else if (distance >= maxDistance) { offset = minOffset; } else { var t = (distance - minDistance) / (maxDistance - minDistance); offset = Vector3.up * math.lerp(maxOffset, minOffset, t); } return offset; } public void RefreshNameTags(EntityManager manager) { var list = new List(); foreach (var nameTag in NameTags.Keys) { if (!manager.Exists(nameTag)) { list.Add(nameTag); } } foreach (var entity in list) { DestroyNameTag(entity); } } } }