Files
Net.Like.Xue.Tokyo/Packages-Local/Com.Project.B.Unity/UX/UXIndicator.cs
2025-06-24 23:49:13 +08:00

156 lines
5.3 KiB
C#

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using BITKit;
using BITKit.Tween;
using BITKit.UX;
using Cysharp.Threading.Tasks;
using Net.Project.B.Damage;
using Project.B.Entities;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.UIElements;
namespace Net.Project.B.UX
{
public class UXIndicator<TPanel> : UIToolkitSubPanel<TPanel> , IDisposable, IUXIndicator where TPanel : IUXPanel
{
private struct IndicatorData
{
public int Initiator;
public VisualElement VisualElement;
public Vector3 Position;
public CancellationTokenSource CancellationTokenSource;
}
private readonly IMainTicker _mainTicker;
private readonly IDamageService _damageService;
private readonly IPlayerFactory _playerFactory;
private readonly ConcurrentDictionary<int,IndicatorData> _indicators = new();
[UXBindPath("indicator-container")]
private VisualElement _indicatorContainer;
private VisualTreeAsset _template;
private readonly Transform _cameraTransform;
public UXIndicator(IServiceProvider serviceProvider, IDamageService damageService, IPlayerFactory playerFactory, IMainTicker mainTicker) : base(serviceProvider)
{
_damageService = damageService;
_playerFactory = playerFactory;
_mainTicker = mainTicker;
_damageService.OnDamaged += OnDamaged;
_mainTicker.Add(OnMainTick);
_cameraTransform = Camera.main!.transform;
}
private void OnMainTick(float obj)
{
foreach (var indicatorData in _indicators.Values)
{
UpdateIndicator(indicatorData);
}
}
private async void OnDamaged(IDamageReport obj)
{
if (_playerFactory.Entities.ContainsKey(obj.Target) is false || obj.Target == obj.Initiator) return;
if (_indicators.TryGetValue(obj.Initiator, out var indicatorData))
{
indicatorData.Position = obj.ContactPosition;
indicatorData.CancellationTokenSource.Cancel();
indicatorData.CancellationTokenSource = new CancellationTokenSource();
}
else
{
indicatorData = new IndicatorData()
{
Initiator = obj.Initiator,
VisualElement = _indicatorContainer.Create(_template),
Position = obj.ContactPosition,
CancellationTokenSource = new(),
};
}
_indicators[obj.Initiator] = indicatorData;
try
{
await BITween.CreateSequence()
.Append(UniTask.Delay(1000))
.Append(BITween.Lerp(indicatorData.VisualElement.SetOpacity, 1f, 0f, 1f, math.lerp,
indicatorData.CancellationTokenSource.Token))
.Play()
;
_indicators.TryRemove(obj.Initiator, out _);
indicatorData.VisualElement.RemoveFromHierarchy();
}
catch (OperationCanceledException)
{
}
}
private void UpdateIndicator(IndicatorData indicatorData){
var rhs = indicatorData.Position - _cameraTransform.position;
var forward =Vector3.ProjectOnPlane( _cameraTransform.forward,Vector3.up);
//Convert angle into screen space
rhs.y = indicatorData.Position.y;
rhs.Normalize();
//Get the angle between two positions.
var angle = Vector3.Angle(rhs, forward);
//Calculate the perpendicular of both vectors
//More information about this calculation: https://unity3d.com/es/learn/tutorials/modules/beginner/scripting/vector-maths-dot-cross-products?playlist=17117
var perpendicular = Vector3.Cross(forward, rhs);
//Calculate magnitude between two vectors
var dot = -Vector3.Dot(perpendicular, Vector3.up);
//get the horizontal angle in direction of target / sender.
angle = AngleCircumference(dot, angle);
//Apply the horizontal rotation to the indicator.
indicatorData.VisualElement.transform.rotation = Quaternion.Euler(new(0,0,-angle));
return;
float AngleCircumference(float dot, float angle)
{
const float circumference = 360f;
var ac = angle - 10;
if (dot < 0)
{
ac = circumference - angle;
}
return ac;
}
}
protected override async UniTask OnInitiatedAsync()
{
await base.OnInitiatedAsync();
_template = _indicatorContainer.Q<TemplateContainer>().templateSource;
_indicatorContainer.Clear();
}
public void Dispose()
{
foreach (var indicatorData in _indicators.Values)
{
indicatorData.CancellationTokenSource.Cancel();
}
_mainTicker.Remove(OnMainTick);
_damageService.OnDamaged -= OnDamaged;
}
}
}