156 lines
5.3 KiB
C#
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;
|
|
}
|
|
}
|
|
} |