using System; using System.Collections; using System.Collections.Generic; using BITKit; using BITKit.Entities; using Cysharp.Threading.Tasks; using Dreamteck.Splines; using Net.Project.B.Interaction; using Unity.Mathematics; using UnityEngine; namespace Net.Project.B.WorldNode { public class UnitySubwayService:IDisposable { private readonly IWorldInteractionService _interactionService; private readonly IEntitiesService _entitiesService; private readonly IFixedTicker _ticker; private readonly Dictionary _prevPlatform = new(); private readonly Dictionary _splineComputers = new(); private readonly Dictionary _distances = new(); private readonly Dictionary _lengths = new(); private readonly Dictionary _vehicleLength=new(); private readonly HashSet _isBusy = new(); public UnitySubwayService(IEntitiesService entitiesService, IFixedTicker ticker, IWorldInteractionService interactionService) { _entitiesService = entitiesService; _ticker = ticker; _interactionService = interactionService; _ticker.Add(OnTick); _entitiesService.OnAdd += OnAdd; } private void OnAdd(IEntity obj) { if (obj.ServiceProvider.QueryComponents(out UnitySubwayNode subwayNode, out Transform transform)) { var colliders = transform.GetComponentsInChildren(); foreach (var x in colliders) { foreach (var y in colliders) { Physics.IgnoreCollision(x, y, true); } } var spline = _splineComputers[obj.Id] = subwayNode.Spline.GetComponent(); var sample = spline.Project(transform.position); var distance = Mathf.Lerp(0, _lengths[obj.Id]=spline.CalculateLength(),(float)sample.percent); subwayNode.Subway.isKinematic = true; subwayNode.Subway.interpolation = RigidbodyInterpolation.Interpolate; _distances[obj.Id] = distance; if(subwayNode.FrontWheel && subwayNode.BackWheel) { var localFrontPosition = subwayNode.FrontWheel.localPosition; var localBackPosition = subwayNode.BackWheel.localPosition; _vehicleLength[obj.Id] = Vector3.Distance(localFrontPosition, localBackPosition); } } } private void OnTick(float obj) { var gc = GC.GetTotalMemory(false); var stopNodes = _entitiesService.QueryComponents(); foreach (var (entity, transform, subwayNode) in _entitiesService .QueryComponents()) { if (_isBusy.Contains(entity.Id)) continue; var spline = _splineComputers[entity.Id]; foreach (var (platformEntity, platformNode, platformTransform) in stopNodes) { var sample = spline.Project(platformTransform.position); var subwayPosition = transform.position; if (Vector3.Distance(sample.position, subwayPosition) < 1) { if (_prevPlatform.TryGetValue(entity.Id, out var prevPlatform) is false || prevPlatform != platformEntity.Id) { subwayNode.Subway.isKinematic = true; _prevPlatform[entity.Id] = platformEntity.Id; Arrived(entity.Id, platformNode.StopTime); break; } } var distance = _distances[entity.Id]; var length = _lengths[entity.Id]; distance = (distance + subwayNode.Speed * obj) % length; _distances[entity.Id] = distance; var percentage = distance<=0 ? 0 : distance / length; sample= spline.Evaluate(percentage,SplineComputer.EvaluateMode.Calculate); if (subwayNode.Offset.sqrMagnitude > 0.1f) { sample.position += sample.rotation * subwayNode.Offset; } if (_vehicleLength.TryGetValue(entity.Id, out var vehicleLength)) { var sampleFront =spline.Evaluate(GetPercent(distance + vehicleLength / 2)) ; var sampleBack =spline.Evaluate(GetPercent(distance - vehicleLength / 2)) ; var up = Vector3.Cross(sample.forward, sample.right); var rotation = quaternion.LookRotation(sampleFront.position - sampleBack.position, up); subwayNode.Subway.MoveRotation(rotation); var newPosition = Vector3.Lerp(sampleBack.position, sampleFront.position, 0.5f); subwayNode.Subway.MovePosition(newPosition); //_distances[entity.Id] = (float)spline.Project(newPosition).percent * length; } else { subwayNode.Subway.MovePosition(sample.position); //sample.position); subwayNode.Subway.MoveRotation(sample.rotation); } continue; float GetPercent(float d) { d %= length; return d<=0 ? 0 : d / length; } } } var after = GC.GetTotalMemory(false); if (gc != after) { // Debug.Log($"GC消耗:{gc}=>{after}"); } } public void Dispose() { _ticker.Remove(OnTick); } private async void Arrived(int id, float stopTime) { if (_entitiesService.TryGetEntity(id, out var entity) is false) return; if (entity.ServiceProvider.QueryComponents(out Transform transform,out UnitySubwayNode subwayNode) is false) return; Door(); try { _isBusy.Add(entity.Id); await UniTask.Delay(TimeSpan.FromSeconds(stopTime),cancellationToken: entity.CancellationToken); Door(); await UniTask.Delay(TimeSpan.FromSeconds(4),cancellationToken: entity.CancellationToken); } catch (OperationCanceledException) { } _isBusy.Remove(entity.Id); return; void Door() { foreach (var x in transform.GetComponentsInChildren()) { if (x.ServiceProvider.QueryComponents(out UnityDoorNode doorNode, out IWorldInteractable interactable)) { _interactionService.Interaction(this, interactable, WorldInteractionProcess.System); } } } } } }