using System.Collections.Generic; using UnityEngine; using UnityEngine.Pool; using UnityEngine.UIElements; namespace DanieleMarotta.RiversongCodeShowcase { [Service(typeof(IWorldUIService))] [GameSystemGroup(typeof(UISystemGroup))] public class WorldUITrackingSystem : GameSystem, IUpdatable, IWorldUIService { [InjectService] private IScene _scene; private readonly Dictionary _trackedElements = new(); public WorldUITrackingSystem(IServiceLocator serviceLocator) : base(serviceLocator) { } public void Register(VisualElement element, Transform target) { _trackedElements[element] = new TrackedElement { Target = target }; } public void Unregister(VisualElement element) { _trackedElements.Remove(element); } public void SetWorldOffset(VisualElement element, Vector3 offset) { var trackedElement = _trackedElements[element]; trackedElement.Offset = offset; _trackedElements[element] = trackedElement; } public void Update() { if (_trackedElements.Count == 0) return; using var invalidScope = ListPool.Get(out var invalidElements); var camera = _scene.MainCamera; foreach (var (element, trackedElement) in _trackedElements) { var target = trackedElement.Target; if (element.panel == null || target == null) { invalidElements.Add(element); continue; } var screenPoint = camera.WorldToScreenPoint(target.position + trackedElement.Offset); var isVisible = screenPoint.x >= 0 && screenPoint.x <= Screen.width && screenPoint.y >= 0 && screenPoint.y <= Screen.height && screenPoint.z > 0; element.style.visibility = isVisible ? Visibility.Visible : Visibility.Hidden; if (isVisible) element.SetAbsoluteScreenPosition(screenPoint); } foreach (var invalidElement in invalidElements) _trackedElements.Remove(invalidElement); } private struct TrackedElement { public Transform Target; public Vector3 Offset; } } }